diff --git a/.github/workflows/nbnet.yml b/.github/workflows/nbnet.yml index d85cee4..a42fb55 100644 --- a/.github/workflows/nbnet.yml +++ b/.github/workflows/nbnet.yml @@ -52,9 +52,6 @@ jobs: cd tests cmake -G "NMake Makefiles" . nmake - # FIXME: string_tests hangs forever - # - name: Run tests - # run: ctest soak-test-linux-c: runs-on: ubuntu-latest @@ -67,7 +64,7 @@ jobs: cd soak mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Debug .. + cmake -DCMAKE_BUILD_TYPE=Debug -DUDP=ON .. make - name: Run soak test run: timeout 240 ./bin/github-actions/run_soak.sh @@ -83,7 +80,7 @@ jobs: cd soak mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Debug -DCPP_COMPILE=ON .. + cmake -DCMAKE_BUILD_TYPE=Debug -DUDP=ON -DCPP_COMPILE=ON .. make - name: Run soak test run: timeout 240 ./bin/github-actions/run_soak.sh @@ -101,7 +98,7 @@ jobs: npm update npm install -g node-pre-gyp npm install -g node-gyp - cd net_drivers/webrtc + cd webrtc npm install --build-from-resource - name: Compile soak test run: ./bin/github-actions/build_soak_web.sh @@ -123,7 +120,7 @@ jobs: cd soak mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Debug .. + cmake -DCMAKE_BUILD_TYPE=Debug -DUDP=ON .. make - name: Run soak test run: gtimeout 240 ./bin/github-actions/run_soak.sh @@ -139,7 +136,7 @@ jobs: cd soak mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=Debug -DCPP_COMPILE=ON .. + cmake -DCMAKE_BUILD_TYPE=Debug -DUDP=ON -DCPP_COMPILE=ON .. make - name: Run soak test run: gtimeout 240 ./bin/github-actions/run_soak.sh @@ -157,7 +154,7 @@ jobs: npm update npm install -g node-pre-gyp npm install -g node-gyp - cd net_drivers/webrtc + cd webrtc npm install --build-from-resource - name: Compile soak test run: ./bin/github-actions/build_soak_web.sh @@ -178,7 +175,7 @@ jobs: cd soak mkdir build cd build - cmake -G "NMake Makefiles" .. + cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug -DUDP=ON .. nmake - name: Run soak test run: ./bin/github-actions/run_soak.sh @@ -198,14 +195,16 @@ jobs: npm update npm install -g node-pre-gyp npm install -g node-gyp - cd net_drivers/webrtc + cd webrtc npm install --build-from-resource - name: Compile soak test (native) run: | cd soak mkdir build cd build - cmake -DLIBDATACHANNEL_LIBRARY_PATH=${{ github.workspace }}/libdatachannel/build/libdatachannel.so -DLIBDATACHANNEL_INCLUDE_PATH=${{ github.workspace }}/libdatachannel/include -DWEBRTC_NATIVE=ON .. + cmake -DLIBDATACHANNEL_LIBRARY_PATH=${{ github.workspace }}/libdatachannel/build/libdatachannel.so \ + -DLIBDATACHANNEL_INCLUDE_PATH=${{ github.workspace }}/libdatachannel/include \ + -DWEBRTC_NATIVE=ON -DUDP=ON -DCMAKE_BUILD_TYPE=Debug .. make - name: Compile soak test (web) run: ./bin/github-actions/build_soak_web.sh @@ -216,7 +215,7 @@ jobs: cd .. WEBRTC_NATIVE=1 timeout 240 ./bin/github-actions/run_soak.sh - compile-examples-linux-c: + compile-examples-udp-linux-c: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -225,10 +224,10 @@ jobs: - name: Compile examples run: | cd examples - cmake . + cmake -DEXAMPLES_UDP=ON . make - compile-examples-linux-cpp: + compile-examples-udp-linux-cpp: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -237,10 +236,10 @@ jobs: - name: Compile examples run: | cd examples - cmake -DCPP_COMPILE=ON . + cmake -DEXAMPLES_UDP=ON -DCPP_COMPILE=ON . make - compile-examples-osx-c: + compile-examples-udp-osx-c: runs-on: macos-latest steps: - uses: actions/checkout@v3 @@ -249,10 +248,10 @@ jobs: - name: Compile examples run: | cd examples - cmake . + cmake -DEXAMPLES_UDP=ON . make - compile-examples-osx-cpp: + compile-examples-udp-osx-cpp: runs-on: macos-latest steps: - uses: actions/checkout@v3 @@ -261,10 +260,10 @@ jobs: - name: Compile examples run: | cd examples - cmake -DCPP_COMPILE=ON . + cmake -DEXAMPLES_UDP=ON -DCPP_COMPILE=ON . make - compile-examples-windows-c: + compile-examples-udp-windows-c: runs-on: windows-latest steps: - uses: actions/checkout@v3 @@ -272,10 +271,10 @@ jobs: - name: Compile examples run: | cd examples - cmake -G "NMake Makefiles" . + cmake -G "NMake Makefiles" -DEXAMPLES_UDP=ON . nmake - compile-examples-windows-cpp: + compile-examples-udp-windows-cpp: runs-on: windows-latest steps: - uses: actions/checkout@v3 @@ -283,5 +282,5 @@ jobs: - name: Compile examples run: | cd examples - cmake -G "NMake Makefiles" -DCPP_COMPILE=ON . + cmake -G "NMake Makefiles" -DEXAMPLES_UDP=ON -DCPP_COMPILE=ON . nmake diff --git a/.gitignore b/.gitignore index 8ede5b0..abc34c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -*.js *.wasm .vscode .ccls @@ -18,3 +17,7 @@ _deps build build_web .DS_Store +.clangd +.clang-format +.cache +compile_flags.txt diff --git a/bin/github-actions/build_soak_web.sh b/bin/github-actions/build_soak_web.sh index f1a1594..ad8187f 100755 --- a/bin/github-actions/build_soak_web.sh +++ b/bin/github-actions/build_soak_web.sh @@ -5,7 +5,7 @@ source ./emsdk_env.sh cd ../soak mkdir build_web cd build_web -emcmake cmake .. +emcmake cmake -DCMAKE_BUILD_TYPE=Debug .. make cd .. npm install diff --git a/bin/github-actions/run_soak.sh b/bin/github-actions/run_soak.sh index 0b5d143..80c7282 100755 --- a/bin/github-actions/run_soak.sh +++ b/bin/github-actions/run_soak.sh @@ -5,95 +5,87 @@ PACKET_DUPLICATION=0.2 PING=0.15 JITTER=0.1 CHANNEL_COUNT=3 -MESSAGE_COUNT=500 +MESSAGE_COUNT=5000 NODE_CMD="$EMSDK_NODE" -run_client () { - node_client=$1 - echo "Running soak client (run in mode: $node_client)..." - - if [ $node_client -eq 1 ] - then - # WASM WebRTC client - $NODE_CMD build_web/client.js --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_cli_out - elif [ $node_client -eq 2 ] - then - # native WebRTC client - ./build/client --webrtc --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_cli_out - else - # UDP client - ./build/client --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_cli_out - fi - - RESULT=$? - - # when running the soak test in the latest version of emscripten with node 16 - # the client aborts at the end when calling emscripten_force_exit - # I could not figure out why, hence the condition - [[ $node_client -eq 1 ]] && EXPECTED_RESULT=7 || EXPECTED_RESULT=0 - - if [ $RESULT -eq $EXPECTED_RESULT ] - then - echo "Soak test completed with success!" - echo "Printing the end of client logs..." - - tail -n 150 soak_cli_out - - return 0 - else - echo "Soak test failed! (code: $RESULT)" - echo "Printing the end of client logs..." - tail -n 150 soak_cli_out - echo "Printing the end of server logs..." - tail -n 150 soak_serv_out - - return 1 - fi +run_client() { + client_mode=$1 + echo "Running soak client (run in mode: $client_mode)..." + + if [ "$client_mode" = "WEBRTC_EMSCRIPTEN" ]; then + # WASM WebRTC client + $NODE_CMD build_web/client.js --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out + elif [ "$client_mode" = "UDP" ]; then + # UDP client + ./build/client --message_count=$MESSAGE_COUNT --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_cli_out + else + echo "Unknown client mode" + return 1 + fi + + RESULT=$? + + # when running the soak test in the latest version of emscripten with node 16 + # the client aborts at the end when calling emscripten_force_exit + # I could not figure out why, hence the condition + [[ "$client_mode" = "WEBRTC_EMSCRIPTEN" ]] && EXPECTED_RESULT=7 || EXPECTED_RESULT=0 + + if [ $RESULT -eq $EXPECTED_RESULT ]; then + echo "Soak test completed with success!" + + return 0 + else + echo "Soak test failed! (code: $RESULT)" + echo "Printing the end of client logs..." + tail -n 150 soak_cli_out + echo "Printing the end of server logs..." + tail -n 150 soak_serv_out + + return 1 + fi } -exit_soak () { - kill -SIGINT $SERV_PID 2> /dev/null +exit_soak() { + kill -SIGINT $SERV_PID 2>/dev/null - exit $1 + exit $1 } cd soak echo "Starting soak server..." -if [ -n "$WEBRTC" ] -then - $NODE_CMD build_web/server.js --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_serv_out & +if [ -n "$WEBRTC" ]; then + $NODE_CMD build_web/server.js --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_serv_out & else - ./build/server --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &> soak_serv_out & + ./build/server --channel_count=$CHANNEL_COUNT --packet_loss=$PACKET_LOSS --packet_duplication=$PACKET_DUPLICATION --ping=$PING --jitter=$JITTER &>soak_serv_out & fi -if [ $? -eq 0 ] -then - SERV_PID=$! +if [ $? -eq 0 ]; then + SERV_PID=$! - echo "Server started (PID: $SERV_PID)" - echo "Running soak test..." + echo "Server started (PID: $SERV_PID)" + echo "Running soak test..." else - echo "Failed to start soak server!" - exit 1 + echo "Failed to start soak server!" + exit 1 fi sleep 3 -if [ -n "$WEBRTC" ] -then - run_client 1 +if [ -n "$WEBRTC" ]; then + run_client "WEBRTC_EMSCRIPTEN" - exit_soak $? + exit_soak $? +elif [ -n "$WEBRTC_NATIVE" ]; then + # run a UDP client, a webrtc WASM client (emscripten) and a native webrtc client (all connecting to the same server) + + if run_client "UDP" && run_client "WEBRTC_EMSCRIPTEN"; then + exit_soak 0 + else + exit_soak 1 + fi else - if [ -n "$WEBRTC_NATIVE" ] - then - # run a UDP client, a webrtc WASM client (emscripten) and a native webrtc client (all connecting to the same server) - - if run_client 0 && run_client 1 && run_client 2; then - exit_soak 0 - else - exit_soak 1 - fi - fi + run_client "UDP" + + exit_soak $? fi diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index aa94e21..0fd039e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.5) project(nbnet_examples) -add_subdirectory(echo) -add_subdirectory(echo_bytes) +option(EXAMPLES_UDP OFF) + +if (EXAMPLES_UDP) + set(UDP ON) +endif (EXAMPLES_UDP) -if (NOT DEFINED CPP_COMPILE) - # not supported in CPP - add_subdirectory(rpc) -endif (NOT DEFINED CPP_COMPILE) +add_subdirectory(echo) diff --git a/examples/echo/CMakeLists.txt b/examples/echo/CMakeLists.txt index 71eea6f..d691f80 100644 --- a/examples/echo/CMakeLists.txt +++ b/examples/echo/CMakeLists.txt @@ -3,9 +3,11 @@ cmake_minimum_required(VERSION 3.5) project(echo) option(CPP_COMPILE OFF) -option(ENABLE_TLS OFF) +option(UDP OFF) option(WEBRTC_NATIVE OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # allow to compile as cpp if (CPP_COMPILE) file(GLOB_RECURSE CFILES "${CMAKE_SOURCE_DIR}/*.c") @@ -21,47 +23,48 @@ if(CMAKE_COMPILER_IS_GNUCXX) add_compile_options(-Wextra -Wpedantic) endif (CMAKE_COMPILER_IS_GNUCXX) -add_executable(echo_client client.c shared.c) -add_executable(echo_server server.c shared.c) +add_executable(echo_client client.c shared.c ../logging/log.c ../../nbnet.c) +add_executable(echo_server server.c shared.c ../logging/log.c ../../nbnet.c) + +target_include_directories(echo_server PUBLIC ../logging) +target_include_directories(echo_client PUBLIC ../logging) + +if (UDP) + # can't compile UDP driver with emscripten + if (EMSCRIPTEN) + message(SEND_ERROR "Can't compile UDP driver with emscripten") + endif (EMSCRIPTEN) -if (ENABLE_TLS) - message("Compile with TLS enabled") - target_compile_definitions(echo_server PUBLIC NBN_TLS) - target_compile_definitions(echo_client PUBLIC NBN_TLS) -endif (ENABLE_TLS) + message("Compiling with UDP driver") -unset(ENABLE_TLS) + target_compile_definitions(echo_server PUBLIC NBN_UDP) + target_compile_definitions(echo_client PUBLIC NBN_UDP) +endif (UDP) -if (WEBRTC_NATIVE_SERVER) +if (WEBRTC_NATIVE) if (EMSCRIPTEN) - message(FATAL_ERROR "Cannot compile native webrtc driver with emscripten") + message(FATAL_ERROR "Cannot compile native WebRTC driver with emscripten") endif (EMSCRIPTEN) - message("Compile server with webrtc native driver") + message("Compile with webrtc native driver") target_compile_definitions(echo_server PUBLIC NBN_WEBRTC_NATIVE) target_link_libraries(echo_server ${LIBDATACHANNEL_LIBRARY_PATH} m) target_include_directories(echo_server PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") -endif (WEBRTC_NATIVE_SERVER) - -unset(WEBRTC_NATIVE_SERVER) - -if (WEBRTC_NATIVE_CLIENT) - if (EMSCRIPTEN) - message(FATAL_ERROR "Cannot compile native webrtc driver with emscripten") - endif (EMSCRIPTEN) - - message("Compile client with webrtc native driver") target_compile_definitions(echo_client PUBLIC NBN_WEBRTC_NATIVE) target_link_libraries(echo_client ${LIBDATACHANNEL_LIBRARY_PATH} m) target_include_directories(echo_client PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") -endif (WEBRTC_NATIVE_CLIENT) +endif (WEBRTC_NATIVE) -unset(WEBRTC_NATIVE_CLIENT) +unset(WEBRTC_NATIVE) -target_compile_definitions(echo_client PUBLIC NBN_DEBUG) -target_compile_definitions(echo_server PUBLIC NBN_DEBUG) +if (CMAKE_BUILD_TYPE MATCHES "Debug") + message("Compiling in Debug mode with packet simulator enabled") + + target_compile_definitions(echo_client PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) + target_compile_definitions(echo_server PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) +endif() if(WIN32) target_link_libraries(echo_client ws2_32) @@ -81,7 +84,7 @@ endif (UNIX) if (EMSCRIPTEN) set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") - set_target_properties(echo_server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ + set_target_properties(echo_server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../webrtc/js/api.js \ -s ALLOW_MEMORY_GROWTH=1 \ -s TOTAL_MEMORY=30MB \ -s EXIT_RUNTIME=1 \ @@ -89,7 +92,7 @@ if (EMSCRIPTEN) -s ASYNCIFY \ -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - set_target_properties(echo_client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ + set_target_properties(echo_client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../webrtc/js/api.js \ -s ALLOW_MEMORY_GROWTH=1 \ -s TOTAL_MEMORY=30MB \ -s EXIT_RUNTIME=1 \ diff --git a/examples/echo/client.c b/examples/echo/client.c index fd082f1..4fb74ba 100644 --- a/examples/echo/client.c +++ b/examples/echo/client.c @@ -20,81 +20,78 @@ */ -#include #include +#include #include +#include // Has to be defined in exactly *one* source file before including the nbnet header #define NBNET_IMPL #include "shared.h" +#include "log.h" static bool running = true; static bool connected = false; static bool disconnected = false; -void OnConnected(void) -{ - Log(LOG_INFO, "Connected"); +void OnConnected(void) { + log_info("Connected"); connected = true; // Start sending messages } -void OnDisconnected(void) -{ - Log(LOG_INFO, "Disconnected"); +void OnDisconnected(NBN_Client *client) { + log_info("Disconnected"); // Stop the main loop disconnected = true; running = false; // Retrieve the server code used when closing our client connection - if (NBN_GameClient_GetServerCloseCode() == ECHO_SERVER_BUSY_CODE) - { - Log(LOG_INFO, "Another client is already connected"); + if (NBN_Client_GetServerCloseCode(client) == ECHO_SERVER_BUSY_CODE) { + log_info("Another client is already connected"); } } -void OnMessageReceived(void) -{ +void OnMessageReceived(NBN_Client *client) { // Get info about the received message - NBN_MessageInfo msg_info = NBN_GameClient_GetMessageInfo(); + NBN_MessageInfo msg_info = NBN_Client_GetMessageInfo(client); assert(msg_info.type == ECHO_MESSAGE_TYPE); - // Retrieve the received message - EchoMessage *msg = (EchoMessage *)msg_info.data; + NBN_Reader *reader = NBN_Client_ReadMessage(client); + unsigned int length; + int res; - Log(LOG_INFO, "Received echo: %s (%d bytes)", msg->data, msg->length); + res = NBN_Reader_ReadUInt32(reader, &length); + assert(res == 0); + static char msg_str[ECHO_MESSAGE_MAX_LENGTH]; - EchoMessage_Destroy(msg); // Destroy the received echo message -} + res = NBN_Reader_ReadBytes(reader, (uint8_t *)msg_str, length); + assert(res == 0); + msg_str[length] = 0; -int SendEchoMessage(const char *msg) -{ - unsigned int length = strlen(msg); // Compute message length + log_info("Received echo: %s (length: %d, channel: %d)", msg_str, msg_info.length, msg_info.channel_id); +} - // Create the echo message - EchoMessage *echo = EchoMessage_Create(); +int SendEcho(NBN_Client *client, const char *msg) { + NBN_Writer *writer = NBN_Client_CreateReliableMessage(client, ECHO_MESSAGE_TYPE); - if (echo == NULL) + if (!writer) { return -1; + } - // Fill echo message with message the length and the message - echo->length = length + 1; - memcpy(echo->data, msg, length + 1); + unsigned int length = strlen(msg); - // Reliably send it to the server - if (NBN_GameClient_SendReliableMessage(ECHO_MESSAGE_TYPE, echo) < 0) - return -1; + NBN_Writer_WriteUInt32(writer, length); + NBN_Writer_WriteBytes(writer, (uint8_t *)msg, length); return 0; } -int main(int argc, char *argv[]) -{ - if (argc != 2) - { +int main(int argc, char *argv[]) { + if (argc != 2) { printf("Usage: client MSG\n"); // Error, quit the client application @@ -106,10 +103,11 @@ int main(int argc, char *argv[]) } const char *msg = argv[1]; + // reserve 4 bytes to write the message length in the message (see the SendEcho function) + unsigned int msg_max_len = ECHO_MESSAGE_MAX_LENGTH - 4; - if (strlen(msg) > ECHO_MESSAGE_LENGTH - 1) - { - Log(LOG_ERROR, "Message length cannot exceed %d. Exit", ECHO_MESSAGE_LENGTH - 1); + if (strlen(msg) > msg_max_len) { + log_error("Message length cannot exceed %d. Exit", msg_max_len); // Error, quit the client application #ifdef __EMSCRIPTEN__ @@ -119,112 +117,62 @@ int main(int argc, char *argv[]) #endif } -#ifdef __EMSCRIPTEN__ - - // Register the WebRTC driver -#ifdef NBN_TLS - NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = true}); -#else - NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = false}); -#endif // NBN_TLS - -#endif // __EMSCRIPTEN__ - -#ifdef NBN_WEBRTC_NATIVE - -#ifdef NBN_TLS - bool enable_tls = true; -#else - bool enable_tls = false; -#endif // NBN_TLS - - const char *ice_servers[] = { "stun:stun01.sipphone.com" }; - NBN_WebRTC_C_Config cfg = { - .ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = enable_tls, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; - - NBN_WebRTC_C_Register(cfg); - -#endif // NBN_WEBRTC_NATIVE - -#if !defined(__EMSCRIPTEN__) && !defined(NBN_WEBRTC_NATIVE) - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ + // Start the client with a protocol name (must be the same than the one used by the server) + // the server host and port - // Initialize the client + NBN_Client *client = NBN_Client_Create(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT); - // Start the client with a protocol name (must be the same than the one used by the server) - // the server host and port and with packet encryption on or off - if (NBN_GameClient_StartEx(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT, NULL, 0) < 0) - { - Log(LOG_ERROR, "Failed to start client"); + if (NBN_Client_Start(client) < 0) { + log_error("Failed to start client"); // Error, quit the client application #ifdef __EMSCRIPTEN__ emscripten_force_exit(1); #else return 1; -#endif +#endif } - // Registering messages, have to be done after NBN_GameClient_StartEx - // Messages need to be registered on both client and server side - NBN_GameClient_RegisterMessage(ECHO_MESSAGE_TYPE, - (NBN_MessageBuilder)EchoMessage_Create, - (NBN_MessageDestructor)EchoMessage_Destroy, - (NBN_MessageSerializer)EchoMessage_Serialize); - // Number of seconds between client ticks double dt = 1.0 / ECHO_TICK_RATE; - while (running) - { + while (running) { int ev; // Poll for client events - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "An error occured while polling client events. Exit"); + while ((ev = NBN_Client_Poll(client)) != NBN_CLIENT_NO_EVENT) { + if (ev < 0) { + log_error("An error occured while polling client events. Exit"); // Stop main loop running = false; break; } - switch (ev) - { - // Client is connected to the server - case NBN_CONNECTED: - OnConnected(); - break; - - // Client has disconnected from the server - case NBN_DISCONNECTED: - OnDisconnected(); - break; - - // A message has been received from the server - case NBN_MESSAGE_RECEIVED: - OnMessageReceived(); - break; + switch (ev) { + // Client is connected to the server + case NBN_CLIENT_CONNECTED: + OnConnected(); + break; + + // Client has disconnected from the server + case NBN_CLIENT_DISCONNECTED: + OnDisconnected(client); + break; + + // A message has been received from the server + case NBN_CLIENT_MESSAGE_RECEIVED: + OnMessageReceived(client); + break; } } if (disconnected) break; - if (connected) - { - if (SendEchoMessage(msg) < 0) - { - Log(LOG_ERROR, "Failed to send message. Exit"); + if (connected) { + if (SendEcho(client, msg) < 0) { + log_error("Failed to send message. Exit"); // Stop main loop running = false; @@ -233,9 +181,8 @@ int main(int argc, char *argv[]) } // Pack all enqueued messages as packets and send them - if (NBN_GameClient_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets. Exit"); + if (NBN_Client_Flush(client) < 0) { + log_error("Failed to send packets. Exit"); // Stop main loop running = false; @@ -247,7 +194,7 @@ int main(int argc, char *argv[]) } // Stop and deinitialize the client - NBN_GameClient_Stop(); + NBN_Client_Stop(client); #ifdef NBN_WEBRTC_NATIVE NBN_WebRTC_C_Unregister(); diff --git a/examples/echo/package-lock.json b/examples/echo/package-lock.json index a0b6e18..87126cd 100644 --- a/examples/echo/package-lock.json +++ b/examples/echo/package-lock.json @@ -1,1728 +1,26 @@ { "name": "echo", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" + "nbnet": "file:../../webrtc" } }, - "../../net_drivers/webrtc": { + "../../webrtc": { "name": "nbnet", "version": "1.0", "license": "MIT", "dependencies": { + "@roamhq/wrtc": "^0.8.0", "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" + "winston": "^3.2.1" } }, - "../../net_drivers/webrtc/node_modules/@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "../../net_drivers/webrtc/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "../../net_drivers/webrtc/node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "../../net_drivers/webrtc/node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "../../net_drivers/webrtc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../../net_drivers/webrtc/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "../../net_drivers/webrtc/node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "../../net_drivers/webrtc/node_modules/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "../../net_drivers/webrtc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "../../net_drivers/webrtc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "../../net_drivers/webrtc/node_modules/color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "../../net_drivers/webrtc/node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "../../net_drivers/webrtc/node_modules/colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "dependencies": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "../../net_drivers/webrtc/node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "../../net_drivers/webrtc/node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "../../net_drivers/webrtc/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "../../net_drivers/webrtc/node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "../../net_drivers/webrtc/node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "../../net_drivers/webrtc/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "../../net_drivers/webrtc/node_modules/domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "dependencies": { - "webidl-conversions": "^4.0.2" - } - }, - "../../net_drivers/webrtc/node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "../../net_drivers/webrtc/node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "../../net_drivers/webrtc/node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "../../net_drivers/webrtc/node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dependencies": { - "type": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/ext/node_modules/type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - }, - "../../net_drivers/webrtc/node_modules/fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "../../net_drivers/webrtc/node_modules/fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "../../net_drivers/webrtc/node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "../../net_drivers/webrtc/node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dependencies": { - "minipass": "^2.6.0" - } - }, - "../../net_drivers/webrtc/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "../../net_drivers/webrtc/node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "../../net_drivers/webrtc/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "../../net_drivers/webrtc/node_modules/ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "../../net_drivers/webrtc/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "../../net_drivers/webrtc/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "../../net_drivers/webrtc/node_modules/ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "../../net_drivers/webrtc/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "../../net_drivers/webrtc/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "../../net_drivers/webrtc/node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "../../net_drivers/webrtc/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "../../net_drivers/webrtc/node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "../../net_drivers/webrtc/node_modules/logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "dependencies": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - } - }, - "../../net_drivers/webrtc/node_modules/logform/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../../net_drivers/webrtc/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - } - }, - "../../net_drivers/webrtc/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "../../net_drivers/webrtc/node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dependencies": { - "minipass": "^2.9.0" - } - }, - "../../net_drivers/webrtc/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - } - }, - "../../net_drivers/webrtc/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "../../net_drivers/webrtc/node_modules/needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "dependencies": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "../../net_drivers/webrtc/node_modules/needle/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "../../net_drivers/webrtc/node_modules/needle/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../../net_drivers/webrtc/node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "../../net_drivers/webrtc/node_modules/node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "../../net_drivers/webrtc/node_modules/node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "dependencies": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "../../net_drivers/webrtc/node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "../../net_drivers/webrtc/node_modules/npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "../../net_drivers/webrtc/node_modules/npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "dependencies": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "../../net_drivers/webrtc/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "../../net_drivers/webrtc/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "../../net_drivers/webrtc/node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "../../net_drivers/webrtc/node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "../../net_drivers/webrtc/node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "../../net_drivers/webrtc/node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "../../net_drivers/webrtc/node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "../../net_drivers/webrtc/node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" - } - }, - "../../net_drivers/webrtc/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "../../net_drivers/webrtc/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "../../net_drivers/webrtc/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "../../net_drivers/webrtc/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "../../net_drivers/webrtc/node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "../../net_drivers/webrtc/node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "../../net_drivers/webrtc/node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "../../net_drivers/webrtc/node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "../../net_drivers/webrtc/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "../../net_drivers/webrtc/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "../../net_drivers/webrtc/node_modules/tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dependencies": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "../../net_drivers/webrtc/node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "../../net_drivers/webrtc/node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "../../net_drivers/webrtc/node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "../../net_drivers/webrtc/node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../../net_drivers/webrtc/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "../../net_drivers/webrtc/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "../../net_drivers/webrtc/node_modules/websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "../../net_drivers/webrtc/node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "../../net_drivers/webrtc/node_modules/winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "dependencies": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "dependencies": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "../../net_drivers/webrtc/node_modules/wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "dependencies": { - "node-pre-gyp": "^0.13.0" - }, - "optionalDependencies": { - "domexception": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "../../net_drivers/webrtc/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, "node_modules/nbnet": { - "resolved": "../../net_drivers/webrtc", + "resolved": "../../webrtc", "link": true } - }, - "dependencies": { - "nbnet": { - "version": "file:../../net_drivers/webrtc", - "requires": { - "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" - }, - "dependencies": { - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "requires": { - "domexception": "^1.0.1", - "node-pre-gyp": "^0.13.0" - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - } } } diff --git a/examples/echo/package.json b/examples/echo/package.json index b7289f6..5826448 100644 --- a/examples/echo/package.json +++ b/examples/echo/package.json @@ -4,6 +4,6 @@ "client": "node build_web/echo_client.js" }, "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" + "nbnet": "file:../../webrtc" } } diff --git a/examples/echo/server.c b/examples/echo/server.c index 93aa0f6..1431479 100644 --- a/examples/echo/server.c +++ b/examples/echo/server.c @@ -20,57 +20,64 @@ */ -#include #include -#include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL - +#include +#include #include "shared.h" +#include "log.h" -static NBN_ConnectionHandle client = 0; +static NBN_ConnectionHandle *connection = NULL; +static NBN_Connection_ID conn_id; // Echo the received message -static int EchoReceivedMessage(void) -{ +static int EchoReceivedMessage(NBN_Server *server) { // Get info about the received message - NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); + NBN_MessageInfo msg_info = NBN_Server_GetMessageInfo(server); - assert(msg_info.sender == client); assert(msg_info.type == ECHO_MESSAGE_TYPE); - // Retrieve the received message - EchoMessage *msg = (EchoMessage *)msg_info.data; + log_info("Received message of type %d from %lld", msg_info.type, msg_info.sender->id); + + assert(msg_info.sender->id == conn_id); + + // read message data + NBN_Reader *reader = NBN_Server_ReadMessage(server); + unsigned int length; + int res; - // Create an echo message - EchoMessage *echo = EchoMessage_Create(); + res = NBN_Reader_ReadUInt32(reader, &length); + assert(res == 0); + static char msg_str[ECHO_MESSAGE_MAX_LENGTH]; - // Fill it with the received message data and length - memcpy(echo->data, msg->data, msg->length); - echo->length = msg->length; + res = NBN_Reader_ReadBytes(reader, (uint8_t *)msg_str, length); + assert(res == 0); + msg_str[length] = 0; - // Reliably send it to the client - // If the send fails the client will be disconnected and a NBN_CLIENT_DISCONNECTED event - // will be received (see event polling in main) - NBN_GameServer_SendReliableMessageTo(client, ECHO_MESSAGE_TYPE, echo); + log_info("Received message: %s, send echo (length: %d, channel: %d)", msg_str, msg_info.length, + msg_info.channel_id); - EchoMessage_Destroy(msg); // Destroy the received echo message + // create and send an echo of the received message + NBN_Writer *writer = NBN_Server_CreateReliableMessage(server, ECHO_MESSAGE_TYPE, connection); + + if (!writer) { + return -1; + } + + NBN_Writer_WriteUInt32(writer, length); + NBN_Writer_WriteBytes(writer, (uint8_t *)msg_str, length); return 0; } static bool error = false; -int main(int argc, const char **argv) -{ +int main(int argc, const char **argv) { #ifdef __EMSCRIPTEN__ // Register the WebRTC driver -#ifdef NBN_TLS +#ifdef NBN_TLS - if (argc != 3) - { + if (argc != 3) { printf("Usage: server CERT_PATH KEY_PATH\n"); return 1; } @@ -95,27 +102,24 @@ int main(int argc, const char **argv) bool enable_tls = false; #endif // NBN_TLS - const char *ice_servers[] = { "stun:stun01.sipphone.com" }; - NBN_WebRTC_C_Config cfg = { - .ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = enable_tls, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; + const char *ice_servers[] = {"stun:stun01.sipphone.com"}; + NBN_WebRTC_C_Config cfg = {.ice_servers = ice_servers, + .ice_servers_count = 1, + .enable_tls = enable_tls, + .cert_path = NULL, + .key_path = NULL, + .passphrase = NULL, + .log_level = RTC_LOG_VERBOSE}; NBN_WebRTC_C_Register(cfg); #endif // NBN_WEBRTC_NATIVE -#if !defined(__EMSCRIPTEN__) && !defined(NBN_WEBRTC_NATIVE) - NBN_UDP_Register(); // Register the UDP driver -#endif + // Start the server with a protocol name and a port + + NBN_Server *server = NBN_Server_Create(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT); - // Start the server with a protocol name, a port, and with packet encryption on or off - if (NBN_GameServer_StartEx(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT) < 0) - { - Log(LOG_ERROR, "Failed to start the server"); + if (NBN_Server_Start(server) < 0) { + log_error("Failed to start the server"); // Error, quit the server application #ifdef __EMSCRIPTEN__ @@ -125,74 +129,64 @@ int main(int argc, const char **argv) #endif } - (void) argc; - (void) argv; - // Registering messages, have to be done after NBN_GameServer_StartEx - NBN_GameServer_RegisterMessage(ECHO_MESSAGE_TYPE, - (NBN_MessageBuilder)EchoMessage_Create, - (NBN_MessageDestructor)EchoMessage_Destroy, - (NBN_MessageSerializer)EchoMessage_Serialize); - // Number of seconds between server ticks double dt = 1.0 / ECHO_TICK_RATE; - while (true) - { + while (true) { int ev; + NBN_DisconnectionInfo disconnect_info; // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "Something went wrong"); + while ((ev = NBN_Server_Poll(server)) != NBN_SERVER_NO_EVENT) { + if (ev < 0) { + log_error("Something went wrong"); // Error, quit the server application error = true; break; } - switch (ev) - { - // New connection request... - case NBN_NEW_CONNECTION: - // Echo server work with one single client at a time - if (client) - { - NBN_GameServer_RejectIncomingConnectionWithCode(ECHO_SERVER_BUSY_CODE); - } - else - { - NBN_GameServer_AcceptIncomingConnection(); - client = NBN_GameServer_GetIncomingConnection(); - } - - break; - - // The client has disconnected - case NBN_CLIENT_DISCONNECTED: - assert(NBN_GameServer_GetDisconnectedClient() == client); - - client = 0; - break; - - // A message has been received from the client - case NBN_CLIENT_MESSAGE_RECEIVED: - if (EchoReceivedMessage() < 0) - { - Log(LOG_ERROR, "Failed to echo received message"); - - // Error, quit the server application - error = true; - } - break; + switch (ev) { + // New connection request... + case NBN_SERVER_NEW_CONNECTION: + // Echo server work with one single client at a time + if (connection) { + NBN_Server_RejectIncomingConnectionWithCode(server, ECHO_SERVER_BUSY_CODE); + } else { + NBN_Server_AcceptIncomingConnection(server); + connection = NBN_Server_GetIncomingConnection(server); + conn_id = connection->id; + } + + break; + + // The client has disconnected + case NBN_SERVER_DISCONNECTION: + disconnect_info = NBN_Server_GetDisconnectionInfo(server); + + assert(disconnect_info.conn_id == conn_id); + connection = NULL; + break; + + // A message has been received from the client + case NBN_SERVER_MESSAGE_RECEIVED: + if (EchoReceivedMessage(server) < 0) { + log_error("Failed to echo received message"); + + // Error, quit the server application + error = true; + } + break; } } + if (error) { + break; + } + // Pack all enqueued messages as packets and send them - if (NBN_GameServer_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets"); + if (NBN_Server_Flush(server) < 0) { + log_error("Failed to send packets"); // Error, quit the server application error = true; @@ -204,7 +198,7 @@ int main(int argc, const char **argv) } // Stop the server - NBN_GameServer_Stop(); + NBN_Server_Stop(server); #ifdef NBN_WEBRTC_NATIVE NBN_WebRTC_C_Unregister(); diff --git a/examples/echo/shared.c b/examples/echo/shared.c index d615842..2f34218 100644 --- a/examples/echo/shared.c +++ b/examples/echo/shared.c @@ -20,45 +20,29 @@ */ +#include +#include #include #include -#include // Sleep function #if defined(__EMSCRIPTEN__) #include #elif defined(_WIN32) || defined(_WIN64) -#include -#include +#define WIN32_LEAN_AND_MEAN +// prevent inclusion of winnt.h in windows.h +#define _WINNT_ #include +#include #else #include #endif #include "shared.h" -EchoMessage *EchoMessage_Create(void) -{ - return (EchoMessage *) malloc(sizeof(EchoMessage)); -} - -void EchoMessage_Destroy(EchoMessage *msg) -{ - free(msg); -} - -int EchoMessage_Serialize(EchoMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->length, 0, ECHO_MESSAGE_LENGTH); - NBN_SerializeBytes(stream, msg->data, msg->length); - - return 0; -} - // Sleep for a given amount of seconds // Used to limit client and server tick rate -void EchoSleep(double sec) -{ +void EchoSleep(double sec) { #if defined(__EMSCRIPTEN__) emscripten_sleep(sec * 1000); #elif defined(_WIN32) || defined(_WIN64) @@ -70,25 +54,3 @@ void EchoSleep(double sec) nanosleep(&t, &t); #endif } - -static const char *log_type_strings[] = { - "INFO", - "ERROR", - "DEBUG", - "TRACE", - "WARNING" -}; - -// Basic logging function -void Log(int type, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - - printf("[%s] ", log_type_strings[type]); - vprintf(fmt, args); - printf("\n"); - - va_end(args); -} diff --git a/examples/echo/shared.h b/examples/echo/shared.h index dc54042..d2a26c0 100644 --- a/examples/echo/shared.h +++ b/examples/echo/shared.h @@ -26,59 +26,14 @@ #define ECHO_PROTOCOL_NAME "echo-example" #define ECHO_EXAMPLE_PORT 42042 #define ECHO_MESSAGE_TYPE 0 -#define ECHO_MESSAGE_LENGTH 255 +#define ECHO_MESSAGE_MAX_LENGTH 255 #define ECHO_TICK_RATE 30 // An arbitrary chosen code used when rejecting a client to let it know that another client is already connected #define ECHO_SERVER_BUSY_CODE 42 -// nbnet logging -// nbnet does not implement any logging capabilities, you need to provide your own -enum -{ - LOG_INFO, - LOG_ERROR, - LOG_DEBUG, - LOG_TRACE, - LOG_WARNING -}; - -#define NBN_LogInfo(...) Log(LOG_INFO, __VA_ARGS__) -#define NBN_LogError(...) Log(LOG_ERROR, __VA_ARGS__) -#define NBN_LogDebug(...) Log(LOG_DEBUG, __VA_ARGS__) -#define NBN_LogTrace(...) Log(LOG_TRACE, __VA_ARGS__) -#define NBN_LogWarning(...) Log(LOG_WARNING, __VA_ARGS__) - -void Log(int, const char *, ...); - #include "../../nbnet.h" -#ifdef __EMSCRIPTEN__ - -#include "../../net_drivers/webrtc.h" - -#else - -#include "../../net_drivers/udp.h" - -#ifdef NBN_WEBRTC_NATIVE - -#include "../../net_drivers/webrtc_c.h" - -#endif // NBN_WEBRTC_NATIVE - -#endif // __EMSCRIPTEN__ - -typedef struct -{ - unsigned int length; - char data[ECHO_MESSAGE_LENGTH]; -} EchoMessage; - -EchoMessage *EchoMessage_Create(void); -void EchoMessage_Destroy(EchoMessage *); -int EchoMessage_Serialize(EchoMessage *, NBN_Stream *); - void EchoSleep(double); #endif /* ECHO_EXAMPLE_SHARED_H */ diff --git a/examples/echo_bytes/CMakeLists.txt b/examples/echo_bytes/CMakeLists.txt deleted file mode 100644 index a935e17..0000000 --- a/examples/echo_bytes/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(echo_bytes) - -option(CPP_COMPILE OFF) - -# allow to compile as cpp -if (CPP_COMPILE) - file(GLOB_RECURSE CFILES "${CMAKE_SOURCE_DIR}/*.c") - SET_SOURCE_FILES_PROPERTIES(${CFILES} PROPERTIES LANGUAGE CXX) - set (CMAKE_CXX_STANDARD 20) -endif (CPP_COMPILE) - -unset(CPP_COMPILE CACHE) - -add_compile_options(-Wall) - -if(CMAKE_COMPILER_IS_GNUCXX) - add_compile_options(-Wextra -Wpedantic) -endif (CMAKE_COMPILER_IS_GNUCXX) - -add_executable(echo_bytes_client client.c shared.c) -add_executable(echo_bytes_server server.c shared.c) - -target_compile_definitions(echo_bytes_client PUBLIC NBN_DEBUG) -target_compile_definitions(echo_bytes_server PUBLIC NBN_DEBUG) - -if(WIN32) - target_link_libraries(echo_bytes_client wsock32 ws2_32) - target_link_libraries(echo_bytes_server wsock32 ws2_32) -else() - # link with pthread when we are not on windows - target_link_libraries(echo_bytes_client pthread) - target_link_libraries(echo_bytes_server pthread) -endif(WIN32) - -if (UNIX) - # link with libm on unix - target_link_libraries(echo_bytes_client m) - target_link_libraries(echo_bytes_server m) -endif (UNIX) - -if (EMSCRIPTEN) - set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") - - set_target_properties(echo_bytes_server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s TOTAL_MEMORY=30MB \ - -s EXIT_RUNTIME=1 \ - -s ASSERTIONS=1 \ - -s ASYNCIFY \ - -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - - set_target_properties(echo_bytes_client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s TOTAL_MEMORY=30MB \ - -s EXIT_RUNTIME=1 \ - -s ASSERTIONS=1 \ - -s ASYNCIFY \ - -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") -endif() diff --git a/examples/echo_bytes/README.md b/examples/echo_bytes/README.md deleted file mode 100644 index aec7378..0000000 --- a/examples/echo_bytes/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Echo (byte array) - -The very same example as the "echo" one, except it uses byte arrays instead of a user defined message. diff --git a/examples/echo_bytes/client.c b/examples/echo_bytes/client.c deleted file mode 100644 index 4cedaf8..0000000 --- a/examples/echo_bytes/client.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL - -#include "shared.h" - -static bool running = true; -static bool connected = false; -static bool disconnected = false; - -void OnConnected(void) -{ - Log(LOG_INFO, "Connected"); - - connected = true; // Start sending messages -} - -void OnDisconnected(void) -{ - Log(LOG_INFO, "Disconnected"); - - // Stop the main loop - disconnected = true; - running = false; - - // Retrieve the server code used when closing our client connection - if (NBN_GameClient_GetServerCloseCode() == ECHO_SERVER_BUSY_CODE) - { - Log(LOG_INFO, "Another client is already connected"); - } -} - -void OnMessageReceived(void) -{ - // Get info about the received message - NBN_MessageInfo msg_info = NBN_GameClient_GetMessageInfo(); - - assert(msg_info.type == NBN_BYTE_ARRAY_MESSAGE_TYPE); - - // Retrieve the received message - NBN_ByteArrayMessage *msg = (NBN_ByteArrayMessage *)msg_info.data; - - Log(LOG_INFO, "Received echo: %s (%d bytes)", msg->bytes, msg->length); - - // Destroy the received message - NBN_ByteArrayMessage_Destroy(msg); -} - -int SendEchoMessage(const char *msg) -{ - unsigned int length = strlen(msg); // Compute message length - - // Reliably send bytes to the server - if (NBN_GameClient_SendReliableByteArray((uint8_t *)msg, length) < 0) - return -1; - - return 0; -} - -int main(int argc, char *argv[]) -{ - if (argc != 2) - { - printf("Usage: client MSG\n"); - -// Error, quit the client application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - const char *msg = argv[1]; - - if (strlen(msg) > NBN_BYTE_ARRAY_MAX_SIZE - 1) - { - Log(LOG_ERROR, "Message length cannot exceed %d. Exit", NBN_BYTE_ARRAY_MAX_SIZE - 1); - -// Error, quit the client application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ - - // Start the client with a protocol name (must be the same than the one used by the server) - // the server host and port - if (NBN_GameClient_Start(ECHO_PROTOCOL_NAME, "127.0.0.1", ECHO_EXAMPLE_PORT) < 0) - { - Log(LOG_ERROR, "Failed to start client"); - -// Error, quit the client application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - // Number of seconds between client ticks - double dt = 1.0 / ECHO_TICK_RATE; - - while (running) - { - int ev; - - // Poll for client events - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "An error occured while polling client events. Exit"); - - // Stop main loop - running = false; - break; - } - - switch (ev) - { - // Client is connected to the server - case NBN_CONNECTED: - OnConnected(); - break; - - // Client has disconnected from the server - case NBN_DISCONNECTED: - OnDisconnected(); - break; - - // A message has been received from the server - case NBN_MESSAGE_RECEIVED: - OnMessageReceived(); - break; - } - } - - if (disconnected) - break; - - if (connected) - { - if (SendEchoMessage(msg) < 0) - { - Log(LOG_ERROR, "Failed to send message. Exit"); - - // Stop main loop - running = false; - break; - } - } - - // Pack all enqueued messages as packets and send them - if (NBN_GameClient_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets. Exit"); - - // Stop main loop - running = false; - break; - } - - // Cap the client tick rate - EchoSleep(dt); - } - - // Stop the client - NBN_GameClient_Stop(); - -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(0); -#else - return 0; -#endif -} diff --git a/examples/echo_bytes/package.json b/examples/echo_bytes/package.json deleted file mode 100644 index 2048af3..0000000 --- a/examples/echo_bytes/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "scripts": { - "server": "node echo_bytes_server.js", - "client": "node echo_bytes_client.js" - }, - "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" - } -} diff --git a/examples/echo_bytes/server.c b/examples/echo_bytes/server.c deleted file mode 100644 index 2062a26..0000000 --- a/examples/echo_bytes/server.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL - -#include "shared.h" - -static NBN_ConnectionHandle client = 0; - -// Echo the received message -static int EchoReceivedMessage(void) -{ - // Get info about the received message - NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); - - assert(msg_info.sender == client); - assert(msg_info.type == NBN_BYTE_ARRAY_MESSAGE_TYPE); - - // Retrieve the received message - NBN_ByteArrayMessage *msg = (NBN_ByteArrayMessage *)msg_info.data; - - // If the send fails the client will be disconnected and a NBN_CLIENT_DISCONNECTED event - // will be received (see event polling in main) - if (NBN_GameServer_SendReliableByteArrayTo(client, msg->bytes, msg->length) < 0) - return -1; - - // Destroy the received message - NBN_ByteArrayMessage_Destroy(msg); - - return 0; -} - -static bool error = false; - -int main(void) -{ -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ - - // Start the server with a protocol name and a port - if (NBN_GameServer_Start(ECHO_PROTOCOL_NAME, ECHO_EXAMPLE_PORT) < 0) - { - Log(LOG_ERROR, "Failed to start the server"); - - // Error, quit the server application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - // Number of seconds between server ticks - double dt = 1.0 / ECHO_TICK_RATE; - - while (true) - { - int ev; - - // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "Something went wrong"); - - // Error, quit the server application - error = true; - break; - } - - switch (ev) - { - // New connection request... - case NBN_NEW_CONNECTION: - // Echo server work with one single client at a time - if (client) - { - NBN_GameServer_RejectIncomingConnectionWithCode(ECHO_SERVER_BUSY_CODE); - } - else - { - NBN_GameServer_AcceptIncomingConnection(); - client = NBN_GameServer_GetIncomingConnection(); - } - - break; - - // The client has disconnected - case NBN_CLIENT_DISCONNECTED: - assert(NBN_GameServer_GetDisconnectedClient() == client); - - client = 0; - break; - - // A message has been received from the client - case NBN_CLIENT_MESSAGE_RECEIVED: - if (EchoReceivedMessage() < 0) - { - Log(LOG_ERROR, "Failed to echo received message"); - - // Error, quit the server application - error = true; - } - break; - } - } - - // Pack all enqueued messages as packets and send them - if (NBN_GameServer_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets"); - - // Error, quit the server application - error = true; - break; - } - - // Cap the server tick rate - EchoSleep(dt); - } - - // Stop the server - NBN_GameServer_Stop(); - - int ret = error ? 1 : 0; - -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(ret); -#else - return ret; -#endif -} diff --git a/examples/echo_bytes/shared.c b/examples/echo_bytes/shared.c deleted file mode 100644 index 6dd9a0c..0000000 --- a/examples/echo_bytes/shared.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Sleep function -#if defined(__EMSCRIPTEN__) -#include -#elif defined(_WIN32) || defined(_WIN64) -#include -#include -#include -#else -#include -#endif - -#include "shared.h" - -// Sleep for a given amount of seconds -// Used to limit client and server tick rate -void EchoSleep(double sec) -{ -#if defined(__EMSCRIPTEN__) - emscripten_sleep(sec * 1000); -#elif defined(_WIN32) || defined(_WIN64) - Sleep(sec * 1000); -#else /* UNIX / OSX */ - long nanos = sec * 1e9; - struct timespec t = {.tv_sec = nanos / 999999999, .tv_nsec = nanos % 999999999}; - - nanosleep(&t, &t); -#endif -} - -static const char *log_type_strings[] = { - "INFO", - "ERROR", - "DEBUG", - "TRACE", - "WARNING" -}; - -// Basic logging function -void Log(int type, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - - printf("[%s] ", log_type_strings[type]); - vprintf(fmt, args); - printf("\n"); - - va_end(args); -} diff --git a/examples/echo_bytes/shared.h b/examples/echo_bytes/shared.h deleted file mode 100644 index eeebe02..0000000 --- a/examples/echo_bytes/shared.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#ifndef ECHO_EXAMPLE_SHARED_H -#define ECHO_EXAMPLE_SHARED_H - -#define ECHO_PROTOCOL_NAME "echo-example" -#define ECHO_EXAMPLE_PORT 42042 -#define ECHO_MESSAGE_TYPE 0 -#define ECHO_TICK_RATE 30 - -// An arbitrary chosen code used when rejecting a client to let it know that another client is already connected -#define ECHO_SERVER_BUSY_CODE 42 - -// nbnet logging -// nbnet does not implement any logging capabilities, you need to provide your own -enum -{ - LOG_INFO, - LOG_ERROR, - LOG_DEBUG, - LOG_TRACE, - LOG_WARNING -}; - -#define NBN_LogInfo(...) Log(LOG_INFO, __VA_ARGS__) -#define NBN_LogError(...) Log(LOG_ERROR, __VA_ARGS__) -#define NBN_LogDebug(...) Log(LOG_DEBUG, __VA_ARGS__) -#define NBN_LogTrace(...) Log(LOG_TRACE, __VA_ARGS__) -#define NBN_LogWarning(...) Log(LOG_WARNING, __VA_ARGS__) - -void Log(int, const char *, ...); - -#include "../../nbnet.h" - -#ifdef __EMSCRIPTEN__ -#include "../../net_drivers/webrtc.h" -#else -#include "../../net_drivers/udp.h" -#endif - -void EchoSleep(double); - -#endif /* ECHO_EXAMPLE_SHARED_H */ diff --git a/examples/logging/log.c b/examples/logging/log.c new file mode 100644 index 0000000..55275e4 --- /dev/null +++ b/examples/logging/log.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2020 rxi + * + * 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. + */ + +#include "log.h" + +#define MAX_CALLBACKS 32 + +typedef struct { + log_LogFn fn; + void *udata; + int level; +} Callback; + +static struct { + void *udata; + log_LockFn lock; + int level; + bool quiet; + Callback callbacks[MAX_CALLBACKS]; +} L; + +static const char *level_strings[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; + +#ifdef LOG_USE_COLOR +static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; +#endif + +static void stdout_callback(log_Event *ev) { + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; +#ifdef LOG_USE_COLOR + fprintf((FILE *)ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[ev->level], + level_strings[ev->level], ev->file, ev->line); +#else + fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); +#endif + vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); + fprintf((FILE *)ev->udata, "\n"); + fflush((FILE *)ev->udata); +} + +static void file_callback(log_Event *ev) { + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); + vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); + fprintf((FILE *)ev->udata, "\n"); + fflush((FILE *)ev->udata); +} + +static void lock(void) { + if (L.lock) { + L.lock(true, L.udata); + } +} + +static void unlock(void) { + if (L.lock) { + L.lock(false, L.udata); + } +} + +const char *log_level_string(int level) { return level_strings[level]; } + +void log_set_lock(log_LockFn fn, void *udata) { + L.lock = fn; + L.udata = udata; +} + +void log_set_level(int level) { L.level = level; } + +void log_set_quiet(bool enable) { L.quiet = enable; } + +int log_add_callback(log_LogFn fn, void *udata, int level) { + for (int i = 0; i < MAX_CALLBACKS; i++) { + if (!L.callbacks[i].fn) { + L.callbacks[i] = (Callback){fn, udata, level}; + return 0; + } + } + return -1; +} + +int log_add_fp(FILE *fp, int level) { return log_add_callback(file_callback, fp, level); } + +static void init_event(log_Event *ev, void *udata) { + if (!ev->time) { + time_t t = time(NULL); + ev->time = localtime(&t); + } + ev->udata = udata; +} + +void log_log(int level, const char *file, int line, const char *fmt, ...) { + log_Event ev = { + .fmt = fmt, + .file = file, + .line = line, + .level = level, + }; + + lock(); + + if (!L.quiet && level >= L.level) { + init_event(&ev, stderr); + va_start(ev.ap, fmt); + stdout_callback(&ev); + va_end(ev.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { + Callback *cb = &L.callbacks[i]; + if (level >= cb->level) { + init_event(&ev, cb->udata); + va_start(ev.ap, fmt); + cb->fn(&ev); + va_end(ev.ap); + } + } + + unlock(); +} diff --git a/examples/logging/log.h b/examples/logging/log.h new file mode 100644 index 0000000..b1fae24 --- /dev/null +++ b/examples/logging/log.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2020 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include +#include +#include + +#define LOG_VERSION "0.1.0" + +typedef struct { + va_list ap; + const char *fmt; + const char *file; + struct tm *time; + void *udata; + int line; + int level; +} log_Event; + +typedef void (*log_LogFn)(log_Event *ev); +typedef void (*log_LockFn)(bool lock, void *udata); + +enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; + +#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +const char* log_level_string(int level); +void log_set_lock(log_LockFn fn, void *udata); +void log_set_level(int level); +void log_set_quiet(bool enable); +int log_add_callback(log_LogFn fn, void *udata, int level); +int log_add_fp(FILE *fp, int level); + +void log_log(int level, const char *file, int line, const char *fmt, ...); + +#endif diff --git a/examples/raylib/CMakeLists.txt b/examples/raylib/CMakeLists.txt index f52651a..7f1ee98 100644 --- a/examples/raylib/CMakeLists.txt +++ b/examples/raylib/CMakeLists.txt @@ -4,14 +4,10 @@ project(raylib_example C) set(raylib_DIR cmake) -set(CLIENT_SOURCES client.c shared.c) -set(SERVER_SOURCES server.c shared.c) +set(CLIENT_SOURCES client.c shared.c ../../nbnet.c) +set(SERVER_SOURCES server.c shared.c ../../nbnet.c) -add_compile_options(-Wall -Wno-unknown-pragmas -Wno-type-limits -std=c99) - -if(CMAKE_COMPILER_IS_GNUCXX) - add_compile_options(-Wextra -Wpedantic) -endif (CMAKE_COMPILER_IS_GNUCXX) +add_compile_options(-Wall -Wextra -std=c99 -Wno-gnu-zero-variadic-macro-arguments) add_executable(raylib_client ${CLIENT_SOURCES}) add_executable(raylib_server ${SERVER_SOURCES}) @@ -35,11 +31,20 @@ target_compile_definitions(raylib_client PUBLIC NBN_DEBUG) target_compile_definitions(raylib_server PUBLIC NBN_DEBUG) target_compile_definitions(raylib_server PUBLIC NBN_RAYLIB_SERVER) -target_compile_definitions(raylib_client PUBLIC _GNU_SOURCE) -target_compile_definitions(raylib_server PUBLIC _GNU_SOURCE) +if (UDP) + # can't compile UDP driver with emscripten + if (EMSCRIPTEN) + message(SEND_ERROR "Can't compile UDP driver with emscripten") + endif (EMSCRIPTEN) + + message("Compiling with UDP driver") + + target_compile_definitions(raylib_server PUBLIC NBN_UDP) + target_compile_definitions(raylib_client PUBLIC NBN_UDP) +endif (UDP) # compile with C WebRTC driver -if (WEBRTC_C_DRIVER) +if (WEBRTC_NATIVE) # can't compile WebRTC native driver with emscripten if (EMSCRIPTEN) message(SEND_ERROR "Can't compile WebRTC native driver with emscripten") @@ -49,13 +54,8 @@ if (WEBRTC_C_DRIVER) target_compile_definitions(raylib_server PUBLIC SOAK_WEBRTC_C_DRIVER) - target_link_libraries(raylib_server ${LIBFACILIO_LIBRARY_PATH}) - target_link_libraries(raylib_server ${LIBCRYPTO_LIBRARY_PATH}) - target_link_libraries(raylib_server ${LIBSSL_LIBRARY_PATH}) target_link_libraries(raylib_server ${LIBDATACHANNEL_LIBRARY_PATH}) - target_include_directories(raylib_server PUBLIC "${LIBFACILIO_INCLUDE_PATH}") - target_include_directories(raylib_server PUBLIC "${OPENSSL_INCLUDE_PATH}") target_include_directories(raylib_server PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") if (USE_HTTPS) @@ -63,9 +63,9 @@ if (WEBRTC_C_DRIVER) target_compile_definitions(raylib_server PUBLIC NBN_HTTPS_KEY_PEM="localhost.key") target_compile_definitions(raylib_server PUBLIC NBN_HTTPS_CERT_PEM="localhost.crt") endif (USE_HTTPS) -endif (WEBRTC_C_DRIVER) +endif (WEBRTC_NATIVE) -unset(WEBRTC_C_DRIVER) +unset(WEBRTC_NATIVE) # Use HTTPS (for WebRTC drivers) if (USE_HTTPS) @@ -80,16 +80,17 @@ unset(USE_HTTPS) if (EMSCRIPTEN) set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=30MB -s EXIT_RUNTIME=1 -s ASSERTIONS=1 -s ASYNCIFY -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../webrtc/js/api.js") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s USE_GLFW=3 --shell-file ${CMAKE_CURRENT_SOURCE_DIR}/shell.html") set_target_properties(raylib_client PROPERTIES SUFFIX ".html") add_custom_command( TARGET raylib_client POST_BUILD - COMMAND browserify ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/nbnet.js -o nbnet_bundle.js) + COMMAND browserify ${CMAKE_CURRENT_SOURCE_DIR}/../../webrtc/js/nbnet.js -o nbnet_bundle.js) endif() if (APPLE) target_link_libraries(raylib_client "-framework OpenGL -framework Cocoa -framework IOKit -framework CoreAudio -framework CoreVideo") + target_link_libraries(raylib_server "-framework OpenGL -framework Cocoa -framework IOKit -framework CoreAudio -framework CoreVideo") endif (APPLE) diff --git a/examples/raylib/client.c b/examples/raylib/client.c index c84c41c..be24f34 100644 --- a/examples/raylib/client.c +++ b/examples/raylib/client.c @@ -19,7 +19,11 @@ */ +#include #include +#include +#include +#include #ifdef __EMSCRIPTEN__ #include @@ -58,9 +62,15 @@ Color client_colors_to_raylib_colors[] = { PINK // CLI_PINK }; -static void SpawnLocalClient(int x, int y, uint32_t client_id) -{ - TraceLog(LOG_INFO, "Received spawn message, position: (%d, %d), client id: %d", x, y, client_id); +static void WriteConnectionRequestData(NBN_Client *client, const char *name) { + NBN_Writer *writer = NBN_Client_WriteConnectionRequestData(client); + + // send the name of the client as the connection request data + NBN_Writer_WriteString(writer, name, CLIENT_NAME_MAX_LEN); +} + +static void SpawnLocalClient(int x, int y, uint32_t client_id) { + TraceLog(LOG_INFO, "Spawning at (%d, %d), client id: %d", x, y, client_id); // Update the local client state based on spawn info sent by the server local_client_state.client_id = client_id; @@ -70,30 +80,32 @@ static void SpawnLocalClient(int x, int y, uint32_t client_id) spawned = true; } -static void HandleConnection(void) -{ - uint8_t data[32]; - unsigned int data_len = NBN_GameClient_ReadServerData(data); - NBN_ReadStream rs; +static int HandleConnection(NBN_Client *client) { + TraceLog(LOG_INFO, "Connected, reading connection data..."); - NBN_ReadStream_Init(&rs, data, data_len); + uint32_t x, y, client_id; + NBN_Reader *reader = NBN_Client_ReadServerData(client); + + if (NBN_Reader_ReadUInt32(reader, &x) < 0) { + return -1; + } - unsigned int x = 0; - unsigned int y = 0; - unsigned int client_id = 0; + if (NBN_Reader_ReadUInt32(reader, &y) < 0) { + return -1; + } - NBN_SerializeUInt(((NBN_Stream *)&rs), x, 0, GAME_WIDTH); - NBN_SerializeUInt(((NBN_Stream *)&rs), y, 0, GAME_HEIGHT); - NBN_SerializeUInt(((NBN_Stream *)&rs), client_id, 0, UINT_MAX); + if (NBN_Reader_ReadUInt32(reader, &client_id) < 0) { + return -1; + } SpawnLocalClient(x, y, client_id); connected = true; + return 0; } -static void HandleDisconnection(void) -{ - int code = NBN_GameClient_GetServerCloseCode(); // Get the server code used when closing the client connection +static void HandleDisconnection(NBN_Client *client) { + int code = NBN_Client_GetServerCloseCode(client); // Get the server code used when closing the client connection TraceLog(LOG_INFO, "Disconnected from server (code: %d)", code); @@ -101,10 +113,8 @@ static void HandleDisconnection(void) server_close_code = code; } -static bool ClientExists(uint32_t client_id) -{ - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { +static bool ClientExists(uint32_t client_id) { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { if (clients[i] && clients[i]->client_id == client_id) return true; } @@ -112,18 +122,15 @@ static bool ClientExists(uint32_t client_id) return false; } -static void CreateClient(ClientState state) -{ +static void CreateClient(ClientState state) { TraceLog(LOG_DEBUG, "CreateClient %d", state.client_id); assert(client_count < MAX_CLIENTS - 1); ClientState *client = NULL; // Create a new remote client state and store it in the remote clients array at the first free slot found - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { - if (clients[i] == NULL) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { + if (clients[i] == NULL) { client = malloc(sizeof(ClientState)); clients[i] = client; @@ -141,15 +148,12 @@ static void CreateClient(ClientState state) TraceLog(LOG_INFO, "New remote client (ID: %d)", client->client_id); } -static void UpdateClient(ClientState state) -{ +static void UpdateClient(ClientState state) { ClientState *client = NULL; // Find the client matching the client id of the received remote client state - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { - if (clients[i] && clients[i]->client_id == state.client_id) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { + if (clients[i] && clients[i]->client_id == state.client_id) { client = clients[i]; break; @@ -162,15 +166,12 @@ static void UpdateClient(ClientState state) memcpy(client, &state, sizeof(ClientState)); } -static void DestroyClient(uint32_t client_id) -{ +static void DestroyClient(uint32_t client_id) { // Find the client matching the client id and destroy it - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { ClientState *client = clients[i]; - if (client && client->client_id == client_id) - { + if (client && client->client_id == client_id) { TraceLog(LOG_INFO, "Destroy disconnected client (ID: %d)", client->client_id); free(client); @@ -182,24 +183,20 @@ static void DestroyClient(uint32_t client_id) } } -static void DestroyDisconnectedClients(void) -{ +static void DestroyDisconnectedClients(void) { /* Loop over all remote client states and remove the one that have not * been updated with the last received game state. * This is how we detect disconnected clients. */ - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { if (clients[i] == NULL) continue; uint32_t client_id = clients[i]->client_id; bool disconnected = true; - for (int j = 0; j < MAX_CLIENTS; j++) - { - if ((int)client_id == updated_ids[j]) - { + for (int j = 0; j < MAX_CLIENTS; j++) { + if ((int)client_id == updated_ids[j]) { disconnected = false; break; @@ -211,132 +208,131 @@ static void DestroyDisconnectedClients(void) } } -static void HandleGameStateMessage(GameStateMessage *msg) -{ +static void HandleGameStateMessage(NBN_Client *client) { if (!spawned) return; // Start by resetting the updated client ids array - for (int i = 0; i < MAX_CLIENTS; i++) + for (int i = 0; i < MAX_CLIENTS; i++) { updated_ids[i] = -1; + } + + static GameState recv_game_state; + + // Read the game state from the received GAME_STATE_MESSAGE message + NBN_Reader *reader = NBN_Client_ReadMessage(client); - // Loop over the received client states - for (unsigned int i = 0; i < msg->client_count; i++) - { - ClientState state = msg->client_states[i]; + if (GameStateMessage_Read(reader, &recv_game_state) < 0) { + TraceLog(LOG_ERROR, "Failed to read game state"); + abort(); + } + + // Loop over the received client states and update the clients + for (unsigned int i = 0; i < recv_game_state.client_count; i++) { + ClientState cli_state = recv_game_state.client_states[i]; // Ignore the state of the local client - if (state.client_id != local_client_state.client_id) - { + if (cli_state.client_id != local_client_state.client_id) { // If the client already exists we update it with the latest received state - if (ClientExists(state.client_id)) - UpdateClient(state); + if (ClientExists(cli_state.client_id)) + UpdateClient(cli_state); else // If the client does not exist, we create it - CreateClient(state); + CreateClient(cli_state); - updated_ids[i] = state.client_id; + updated_ids[i] = cli_state.client_id; } } // Destroy disconnected clients DestroyDisconnectedClients(); - - GameStateMessage_Destroy(msg); } -static void HandleReceivedMessage(void) -{ +static void HandleReceivedMessage(NBN_Client *client) { // Fetch info about the last received message - NBN_MessageInfo msg_info = NBN_GameClient_GetMessageInfo(); + NBN_MessageInfo msg_info = NBN_Client_GetMessageInfo(client); - switch (msg_info.type) - { + switch (msg_info.type) { // We received the latest game state from the server case GAME_STATE_MESSAGE: - HandleGameStateMessage(msg_info.data); - break; + HandleGameStateMessage(client); + return; } + + TraceLog(LOG_ERROR, "Received unexpected message"); + abort(); } -static void HandleGameClientEvent(int ev) -{ - switch (ev) - { - case NBN_CONNECTED: +static void HandleGameClientEvent(NBN_Client *client, int ev) { + switch (ev) { + case NBN_CLIENT_CONNECTED: // We are connected to the server - HandleConnection(); + if (HandleConnection(client) < 0) { + TraceLog(LOG_ERROR, "Failed to handle connection"); + abort(); + } break; - case NBN_DISCONNECTED: + case NBN_CLIENT_DISCONNECTED: // The server has closed our connection - HandleDisconnection(); + HandleDisconnection(client); break; - case NBN_MESSAGE_RECEIVED: + case NBN_CLIENT_MESSAGE_RECEIVED: // We received a message from the server - HandleReceivedMessage(); + HandleReceivedMessage(client); break; } } -static int SendPositionUpdate(void) -{ - UpdateStateMessage *msg = UpdateStateMessage_Create(); - - // Fill message data - msg->x = local_client_state.x; - msg->y = local_client_state.y; - msg->val = local_client_state.val; +static int SendStateUpdate(NBN_Client *client) { + NBN_Writer *writer = NBN_Client_CreateUnreliableMessage(client, UPDATE_STATE_MESSAGE); - // Unreliably send it to the server - if (NBN_GameClient_SendUnreliableMessage(UPDATE_STATE_MESSAGE, msg) < 0) + if (!writer) { return -1; + } + + UpdateClientStateMessage_Write(writer, local_client_state); return 0; } -static int SendColorUpdate(void) -{ - ChangeColorMessage *msg = ChangeColorMessage_Create(); +static int SendColorUpdate(NBN_Client *client) { + NBN_Writer *writer = NBN_Client_CreateReliableMessage(client, CHANGE_COLOR_MESSAGE); - // Fill message data - msg->color = local_client_state.color; - - // Reliably send it to the server - if (NBN_GameClient_SendReliableMessage(CHANGE_COLOR_MESSAGE, msg) < 0) + if (!writer) { return -1; + } + + ChangeColorMessage_Write(writer, local_client_state.color); return 0; } bool color_key_pressed = false; -static int Update(void) -{ +static int Update(NBN_Client *client) { if (!spawned) return 0; // Movement code if (IsKeyDown(KEY_UP)) - local_client_state.y = MAX(0, local_client_state.y - 5); + local_client_state.y = (int)fmax(0, local_client_state.y - 5); else if (IsKeyDown(KEY_DOWN)) - local_client_state.y = MIN(GAME_HEIGHT - 50, local_client_state.y + 5); + local_client_state.y = (int)fmin(GAME_HEIGHT - 50, local_client_state.y + 5); if (IsKeyDown(KEY_LEFT)) - local_client_state.x = MAX(0, local_client_state.x - 5); + local_client_state.x = (int)fmax(0, local_client_state.x - 5); else if (IsKeyDown(KEY_RIGHT)) - local_client_state.x = MIN(GAME_WIDTH - 50, local_client_state.x + 5); + local_client_state.x = (int)fmin(GAME_WIDTH - 50, local_client_state.x + 5); // Color switching - if (IsKeyDown(KEY_SPACE) && !color_key_pressed) - { + if (IsKeyDown(KEY_SPACE) && !color_key_pressed) { color_key_pressed = true; local_client_state.color = (local_client_state.color + 1) % MAX_COLORS; TraceLog(LOG_INFO, "Switched color, new color: %d", local_client_state.color); - if (SendColorUpdate() < 0) - { + if (SendColorUpdate(client) < 0) { TraceLog(LOG_WARNING, "Failed to send color update"); return -1; @@ -348,14 +344,13 @@ static int Update(void) // Increasing/Decreasing floating point value if (IsKeyDown(KEY_K)) - local_client_state.val = MIN(MAX_FLOAT_VAL, local_client_state.val + 0.005); + local_client_state.val = fmin(MAX_FLOAT_VAL, local_client_state.val + 0.005); if (IsKeyDown(KEY_J)) - local_client_state.val = MAX(MIN_FLOAT_VAL, local_client_state.val - 0.005); + local_client_state.val = fmax(MIN_FLOAT_VAL, local_client_state.val - 0.005); // Send the latest local client state to the server - if (SendPositionUpdate() < 0) - { + if (SendStateUpdate(client) < 0) { TraceLog(LOG_WARNING, "Failed to send client state update"); return -1; @@ -364,23 +359,23 @@ static int Update(void) return 0; } -void DrawClient(ClientState *state, bool is_local) -{ +void DrawClient(ClientState *state, bool is_local) { Color color = client_colors_to_raylib_colors[state->color]; - const char *text = TextFormat("%.3f", state->val); + const char *val_text = TextFormat("%.3f", state->val); int font_size = 20; - int text_width = MeasureText(text, font_size); + int name_text_width = MeasureText(state->name, font_size); + int val_text_width = MeasureText(val_text, font_size); - DrawText(text, (state->x + 25) - text_width / 2, state->y - 20, font_size, color); + DrawText(state->name, (state->x + 25) - name_text_width / 2, state->y - 20, font_size, color); + DrawText(val_text, (state->x + 25) - val_text_width / 2, state->y + 70, font_size, color); DrawRectangle(state->x, state->y, 50, 50, color); if (is_local) DrawRectangleLinesEx((Rectangle){state->x, state->y, 50, 50}, 3, DARKBROWN); } -void DrawHUD(void) -{ - NBN_ConnectionStats stats = NBN_GameClient_GetStats(); +void DrawHUD(NBN_Client *client) { + NBN_ConnectionStats stats = NBN_Client_GetStats(client); unsigned int ping = stats.ping * 1000; unsigned int packet_loss = stats.packet_loss * 100; @@ -391,30 +386,22 @@ void DrawHUD(void) DrawText(TextFormat("Download: %.1f Bps", stats.download_bandwidth), 450, 550, 32, MAROON); } -void Draw(void) -{ +void Draw(NBN_Client *client) { BeginDrawing(); ClearBackground(LIGHTGRAY); - if (disconnected) - { - if (server_close_code == -1) - { + if (disconnected) { + if (server_close_code == -1) { if (connected) DrawText("Connection to the server was lost", 265, 280, 20, RED); else DrawText("Server cannot be reached", 265, 280, 20, RED); - } - else if (server_close_code == SERVER_FULL_CODE) - { + } else if (server_close_code == SERVER_FULL_CODE) { DrawText("Cannot connect, server is full", 265, 280, 20, RED); } - } - else if (connected && spawned) - { + } else if (connected && spawned) { // Start by drawing the remote clients - for (int i = 0; i < MAX_CLIENTS - 1; i++) - { + for (int i = 0; i < MAX_CLIENTS - 1; i++) { if (clients[i]) DrawClient(clients[i], false); } @@ -423,10 +410,8 @@ void Draw(void) DrawClient(&local_client_state, true); // Finally draw the HUD - DrawHUD(); - } - else - { + DrawHUD(client); + } else { DrawText("Connecting to server...", 265, 280, 20, RED); } @@ -436,8 +421,7 @@ void Draw(void) static double tick_dt = 1.0 / TICK_RATE; // Tick delta time (in seconds) static double acc = 0; -void UpdateAndDraw(void) -{ +void UpdateAndDraw(NBN_Client *client) { // Very basic fixed timestep implementation. // Target FPS is either 100 (in desktop) or whatever the browser frame rate is (in web) but the simulation runs at // TICK_RATE ticks per second. @@ -447,32 +431,26 @@ void UpdateAndDraw(void) acc += GetFrameTime(); // Accumulates time // Simulates as many ticks as we can - while (acc >= tick_dt) - { + while (acc >= tick_dt) { int ev; - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { + while ((ev = NBN_Client_Poll(client)) != NBN_CLIENT_NO_EVENT) { + if (ev < 0) { TraceLog(LOG_WARNING, "An occured while polling network events. Exit"); break; } - HandleGameClientEvent(ev); + HandleGameClientEvent(client, ev); } - if (connected && !disconnected) - { - if (Update() < 0) + if (connected && !disconnected) { + if (Update(client) < 0) break; } - if (!disconnected) - { - if (NBN_GameClient_SendPackets() < 0) - { + if (!disconnected) { + if (NBN_Client_Flush(client) < 0) { TraceLog(LOG_ERROR, "An occured while flushing the send queue. Exit"); break; @@ -482,27 +460,31 @@ void UpdateAndDraw(void) acc -= tick_dt; // Consumes time } - Draw(); + Draw(client); } -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { // Read command line arguments expect when we are running in a web browser. // When running in web browser we need another way to provide arguments (TODO) #ifdef __EMSCRIPTEN__ - if (ReadCommandLine(argc, argv) < 0) - { + if (ReadCommandLine(argc, argv) < 0) { printf("Usage: client [--packet_loss=] [--packet_duplication=] [--ping=] \ [--jitter=]\n"); return 1; } -#else - (void)argc; - (void)argv; #endif - SetTraceLogLevel(LOG_DEBUG); + if (argc < 2) { + printf("Usage: raylib_client NAME\n"); + return 1; + } + + const char *name = argv[1]; + + memcpy(local_client_state.name, name, sizeof(local_client_state.name)); + + SetTraceLogLevel(LOG_TRACE); InitWindow(GAME_WIDTH, GAME_HEIGHT, "raylib client"); // Set target FPS to 100 when we are not running in a web browser @@ -510,66 +492,42 @@ int main(int argc, char *argv[]) SetTargetFPS(TARGET_FPS); #endif -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ + // Initialize the client with a protocol name, the server host and the server port + // protocol name has to be the same as the one used by the server + NBN_Client *client = NBN_Client_Create(RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT); - // Initialize the client with a protocol name (must be the same than the one used by the server), the server ip address and port + WriteConnectionRequestData(client, name); - // Start the client with a protocol name (must be the same than the one used by the server) - // the server host and port - if (NBN_GameClient_StartEx(RAYLIB_EXAMPLE_PROTOCOL_NAME, "127.0.0.1", RAYLIB_EXAMPLE_PORT, NULL, 0) < 0) - { + // Start the client with the configuration + if (NBN_Client_Start(client) < 0) { TraceLog(LOG_WARNING, "Game client failed to start. Exit"); return 1; } - // Register messages, have to be done after NBN_GameClient_StartEx - // Messages need to be registered on both client and server side - NBN_GameClient_RegisterMessage( - CHANGE_COLOR_MESSAGE, - (NBN_MessageBuilder)ChangeColorMessage_Create, - (NBN_MessageDestructor)ChangeColorMessage_Destroy, - (NBN_MessageSerializer)ChangeColorMessage_Serialize); - NBN_GameClient_RegisterMessage( - UPDATE_STATE_MESSAGE, - (NBN_MessageBuilder)UpdateStateMessage_Create, - (NBN_MessageDestructor)UpdateStateMessage_Destroy, - (NBN_MessageSerializer)UpdateStateMessage_Serialize); - NBN_GameClient_RegisterMessage( - GAME_STATE_MESSAGE, - (NBN_MessageBuilder)GameStateMessage_Create, - (NBN_MessageDestructor)GameStateMessage_Destroy, - (NBN_MessageSerializer)GameStateMessage_Serialize); - // Network conditions simulated variables (read from the command line, default is always 0) - NBN_GameClient_SetPing(GetOptions().ping); - NBN_GameClient_SetJitter(GetOptions().jitter); - NBN_GameClient_SetPacketLoss(GetOptions().packet_loss); - NBN_GameClient_SetPacketDuplication(GetOptions().packet_duplication); + NBN_Client_SetPing(client, GetOptions().ping); + NBN_Client_SetJitter(client, GetOptions().jitter); + NBN_Client_SetPacketLoss(client, GetOptions().packet_loss); + NBN_Client_SetPacketDuplication(client, GetOptions().packet_duplication); // Main loop #ifdef __EMSCRIPTEN__ - while (true) - { - UpdateAndDraw(); + while (true) { + UpdateAndDraw(client); // Since we don't set any target FPS when running in a web browser we need to sleep for the correct amount // of time to achieve the targetted FPS emscripten_sleep(1000 / TARGET_FPS); } #else - while (!WindowShouldClose()) - { - UpdateAndDraw(); + while (!WindowShouldClose()) { + UpdateAndDraw(client); } #endif // Stop the client - NBN_GameClient_Stop(); + NBN_Client_Stop(client); CloseWindow(); diff --git a/examples/raylib/package-lock.json b/examples/raylib/package-lock.json index 7c0a7f6..bac9863 100644 --- a/examples/raylib/package-lock.json +++ b/examples/raylib/package-lock.json @@ -1,1842 +1,26 @@ { "name": "raylib", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" + "nbnet": "file:../../webrtc" } }, - "../../net_drivers/webrtc": { + "../../webrtc": { "name": "nbnet", - "version": "0.0.1", + "version": "1.0", "license": "MIT", "dependencies": { + "@roamhq/wrtc": "^0.8.0", "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" + "winston": "^3.2.1" } }, - "../../net_drivers/webrtc/node_modules/@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "../../net_drivers/webrtc/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../../net_drivers/webrtc/node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "../../net_drivers/webrtc/node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "../../net_drivers/webrtc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../../net_drivers/webrtc/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "../../net_drivers/webrtc/node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "../../net_drivers/webrtc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "../../net_drivers/webrtc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "../../net_drivers/webrtc/node_modules/color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "../../net_drivers/webrtc/node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "engines": { - "node": ">=0.1.90" - } - }, - "../../net_drivers/webrtc/node_modules/colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "dependencies": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "../../net_drivers/webrtc/node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "../../net_drivers/webrtc/node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "../../net_drivers/webrtc/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "../../net_drivers/webrtc/node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "../../net_drivers/webrtc/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "../../net_drivers/webrtc/node_modules/domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "dependencies": { - "webidl-conversions": "^4.0.2" - } - }, - "../../net_drivers/webrtc/node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "../../net_drivers/webrtc/node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "../../net_drivers/webrtc/node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "../../net_drivers/webrtc/node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dependencies": { - "type": "^2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/ext/node_modules/type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - }, - "../../net_drivers/webrtc/node_modules/fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "../../net_drivers/webrtc/node_modules/fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "../../net_drivers/webrtc/node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "../../net_drivers/webrtc/node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dependencies": { - "minipass": "^2.6.0" - } - }, - "../../net_drivers/webrtc/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "../../net_drivers/webrtc/node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "../../net_drivers/webrtc/node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "../../net_drivers/webrtc/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "../../net_drivers/webrtc/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "../../net_drivers/webrtc/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "../../net_drivers/webrtc/node_modules/ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "engines": { - "node": "*" - } - }, - "../../net_drivers/webrtc/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "../../net_drivers/webrtc/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "engines": { - "node": ">=8" - } - }, - "../../net_drivers/webrtc/node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "../../net_drivers/webrtc/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "../../net_drivers/webrtc/node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "../../net_drivers/webrtc/node_modules/logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "dependencies": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - } - }, - "../../net_drivers/webrtc/node_modules/logform/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../../net_drivers/webrtc/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "../../net_drivers/webrtc/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "../../net_drivers/webrtc/node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dependencies": { - "minipass": "^2.9.0" - } - }, - "../../net_drivers/webrtc/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "../../net_drivers/webrtc/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "../../net_drivers/webrtc/node_modules/needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "dependencies": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" - }, - "engines": { - "node": ">= 4.4.x" - } - }, - "../../net_drivers/webrtc/node_modules/needle/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "../../net_drivers/webrtc/node_modules/needle/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../../net_drivers/webrtc/node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "../../net_drivers/webrtc/node_modules/node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "../../net_drivers/webrtc/node_modules/node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "dependencies": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "../../net_drivers/webrtc/node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "../../net_drivers/webrtc/node_modules/npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "../../net_drivers/webrtc/node_modules/npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "dependencies": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "../../net_drivers/webrtc/node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "../../net_drivers/webrtc/node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "../../net_drivers/webrtc/node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "../../net_drivers/webrtc/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "../../net_drivers/webrtc/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "../../net_drivers/webrtc/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "../../net_drivers/webrtc/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "../../net_drivers/webrtc/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "../../net_drivers/webrtc/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "../../net_drivers/webrtc/node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "../../net_drivers/webrtc/node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "../../net_drivers/webrtc/node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "../../net_drivers/webrtc/node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "engines": { - "node": "*" - } - }, - "../../net_drivers/webrtc/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "../../net_drivers/webrtc/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, - "../../net_drivers/webrtc/node_modules/tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dependencies": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "engines": { - "node": ">=4.5" - } - }, - "../../net_drivers/webrtc/node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "../../net_drivers/webrtc/node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "../../net_drivers/webrtc/node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "../../net_drivers/webrtc/node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../../net_drivers/webrtc/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "../../net_drivers/webrtc/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "../../net_drivers/webrtc/node_modules/websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "../../net_drivers/webrtc/node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "../../net_drivers/webrtc/node_modules/winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "dependencies": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - }, - "engines": { - "node": ">= 6.4.0" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "dependencies": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "engines": { - "node": ">= 6.4.0" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../../net_drivers/webrtc/node_modules/winston-transport/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../../net_drivers/webrtc/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "../../net_drivers/webrtc/node_modules/wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "dependencies": { - "domexception": "^1.0.1", - "node-pre-gyp": "^0.13.0" - }, - "engines": { - "node": "^8.11.2 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0" - }, - "optionalDependencies": { - "domexception": "^1.0.1" - } - }, - "../../net_drivers/webrtc/node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=", - "engines": { - "node": ">=0.10.32" - } - }, - "../../net_drivers/webrtc/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, "node_modules/nbnet": { - "resolved": "../../net_drivers/webrtc", + "resolved": "../../webrtc", "link": true } - }, - "dependencies": { - "nbnet": { - "version": "file:../../net_drivers/webrtc", - "requires": { - "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" - }, - "dependencies": { - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "requires": { - "domexception": "^1.0.1", - "node-pre-gyp": "^0.13.0" - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - } } } diff --git a/examples/raylib/package.json b/examples/raylib/package.json index f6e1142..03e752a 100644 --- a/examples/raylib/package.json +++ b/examples/raylib/package.json @@ -1,8 +1,8 @@ { "scripts": { - "server": "node --experimental-wasm-threads --experimental-wasm-bulk-memory raylib_server.js" + "server": "node raylib_server.js" }, "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" + "nbnet": "file:../../webrtc" } } diff --git a/examples/raylib/server.c b/examples/raylib/server.c index 66731f0..b24978e 100644 --- a/examples/raylib/server.c +++ b/examples/raylib/server.c @@ -20,15 +20,19 @@ */ +#include +#include +#include #include +#include // For Sleep function #if defined(__EMSCRIPTEN__) -#include +#include #elif defined(_WIN32) || defined(_WIN64) -#include +#include #include -#include +#include #else #include #endif @@ -36,10 +40,9 @@ #include "shared.h" // A simple structure to represent connected clients -typedef struct -{ - // Underlying nbnet connection handle, used to send messages to that particular client - NBN_ConnectionHandle client_handle; +typedef struct { + // Underlying nbnet connection, used to send messages to that particular client + NBN_ConnectionHandle *conn; // Client state ClientState state; @@ -52,67 +55,60 @@ static Client *clients[MAX_CLIENTS] = {NULL}; static unsigned int client_count = 0; // Spawn positions -static Vector2 spawns[] = { - (Vector2){50, 50}, - (Vector2){GAME_WIDTH - 100, 50}, - (Vector2){50, GAME_HEIGHT - 100}, - (Vector2){GAME_WIDTH - 100, GAME_HEIGHT - 100} -}; - -static void AcceptConnection(unsigned int x, unsigned int y, NBN_ConnectionHandle conn) -{ - NBN_WriteStream ws; - uint8_t data[32]; - - NBN_WriteStream_Init(&ws, data, sizeof(data)); - - NBN_SerializeUInt((NBN_Stream *)&ws, x, 0, GAME_WIDTH); - NBN_SerializeUInt((NBN_Stream *)&ws, y, 0, GAME_HEIGHT); - NBN_SerializeUInt((NBN_Stream *)&ws, conn, 0, UINT_MAX); - - // Accept the connection - NBN_GameServer_AcceptIncomingConnectionWithData(data, sizeof(data)); +static Vector2 spawns[] = {(Vector2){50, 50}, (Vector2){GAME_WIDTH - 100, 50}, (Vector2){50, GAME_HEIGHT - 100}, + (Vector2){GAME_WIDTH - 100, GAME_HEIGHT - 100}}; + +static void AcceptConnection(NBN_Server *server, Vector2 spawn, NBN_ConnectionHandle *conn) { + // Accept the connection with some data + // this data can be read by the client upon processing the connection event + NBN_Writer *writer = NBN_Server_WriteConnectionData(server); + + NBN_Writer_WriteUInt32(writer, (uint32_t)spawn.x); + NBN_Writer_WriteUInt32(writer, (uint32_t)spawn.y); + NBN_Writer_WriteUInt32(writer, conn->id); // TODO: id is 64bits + + NBN_Server_AcceptIncomingConnection(server); } -static int HandleNewConnection(void) -{ +static int HandleNewConnection(NBN_Server *server) { TraceLog(LOG_INFO, "New connection"); // If the server is full - if (client_count == MAX_CLIENTS) - { + if (client_count == MAX_CLIENTS) { // Reject the connection (send a SERVER_FULL_CODE code to the client) TraceLog(LOG_INFO, "Connection rejected"); - NBN_GameServer_RejectIncomingConnectionWithCode(SERVER_FULL_CODE); + NBN_Server_RejectIncomingConnectionWithCode(server, SERVER_FULL_CODE); return 0; } // Otherwise... - NBN_ConnectionHandle client_handle; + NBN_ConnectionHandle *conn = NBN_Server_GetIncomingConnection(server); - client_handle = NBN_GameServer_GetIncomingConnection(); + // Read the connection request data transmitted by the client + NBN_Reader *reader = NBN_Server_ReadConnectionRequestData(server); + char name[CLIENT_NAME_MAX_LEN]; + + if (NBN_Reader_ReadString(reader, name, sizeof(name)) < 0) { + TraceLog(LOG_ERROR, "Failed to read client name"); + abort(); + } // Get a spawning position for the client - Vector2 spawn = spawns[client_handle % MAX_CLIENTS]; + Vector2 spawn = spawns[conn->id % MAX_CLIENTS]; // Build some "initial" data that will be sent to the connected client - unsigned int x = (unsigned int)spawn.x; - unsigned int y = (unsigned int)spawn.y; + AcceptConnection(server, spawn, conn); - AcceptConnection(x, y, client_handle); - - TraceLog(LOG_INFO, "Connection accepted (ID: %d)", client_handle); + TraceLog(LOG_INFO, "Connection accepted (ID: %d, name: %s)", conn->id, name); Client *client = NULL; // Find a free slot in the clients array and create a new client - for (int i = 0; i < MAX_CLIENTS; i++) - { - if (clients[i] == NULL) - { + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i] == NULL) { client = malloc(sizeof(Client)); clients[i] = client; @@ -122,33 +118,21 @@ static int HandleNewConnection(void) assert(client != NULL); - client->client_handle = client_handle; // Store the nbnet connection ID + client->conn = conn; + conn->user_data = client; // Fill the client state with initial spawning data - client->state = (ClientState){.client_id = client_handle, .x = 200, .y = 400, .color = CLI_RED, .val = 0}; + client->state = (ClientState){.client_id = conn->id, .x = 200, .y = 400, .color = CLI_RED, .val = 0}; + memcpy(client->state.name, name, sizeof(client->state.name)); client_count++; return 0; } -static Client *FindClientById(uint32_t client_id) -{ - for (int i = 0; i < MAX_CLIENTS; i++) - { - if (clients[i] && clients[i]->state.client_id == client_id) - return clients[i]; - } - - return NULL; -} - -static void DestroyClient(Client *client) -{ - for (int i = 0; i < MAX_CLIENTS; i++) - { - if (clients[i] && clients[i]->state.client_id == client->state.client_id) - { +static void DestroyClient(Client *client) { + for (int i = 0; i < MAX_CLIENTS; i++) { + if (clients[i] && clients[i]->state.client_id == client->state.client_id) { clients[i] = NULL; return; @@ -158,129 +142,113 @@ static void DestroyClient(Client *client) free(client); } -static void HandleClientDisconnection() -{ - NBN_ConnectionHandle client_handle = NBN_GameServer_GetDisconnectedClient(); // Get the disconnected client +static void HandleClientDisconnection(NBN_Server *server) { + NBN_DisconnectionInfo info = NBN_Server_GetDisconnectionInfo(server); - TraceLog(LOG_INFO, "Client has disconnected (id: %d)", client_handle); + TraceLog(LOG_INFO, "Client has disconnected (id: %d, user data: %p)", info.conn_id, info.user_data); - Client *client = FindClientById(client_handle); + Client *client = info.user_data; assert(client); - DestroyClient(client); client_count--; } -static void HandleUpdateStateMessage(UpdateStateMessage *msg, Client *sender) -{ - // Update the state of the client with the data from the received UpdateStateMessage message - sender->state.x = msg->x; - sender->state.y = msg->y; - sender->state.val = msg->val; +static int HandleUpdateStateMessage(NBN_Server *server, Client *sender) { + // Update the state of the client with the data from the received UPDATE_STATE_MESSAGE message + NBN_Reader *reader = NBN_Server_ReadMessage(server); - UpdateStateMessage_Destroy(msg); + return UpdateClientStateMessage_Read(reader, &sender->state); } -static void HandleChangeColorMessage(ChangeColorMessage *msg, Client *sender) -{ +static int HandleChangeColorMessage(NBN_Server *server, Client *sender) { // Update the client color - sender->state.color = msg->color; + NBN_Reader *reader = NBN_Server_ReadMessage(server); - ChangeColorMessage_Destroy(msg); + return ChangeColorMessage_Read(reader, &sender->state.color); } -static void HandleReceivedMessage(void) -{ +static int HandleReceivedMessage(NBN_Server *server) { // Fetch info about the last received message - NBN_MessageInfo msg_info = NBN_GameServer_GetMessageInfo(); - - // Find the client that sent the message - Client *sender = FindClientById(msg_info.sender); - + NBN_MessageInfo msg_info = NBN_Server_GetMessageInfo(server); + assert(msg_info.sender != NULL); + Client *sender = msg_info.sender->user_data; assert(sender != NULL); - switch (msg_info.type) - { - case UPDATE_STATE_MESSAGE: - // The server received a client state update - HandleUpdateStateMessage(msg_info.data, sender); - break; + switch (msg_info.type) { + case UPDATE_STATE_MESSAGE: + // The server received a client state update + return HandleUpdateStateMessage(server, sender); - case CHANGE_COLOR_MESSAGE: - // The server received a client switch color action - HandleChangeColorMessage(msg_info.data, sender); - break; + case CHANGE_COLOR_MESSAGE: + // The server received a client switch color action + return HandleChangeColorMessage(server, sender); } -} - -static int HandleGameServerEvent(int ev) -{ - switch (ev) - { - case NBN_NEW_CONNECTION: - // A new client has requested a connection - if (HandleNewConnection() < 0) - return -1; - break; - case NBN_CLIENT_DISCONNECTED: - // A previously connected client has disconnected - HandleClientDisconnection(); - break; + // Received an unexpected message + return -1; +} - case NBN_CLIENT_MESSAGE_RECEIVED: - // A message from a client has been received - HandleReceivedMessage(); - break; +static int HandleGameServerEvent(NBN_Server *server, int ev) { + switch (ev) { + case NBN_SERVER_NEW_CONNECTION: + // A new client has requested a connection + if (HandleNewConnection(server) < 0) + return -1; + break; + + case NBN_SERVER_DISCONNECTION: + // A previously connected client has disconnected + HandleClientDisconnection(server); + break; + + case NBN_SERVER_MESSAGE_RECEIVED: + // A message from a client has been received + if (HandleReceivedMessage(server) < 0) { + // TODO: kick client + return -1; + } + break; } return 0; } // Broadcasts the latest game state to all connected clients -static int BroadcastGameState(void) -{ - ClientState client_states[MAX_CLIENTS]; +static int BroadcastGameState(NBN_Server *server) { + static GameState game_state; unsigned int client_index = 0; - // Loop over the clients and build an array of ClientState - for (int i = 0; i < MAX_CLIENTS; i++) - { + // Loop over the clients and build the game state + for (int i = 0; i < MAX_CLIENTS; i++) { Client *client = clients[i]; - if (client == NULL) continue; + if (client == NULL) + continue; + + ClientState state = (ClientState){.client_id = client->state.client_id, + .x = client->state.x, + .y = client->state.y, + .val = client->state.val, + .color = client->state.color}; - client_states[client_index] = (ClientState) { - .client_id = client->state.client_id, - .x = client->state.x, - .y = client->state.y, - .val = client->state.val, - .color = client->state.color - }; + memcpy(state.name, client->state.name, sizeof(client->state.name)); + game_state.client_states[client_index] = state; client_index++; } assert(client_index == client_count); - // Broadcast the game state to all clients - for (int i = 0; i < MAX_CLIENTS; i++) - { - Client *client = clients[i]; - - if (client == NULL) continue; - - GameStateMessage *msg = GameStateMessage_Create(); - - // Fill message data - msg->client_count = client_count; - memcpy(msg->client_states, client_states, sizeof(ClientState) * client_count); + game_state.client_count = client_count; - // Unreliably send the message to all connected clients - NBN_GameServer_SendUnreliableMessageTo(client->client_handle, GAME_STATE_MESSAGE, msg); + NBN_Client_Iterator it = 0; + NBN_ConnectionHandle *cli; - // TraceLog(LOG_DEBUG, "Sent game state to client %d (%d, %d)", client->client_id, client_count, client_index); + // Broadcast GAME_STATE_MESSAGE to all clients + while ((cli = NBN_Server_GetNextClient(server, &it)) != NULL) { + NBN_Writer *writer = NBN_Server_CreateUnreliableMessage(server, GAME_STATE_MESSAGE, cli); + GameStateMessage_Write(writer, &game_state); } return 0; @@ -291,22 +259,17 @@ static bool running = true; #ifndef __EMSCRIPTEN__ #include -static void SigintHandler(int dummy) -{ - running = false; -} +static void SigintHandler(int dummy) { running = false; } #endif -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { #ifndef __EMSCRIPTEN__ signal(SIGINT, SigintHandler); #endif // Read command line arguments - if (ReadCommandLine(argc, argv)) - { + if (ReadCommandLine(argc, argv)) { printf("Usage: server [--packet_loss=] [--packet_duplication=] [--ping=] \ [--jitter=]\n"); @@ -314,83 +277,56 @@ int main(int argc, char *argv[]) } // Even though we do not display anything we still use raylib logging capacibilities - SetTraceLogLevel(LOG_DEBUG); - -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver + SetTraceLogLevel(LOG_TRACE); -#endif // __EMSCRIPTEN__ + // Initialize the server with a protocol name and a port + // protocol name has to match between the server and the clients + NBN_Server *server = NBN_Server_Create(RAYLIB_EXAMPLE_PROTOCOL_NAME, RAYLIB_EXAMPLE_PORT); - // Start the server with a protocol name and a port - if (NBN_GameServer_StartEx(RAYLIB_EXAMPLE_PROTOCOL_NAME, RAYLIB_EXAMPLE_PORT) < 0) - { + // Start the server with the configuration + if (NBN_Server_Start(server) < 0) { TraceLog(LOG_ERROR, "Game server failed to start. Exit"); return 1; } - // Register messages, have to be done after NBN_GameServer_StartEx - NBN_GameServer_RegisterMessage( - CHANGE_COLOR_MESSAGE, - (NBN_MessageBuilder)ChangeColorMessage_Create, - (NBN_MessageDestructor)ChangeColorMessage_Destroy, - (NBN_MessageSerializer)ChangeColorMessage_Serialize); - NBN_GameServer_RegisterMessage( - UPDATE_STATE_MESSAGE, - (NBN_MessageBuilder)UpdateStateMessage_Create, - (NBN_MessageDestructor)UpdateStateMessage_Destroy, - (NBN_MessageSerializer)UpdateStateMessage_Serialize); - NBN_GameServer_RegisterMessage( - GAME_STATE_MESSAGE, - (NBN_MessageBuilder)GameStateMessage_Create, - (NBN_MessageDestructor)GameStateMessage_Destroy, - (NBN_MessageSerializer)GameStateMessage_Serialize); - // Network conditions simulated variables (read from the command line, default is always 0) - NBN_GameServer_SetPing(GetOptions().ping); - NBN_GameServer_SetJitter(GetOptions().jitter); - NBN_GameServer_SetPacketLoss(GetOptions().packet_loss); - NBN_GameServer_SetPacketDuplication(GetOptions().packet_duplication); + NBN_Server_SetPing(server, GetOptions().ping); + NBN_Server_SetJitter(server, GetOptions().jitter); + NBN_Server_SetPacketLoss(server, GetOptions().packet_loss); + NBN_Server_SetPacketDuplication(server, GetOptions().packet_duplication); float tick_dt = 1.f / TICK_RATE; // Tick delta time - while (running) - { + while (running) { int ev; // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { + while ((ev = NBN_Server_Poll(server)) != NBN_SERVER_NO_EVENT) { + if (ev < 0) { TraceLog(LOG_ERROR, "An occured while polling network events. Exit"); break; } - if (HandleGameServerEvent(ev) < 0) + if (HandleGameServerEvent(server, ev) < 0) break; } - // Broadcast latest game state - if (BroadcastGameState() < 0) - { + if (BroadcastGameState(server) < 0) { TraceLog(LOG_ERROR, "An occured while broadcasting game states. Exit"); break; } // Pack all enqueued messages as packets and send them - if (NBN_GameServer_SendPackets() < 0) - { + if (NBN_Server_Flush(server) < 0) { TraceLog(LOG_ERROR, "An occured while flushing the send queue. Exit"); break; } - NBN_GameServerStats stats = NBN_GameServer_GetStats(); + NBN_ServerStats stats = NBN_Server_GetStats(server); TraceLog(LOG_TRACE, "Upload: %f Bps | Download: %f Bps", stats.upload_bandwidth, stats.download_bandwidth); @@ -408,7 +344,7 @@ int main(int argc, char *argv[]) } // Stop the server - NBN_GameServer_Stop(); + NBN_Server_Stop(server); return 0; } diff --git a/examples/raylib/shared.c b/examples/raylib/shared.c index 26fb281..3135cd7 100644 --- a/examples/raylib/shared.c +++ b/examples/raylib/shared.c @@ -20,126 +20,138 @@ */ +#include #include #include #include // nbnet implementation -#define NBNET_IMPL +#define NBNET_IMPL #include "shared.h" // Command line options -enum -{ - OPT_MESSAGES_COUNT, - OPT_PACKET_LOSS, - OPT_PACKET_DUPLICATION, - OPT_PING, - OPT_JITTER -}; +enum { OPT_MESSAGES_COUNT, OPT_PACKET_LOSS, OPT_PACKET_DUPLICATION, OPT_PING, OPT_JITTER }; static Options options = {0}; -ChangeColorMessage *ChangeColorMessage_Create(void) -{ - return malloc(sizeof(ChangeColorMessage)); +void ChangeColorMessage_Write(NBN_Writer *writer, ClientColor color) { + NBN_Writer_WriteUInt32(writer, (uint32_t)color); } -void ChangeColorMessage_Destroy(ChangeColorMessage *msg) -{ - free(msg); +int ChangeColorMessage_Read(NBN_Reader *reader, ClientColor *color) { + return NBN_Reader_ReadUInt32(reader, (uint32_t *)color); } -int ChangeColorMessage_Serialize(ChangeColorMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->color, 0, MAX_COLORS - 1); - - return 0; +void UpdateClientStateMessage_Write(NBN_Writer *writer, ClientState state) { + NBN_Writer_WriteInt32(writer, state.x); + NBN_Writer_WriteInt32(writer, state.y); + NBN_Writer_WriteFloat(writer, state.val); } -UpdateStateMessage *UpdateStateMessage_Create(void) -{ - return malloc(sizeof(UpdateStateMessage)); -} +int UpdateClientStateMessage_Read(NBN_Reader *reader, ClientState *state) { + if (NBN_Reader_ReadInt32(reader, &state->x) < 0) { + return -1; + } -void UpdateStateMessage_Destroy(UpdateStateMessage *msg) -{ - free(msg); -} + if (NBN_Reader_ReadInt32(reader, &state->y) < 0) { + return -1; + } -int UpdateStateMessage_Serialize(UpdateStateMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->x, 0, GAME_WIDTH); - NBN_SerializeUInt(stream, msg->y, 0, GAME_HEIGHT); - NBN_SerializeFloat(stream, msg->val, MIN_FLOAT_VAL, MAX_FLOAT_VAL, 3); + if (NBN_Reader_ReadFloat(reader, &state->val) < 0) { + return -1; + } return 0; } -GameStateMessage *GameStateMessage_Create(void) -{ - return malloc(sizeof(GameStateMessage)); -} +void GameStateMessage_Write(NBN_Writer *writer, GameState *state) { + NBN_Writer_WriteUInt32(writer, state->client_count); + + for (unsigned int i = 0; i < state->client_count; i++) { + ClientState cli_state = state->client_states[i]; -void GameStateMessage_Destroy(GameStateMessage *msg) -{ - free(msg); + NBN_Writer_WriteUInt32(writer, cli_state.client_id); + NBN_Writer_WriteUInt32(writer, (uint32_t)cli_state.color); + NBN_Writer_WriteInt32(writer, cli_state.x); + NBN_Writer_WriteInt32(writer, cli_state.y); + NBN_Writer_WriteFloat(writer, cli_state.val); + NBN_Writer_WriteString(writer, cli_state.name, CLIENT_NAME_MAX_LEN); + } } -int GameStateMessage_Serialize(GameStateMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->client_count, 0, MAX_CLIENTS); - - for (unsigned int i = 0; i < msg->client_count; i++) - { - NBN_SerializeUInt(stream, msg->client_states[i].client_id, 0, UINT_MAX); - NBN_SerializeUInt(stream, msg->client_states[i].color, 0, MAX_COLORS - 1); - NBN_SerializeUInt(stream, msg->client_states[i].x, 0, GAME_WIDTH); - NBN_SerializeUInt(stream, msg->client_states[i].y, 0, GAME_HEIGHT); - NBN_SerializeFloat(stream, msg->client_states[i].val, MIN_FLOAT_VAL, MAX_FLOAT_VAL, 3); +int GameStateMessage_Read(NBN_Reader *reader, GameState *state) { + if (NBN_Reader_ReadUInt32(reader, &state->client_count) < 0) { + return -1; + } + + if (state->client_count > MAX_CLIENTS) { + return -1; + } + + for (unsigned int i = 0; i < state->client_count; i++) { + ClientState *cli_state = &state->client_states[i]; + + if (NBN_Reader_ReadUInt32(reader, &cli_state->client_id) < 0) { + return -1; + } + + if (NBN_Reader_ReadUInt32(reader, (uint32_t *)&cli_state->color) < 0) { + return -1; + } + + if (NBN_Reader_ReadInt32(reader, &cli_state->x) < 0) { + return -1; + } + + if (NBN_Reader_ReadInt32(reader, &cli_state->y) < 0) { + return -1; + } + + if (NBN_Reader_ReadFloat(reader, &cli_state->val) < 0) { + return -1; + } + + if (NBN_Reader_ReadString(reader, cli_state->name, CLIENT_NAME_MAX_LEN) < 0) { + return -1; + } } return 0; } // Parse the command line -int ReadCommandLine(int argc, char *argv[]) -{ +int ReadCommandLine(int argc, char *argv[]) { int opt; int option_index; - struct option long_options[] = { - { "packet_loss", required_argument, NULL, OPT_PACKET_LOSS }, - { "packet_duplication", required_argument, NULL, OPT_PACKET_DUPLICATION }, - { "ping", required_argument, NULL, OPT_PING }, - { "jitter", required_argument, NULL, OPT_JITTER } - }; - - while ((opt = getopt_long(argc, argv, "", long_options, &option_index)) != -1) - { - switch (opt) - { - case OPT_PACKET_LOSS: - options.packet_loss = atof(optarg); - break; - - case OPT_PACKET_DUPLICATION: - options.packet_duplication = atof(optarg); - break; - - case OPT_PING: - options.ping = atof(optarg); - break; - - case OPT_JITTER: - options.jitter = atof(optarg); - break; - - case '?': - return -1; - - default: - return -1; + struct option long_options[] = {{"packet_loss", required_argument, NULL, OPT_PACKET_LOSS}, + {"packet_duplication", required_argument, NULL, OPT_PACKET_DUPLICATION}, + {"ping", required_argument, NULL, OPT_PING}, + {"jitter", required_argument, NULL, OPT_JITTER}}; + + while ((opt = getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + switch (opt) { + case OPT_PACKET_LOSS: + options.packet_loss = atof(optarg); + break; + + case OPT_PACKET_DUPLICATION: + options.packet_duplication = atof(optarg); + break; + + case OPT_PING: + options.ping = atof(optarg); + break; + + case OPT_JITTER: + options.jitter = atof(optarg); + break; + + case '?': + return -1; + + default: + return -1; } } @@ -147,7 +159,4 @@ int ReadCommandLine(int argc, char *argv[]) } // Return the command line options -Options GetOptions(void) -{ - return options; -} +Options GetOptions(void) { return options; } diff --git a/examples/raylib/shared.h b/examples/raylib/shared.h index a8f3617..b877f36 100644 --- a/examples/raylib/shared.h +++ b/examples/raylib/shared.h @@ -54,24 +54,24 @@ #define NOKERNEL // All KERNEL defines and routines #define NOUSER // All USER defines and routines /*#define NONLS // All NLS defines and routines*/ -#define NOMB // MB_* and MessageBox() -#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines -#define NOMETAFILE // typedef METAFILEPICT -#define NOMINMAX // Macros min(a,b) and max(a,b) -#define NOMSG // typedef MSG and associated routines -#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* -#define NOSCROLL // SB_* and scrolling routines -#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. -#define NOSOUND // Sound driver routines -#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines -#define NOWH // SetWindowsHook and WH_* -#define NOWINOFFSETS // GWL_*, GCL_*, associated routines -#define NOCOMM // COMM driver routines -#define NOKANJI // Kanji support stuff. -#define NOHELP // Help engine interface. -#define NOPROFILER // Profiler interface. -#define NODEFERWINDOWPOS // DeferWindowPos routines -#define NOMCX // Modem Configuration Extensions +#define NOMB // MB_* and MessageBox() +#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines +#define NOMETAFILE // typedef METAFILEPICT +#define NOMINMAX // Macros min(a,b) and max(a,b) +#define NOMSG // typedef MSG and associated routines +#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* +#define NOSCROLL // SB_* and scrolling routines +#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. +#define NOSOUND // Sound driver routines +#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines +#define NOWH // SetWindowsHook and WH_* +#define NOWINOFFSETS // GWL_*, GCL_*, associated routines +#define NOCOMM // COMM driver routines +#define NOKANJI // Kanji support stuff. +#define NOHELP // Help engine interface. +#define NOPROFILER // Profiler interface. +#define NODEFERWINDOWPOS // DeferWindowPos routines +#define NOMCX // Modem Configuration Extensions // Type required before windows.h inclusion typedef struct tagMSG *LPMSG; @@ -95,25 +95,16 @@ typedef struct tagMSG *LPMSG; #include "../../nbnet.h" -#ifdef __EMSCRIPTEN__ -#include "../../net_drivers/webrtc.h" -#else -#include "../../net_drivers/udp.h" - -#ifdef SOAK_WEBRTC_C_DRIVER -#include "../../net_drivers/webrtc_c.h" -#endif - -#endif // __EMSCRIPTEN__ - #define TICK_RATE 60 // Simulation tick rate // Window size, used to display window but also to cap the serialized position values within messages #define GAME_WIDTH 800 #define GAME_HEIGHT 600 +#define CLIENT_NAME_MAX_LEN 16 // Client name maximum length + #define MIN_FLOAT_VAL -5 // Minimum value of networked client float value -#define MAX_FLOAT_VAL 5 // Maximum value of networked client float value +#define MAX_FLOAT_VAL 5 // Maximum value of networked client float value // Maximum number of connected clients at a time #define MAX_CLIENTS 4 @@ -124,78 +115,47 @@ typedef struct tagMSG *LPMSG; // A code passed by the server when closing a client connection due to being full (max client count reached) #define SERVER_FULL_CODE 42 -// Message ids -enum -{ - CHANGE_COLOR_MESSAGE, - UPDATE_STATE_MESSAGE, - GAME_STATE_MESSAGE -}; - -// Messages +#define CHANGE_COLOR_MESSAGE_MAX_LENGTH 4 +#define UPDATE_STATE_MESSAGE_MAX_LENGTH 20 // ClientState is 20 bytes +#define GAME_STATE_MESSAGE_MAX_LENGTH ((20 * MAX_CLIENTS) + 4) -typedef struct -{ - int x; - int y; - float val; -} UpdateStateMessage; +// Message ids +enum { CHANGE_COLOR_MESSAGE, UPDATE_STATE_MESSAGE, GAME_STATE_MESSAGE }; // Client colors used for ChangeColorMessage and GameStateMessage messages -typedef enum -{ - CLI_RED, - CLI_GREEN, - CLI_BLUE, - CLI_YELLOW, - CLI_ORANGE, - CLI_PURPLE, - CLI_PINK -} ClientColor; - -typedef struct -{ - ClientColor color; -} ChangeColorMessage; +typedef enum { CLI_RED, CLI_GREEN, CLI_BLUE, CLI_YELLOW, CLI_ORANGE, CLI_PURPLE, CLI_PINK } ClientColor; // Client state, represents a client over the network -typedef struct -{ +typedef struct { uint32_t client_id; int x; int y; float val; ClientColor color; + char name[CLIENT_NAME_MAX_LEN]; } ClientState; -typedef struct -{ +// Represents the state of all clients +typedef struct { unsigned int client_count; ClientState client_states[MAX_CLIENTS]; -} GameStateMessage; +} GameState; // Store all options from the command line -typedef struct -{ +typedef struct { float packet_loss; float packet_duplication; float ping; float jitter; } Options; -ChangeColorMessage *ChangeColorMessage_Create(void); -void ChangeColorMessage_Destroy(ChangeColorMessage *); -int ChangeColorMessage_Serialize(ChangeColorMessage *msg, NBN_Stream *); - -UpdateStateMessage *UpdateStateMessage_Create(void); -void UpdateStateMessage_Destroy(UpdateStateMessage *); -int UpdateStateMessage_Serialize(UpdateStateMessage *, NBN_Stream *); - -GameStateMessage *GameStateMessage_Create(void); -void GameStateMessage_Destroy(GameStateMessage *); -int GameStateMessage_Serialize(GameStateMessage *, NBN_Stream *); - int ReadCommandLine(int, char *[]); Options GetOptions(void); +void ChangeColorMessage_Write(NBN_Writer *writer, ClientColor color); +int ChangeColorMessage_Read(NBN_Reader *reader, ClientColor *color); +void UpdateClientStateMessage_Write(NBN_Writer *writer, ClientState state); +int UpdateClientStateMessage_Read(NBN_Reader *reader, ClientState *state); +void GameStateMessage_Write(NBN_Writer *writer, GameState *state); +int GameStateMessage_Read(NBN_Reader *reader, GameState *state); #endif /* RAYLIB_EXAMPLE_SHARED_H */ diff --git a/examples/rpc/CMakeLists.txt b/examples/rpc/CMakeLists.txt deleted file mode 100644 index c6bf564..0000000 --- a/examples/rpc/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(rpc) - -option(CPP_COMPILE OFF) - -# allow to compile as cpp -if (CPP_COMPILE) - file(GLOB_RECURSE CFILES "${CMAKE_SOURCE_DIR}/*.c") - SET_SOURCE_FILES_PROPERTIES(${CFILES} PROPERTIES LANGUAGE CXX) - set (CMAKE_CXX_STANDARD 20) -endif (CPP_COMPILE) - -unset(CPP_COMPILE CACHE) - -add_compile_options(-Wall) - -if(CMAKE_COMPILER_IS_GNUCXX) - add_compile_options(-Wextra -Wpedantic) -endif (CMAKE_COMPILER_IS_GNUCXX) - -add_executable(rpc_client client.c shared.c) -add_executable(rpc_server server.c shared.c) - -target_compile_definitions(rpc_client PUBLIC NBN_DEBUG) -target_compile_definitions(rpc_server PUBLIC NBN_DEBUG) - -option(WEBRTC_DRIVER_C OFF) - -if(WIN32) - target_link_libraries(rpc_client wsock32 ws2_32) - target_link_libraries(rpc_server wsock32 ws2_32) -else() - # link with pthread when we are not on windows - target_link_libraries(rpc_client pthread) - target_link_libraries(rpc_server pthread) -endif(WIN32) - -if (UNIX) - # link with libm on unix - target_link_libraries(rpc_client m) - target_link_libraries(rpc_server m) -endif (UNIX) - -if (EMSCRIPTEN) - set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") - - set_target_properties(rpc_server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s TOTAL_MEMORY=30MB \ - -s EXIT_RUNTIME=1 \ - -s ASSERTIONS=1 \ - -s ASYNCIFY \ - -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - - set_target_properties(rpc_client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../../net_drivers/webrtc/js/api.js \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s TOTAL_MEMORY=30MB \ - -s EXIT_RUNTIME=1 \ - -s ASSERTIONS=1 \ - -s ASYNCIFY \ - -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") -endif() diff --git a/examples/rpc/README.md b/examples/rpc/README.md deleted file mode 100644 index e6d7dd6..0000000 --- a/examples/rpc/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# RPC - -Simple example demonstrating how to use RPCs. - -Use the CMake script to compile the example: - -``` -cmake . -make -``` - -To run the server simply do: - -`./rpc_server` - -and to run the client: - -`./rpc_client` - -## WebRTC - -Use the CMake script to compile the example: - -``` -EMSCRIPTEN=1 cmake . -make -``` - -To run this example you need to have nodejs installed (see the package.json file). - -To run the server simply do: - -`npm run server` - -and to run the client: - -`npm run client` diff --git a/examples/rpc/client.c b/examples/rpc/client.c deleted file mode 100644 index afd765d..0000000 --- a/examples/rpc/client.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL - -#include "shared.h" - -static bool running = true; -static bool connected = false; -static bool disconnected = false; - -static void TestRPC2(unsigned int param_count, NBN_RPC_Param params[NBN_RPC_MAX_PARAM_COUNT], NBN_ConnectionHandle sender) -{ - (void) sender; - TEST_VERIFY(param_count == 2); - Log(LOG_INFO, "TestRPC2 called !"); - Log(LOG_INFO, "Parameter 1 (float): %f", NBN_RPC_GetFloat(params, 0)); - Log(LOG_INFO, "Parameter 2 (string): %s", NBN_RPC_GetString(params, 1)); -} - -void OnConnected(void) -{ - Log(LOG_INFO, "Connected"); - - connected = true; - - int ret = NBN_GameClient_CallRPC(TEST_RPC_ID, 4242, -1234.5678f, true); - - TEST_VERIFY(ret == 0); -} - -void OnDisconnected(void) -{ - Log(LOG_INFO, "Disconnected"); - - // Stop the main loop - disconnected = true; - running = false; -} - -int main(int argc, char *argv[]) -{ - (void) argc; - (void) argv; -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ - - // Initialize the client with a protocol name (must be the same than the one used by the server), the server ip address and port - - if (NBN_GameClient_StartEx(RPC_PROTOCOL_NAME, "127.0.0.1", RPC_EXAMPLE_PORT, NULL, 0) < 0) - { - Log(LOG_ERROR, "Failed to start client"); - -// Error, quit the client application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - int ret = NBN_GameClient_RegisterRPC(TEST_RPC_ID, TEST_RPC_SIGNATURE, NULL); - - TEST_VERIFY(ret == 0); - - ret = NBN_GameClient_RegisterRPC(TEST_RPC_2_ID, TEST_RPC_2_SIGNATURE, TestRPC2); - - TEST_VERIFY(ret == 0); - - // Number of seconds between client ticks - double dt = 1.0 / RPC_TICK_RATE; - - while (running) - { - int ev; - - // Poll for client events - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "An error occured while polling client events. Exit"); - - // Stop main loop - running = false; - break; - } - - switch (ev) - { - // Client is connected to the server - case NBN_CONNECTED: - OnConnected(); - break; - - // Client has disconnected from the server - case NBN_DISCONNECTED: - OnDisconnected(); - break; - } - } - - if (disconnected) - break; - - // Pack all enqueued messages as packets and send them - if (NBN_GameClient_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets. Exit"); - - // Stop main loop - running = false; - break; - } - - // Cap the client tick rate - ExampleSleep(dt); - } - - // Stop the client - NBN_GameClient_Stop(); - -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(0); -#else - return 0; -#endif -} diff --git a/examples/rpc/package-lock.json b/examples/rpc/package-lock.json deleted file mode 100644 index 4e06a55..0000000 --- a/examples/rpc/package-lock.json +++ /dev/null @@ -1,865 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "nbnet": { - "version": "file:../../net_drivers/webrtc", - "requires": { - "websocket": "^1.0.31", - "winston": "^3.2.1", - "wrtc": "^0.4.2" - }, - "dependencies": { - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "optional": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "optional": true - }, - "websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "wrtc": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.6.tgz", - "integrity": "sha512-4uD+oFoY2yuo3AV9fum3cXUXR6v8YQHZlqBrKkCRGjW1BvKrVHtLNH4UaNLBLiJu9DL89WqUWmbzsQ9RxMzANw==", - "requires": { - "domexception": "^1.0.1", - "node-pre-gyp": "^0.13.0" - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - } - } -} diff --git a/examples/rpc/package.json b/examples/rpc/package.json deleted file mode 100644 index fe7f3c6..0000000 --- a/examples/rpc/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "scripts": { - "server": "node rpc_server.js", - "client": "node rpc_client.js" - }, - "dependencies": { - "nbnet": "file:../../net_drivers/webrtc" - } -} diff --git a/examples/rpc/server.c b/examples/rpc/server.c deleted file mode 100644 index 1fee8cc..0000000 --- a/examples/rpc/server.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Has to be defined in exactly *one* source file before including the nbnet header -#define NBNET_IMPL - -#include "shared.h" - -static bool error = false; - -static void TestRPC(unsigned int param_count, NBN_RPC_Param params[NBN_RPC_MAX_PARAM_COUNT], NBN_ConnectionHandle sender) -{ - TEST_VERIFY(param_count == 3); - Log(LOG_INFO, "TestRPC called ! (Sender: %d)", sender); - Log(LOG_INFO, "Parameter 1 (int): %d", NBN_RPC_GetInt(params, 0)); - Log(LOG_INFO, "Parameter 2 (float): %f", NBN_RPC_GetFloat(params, 1)); - Log(LOG_INFO, "Parameter 3 (bool): %d", NBN_RPC_GetBool(params, 2)); - - NBN_GameServer_CallRPC( - TEST_RPC_2_ID, - sender, - NBN_RPC_GetInt(params, 0) * NBN_RPC_GetFloat(params, 1), - "Some test string"); -} - -int main(void) -{ -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register(); // Register the WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ - - if (NBN_GameServer_StartEx(RPC_PROTOCOL_NAME, RPC_EXAMPLE_PORT) < 0) - { - Log(LOG_ERROR, "Failed to start the server"); - - // Error, quit the server application -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(1); -#else - return 1; -#endif - } - - int ret = NBN_GameServer_RegisterRPC(TEST_RPC_ID, TEST_RPC_SIGNATURE, TestRPC); - - assert(ret == 0); - - ret = NBN_GameServer_RegisterRPC(TEST_RPC_2_ID, TEST_RPC_2_SIGNATURE, NULL); - - assert(ret == 0); - - // Number of seconds between server ticks - double dt = 1.0 / RPC_TICK_RATE; - - while (true) - { - int ev; - - // Poll for server events - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) - { - Log(LOG_ERROR, "Something went wrong"); - - // Error, quit the server application - error = true; - break; - } - - switch (ev) - { - // New connection request... - case NBN_NEW_CONNECTION: - NBN_GameServer_AcceptIncomingConnection(); - - break; - - // A client has disconnected - case NBN_CLIENT_DISCONNECTED: - break; - } - } - - // Pack all enqueued messages as packets and send them - if (NBN_GameServer_SendPackets() < 0) - { - Log(LOG_ERROR, "Failed to send packets"); - - // Error, quit the server application - error = true; - break; - } - - // Cap the server tick rate - ExampleSleep(dt); - } - - // Stop the server - NBN_GameServer_Stop(); - - ret = error ? 1 : 0; - -#ifdef __EMSCRIPTEN__ - emscripten_force_exit(ret); -#else - return ret; -#endif -} diff --git a/examples/rpc/shared.c b/examples/rpc/shared.c deleted file mode 100644 index 88becd9..0000000 --- a/examples/rpc/shared.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include -#include -#include - -// Sleep function -#if defined(__EMSCRIPTEN__) -#include -#elif defined(_WIN32) || defined(_WIN64) -#include -#include -#include -#else -#include -#endif - -#include "shared.h" - -// Sleep for a given amount of seconds -// Used to limit client and server tick rate -void ExampleSleep(double sec) -{ -#if defined(__EMSCRIPTEN__) - emscripten_sleep(sec * 1000); -#elif defined(_WIN32) || defined(_WIN64) - Sleep(sec * 1000); -#else /* UNIX / OSX */ - long nanos = sec * 1e9; - struct timespec t = {.tv_sec = nanos / 999999999, .tv_nsec = nanos % 999999999}; - - nanosleep(&t, &t); -#endif -} - -static const char *log_type_strings[] = { - "INFO", - "ERROR", - "DEBUG", - "TRACE" -}; - -// Basic logging function -void Log(int type, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - - printf("[%s] ", log_type_strings[type]); - vprintf(fmt, args); - printf("\n"); - - va_end(args); -} diff --git a/examples/rpc/shared.h b/examples/rpc/shared.h deleted file mode 100644 index 48c03ef..0000000 --- a/examples/rpc/shared.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#ifndef RPC_EXAMPLE_SHARED_H -#define RPC_EXAMPLE_SHARED_H - -#include - -#if defined(_WIN32) || defined(_WIN64) - -/* - * The following defines are meant to avoid conflicts between raylib and windows.h - * https://github.com/raysan5/raylib/issues/857 - */ - -// If defined, the following flags inhibit definition of the indicated items -#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ -#define NOVIRTUALKEYCODES // VK_* -#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_* -#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_* -#define NOSYSMETRICS // SM_* -#define NOMENUS // MF_* -#define NOICONS // IDI_* -#define NOKEYSTATES // MK_* -#define NOSYSCOMMANDS // SC_* -#define NORASTEROPS // Binary and Tertiary raster ops -#define NOSHOWWINDOW // SW_* -#define OEMRESOURCE // OEM Resource values -#define NOATOM // Atom Manager routines -#define NOCLIPBOARD // Clipboard routines -#define NOCOLOR // Screen colors -#define NOCTLMGR // Control and Dialog routines -#define NODRAWTEXT // DrawText() and DT_* -#define NOGDI // All GDI defines and routines -#define NOKERNEL // All KERNEL defines and routines -#define NOUSER // All USER defines and routines -/*#define NONLS // All NLS defines and routines*/ -#define NOMB // MB_* and MessageBox() -#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines -#define NOMETAFILE // typedef METAFILEPICT -#define NOMINMAX // Macros min(a,b) and max(a,b) -#define NOMSG // typedef MSG and associated routines -#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* -#define NOSCROLL // SB_* and scrolling routines -#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. -#define NOSOUND // Sound driver routines -#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines -#define NOWH // SetWindowsHook and WH_* -#define NOWINOFFSETS // GWL_*, GCL_*, associated routines -#define NOCOMM // COMM driver routines -#define NOKANJI // Kanji support stuff. -#define NOHELP // Help engine interface. -#define NOPROFILER // Profiler interface. -#define NODEFERWINDOWPOS // DeferWindowPos routines -#define NOMCX // Modem Configuration Extensions - -// Type required before windows.h inclusion -typedef struct tagMSG *LPMSG; - -#include // Has to be included before windows.h -#include - -#endif // WINDOWS - -#define RPC_PROTOCOL_NAME "rpc-example" -#define RPC_EXAMPLE_PORT 42042 -#define RPC_TICK_RATE 30 -#define TEST_RPC_ID 0 -#define TEST_RPC_2_ID 1 -#define TEST_RPC_SIGNATURE NBN_RPC_BuildSignature(3, NBN_RPC_PARAM_INT, NBN_RPC_PARAM_FLOAT, NBN_RPC_PARAM_BOOL) -#define TEST_RPC_2_SIGNATURE NBN_RPC_BuildSignature(2, NBN_RPC_PARAM_FLOAT, NBN_RPC_PARAM_STRING) -#define TEST_VERIFY(exp) if(!(exp)) abort() - -// nbnet logging -// nbnet does not implement any logging capabilities, you need to provide your own -enum -{ - LOG_INFO, - LOG_ERROR, - LOG_DEBUG, - LOG_TRACE, - LOG_WARNING -}; - -#define NBN_LogInfo(...) Log(LOG_INFO, __VA_ARGS__) -#define NBN_LogError(...) Log(LOG_ERROR, __VA_ARGS__) -#define NBN_LogDebug(...) Log(LOG_DEBUG, __VA_ARGS__) -#define NBN_LogWarning(...) Log(LOG_WARNING, __VA_ARGS__) -#define NBN_LogTrace(...) (void)0; - -void Log(int, const char *, ...); - -#include "../../nbnet.h" - -#ifdef __EMSCRIPTEN__ -#include "../../net_drivers/webrtc.h" -#else -#include "../../net_drivers/udp.h" -#endif - -void ExampleSleep(double); - -#endif /* RPC_EXAMPLE_SHARED_H */ diff --git a/net_drivers/json.h b/json.h similarity index 100% rename from net_drivers/json.h rename to json.h diff --git a/nbnet.c b/nbnet.c new file mode 100644 index 0000000..f26fa05 --- /dev/null +++ b/nbnet.c @@ -0,0 +1,4357 @@ +/* + + Copyright (C) 2026 BIAGINI Nathan + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +#include +#include "nbnet.h" + +#define STB_DS_IMPLEMENTATION +#include "stb_ds.h" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) + +#define NBN_PLATFORM_WINDOWS +#define WIN32_LEAN_AND_MEAN +// prevent inclusion of winnt.h in windows.h +#define _WINNT_ + +#include +#include + +#elif (defined(__APPLE__) && defined(__MACH__)) + +#define NBN_PLATFORM_MAC + +#else + +#define NBN_PLATFORM_UNIX + +#endif + +#ifndef NBN_PLATFORM_WINDOWS + +#include + +#if _POSIX_C_SOURCE >= 199309L + +#include + +#else + +#include + +#endif // _POSIX_C_SOURCE >= 199309L + +#ifndef CLOCK_MONOTONIC_RAW +#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC +#endif + +#endif // NBN_PLATFORM_WINDOWS + +#ifdef NBN_UDP + +#if defined(NBN_PLATFORM_WINDOWS) + +#include + +typedef int socklen_t; + +#elif defined(NBN_PLATFORM_UNIX) || defined(NBN_PLATFORM_MAC) + +#include +#include +#include +#include +#include + +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define closesocket(s) close(s) + +typedef int SOCKET; +typedef struct sockaddr_in SOCKADDR_IN; +typedef struct sockaddr SOCKADDR; +typedef struct in_addr IN_ADDR; + +#endif // NBN_PLATFORM_WINDOWS + +#endif // NBN_UDP + +typedef struct NBN_Connection NBN_Connection; +typedef struct NBN_Endpoint NBN_Endpoint; +typedef struct NBN_Channel NBN_Channel; +typedef struct NBN_Driver NBN_Driver; + +typedef enum NBN_Driver_ID NBN_Driver_ID; + +#define NBN_Abort abort +#define NBN_Assert(cond) assert(cond) + +#define SEQUENCE_NUMBER_GT(seq1, seq2) \ + ((seq1 > seq2 && (seq1 - seq2) <= 32767) || (seq1 < seq2 && (seq2 - seq1) >= 32767)) +#define SEQUENCE_NUMBER_GTE(seq1, seq2) \ + ((seq1 >= seq2 && (seq1 - seq2) <= 32767) || (seq1 <= seq2 && (seq2 - seq1) >= 32767)) +#define SEQUENCE_NUMBER_LT(seq1, seq2) \ + ((seq1 < seq2 && (seq2 - seq1) <= 32767) || (seq1 > seq2 && (seq1 - seq2) >= 32767)) +#define SEQUENCE_NUMBER_LTE(seq1, seq2) \ + ((seq1 <= seq2 && (seq2 - seq1) <= 32767) || (seq1 >= seq2 && (seq1 - seq2) >= 32767)) + +#define B_MASK(n) (1u << (n)) +#define B_SET(mask, n) (mask |= B_MASK(n)) +#define B_UNSET(mask, n) (mask &= ~B_MASK(n)) +#define B_IS_SET(mask, n) ((B_MASK(n) & mask) == B_MASK(n)) +#define B_IS_UNSET(mask, n) ((B_MASK(n) & mask) == 0) + +#define HANDLE_TO_CONN(h) ((NBN_Connection *)(void *)h) + +#define NBN_EVENT_QUEUE_CAPACITY 1024 + +#define NBN_CLIENT_CLOSED_MESSAGE_TYPE UINT8_MAX +#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (UINT8_MAX - 1) +#define NBN_DISCONNECTION_MESSAGE_TYPE (UINT8_MAX - 2) +#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (UINT8_MAX - 3) + +/* + * Maximum allowed packet size (including header) in bytes. + * The 1400 value has been chosen based on this statement: + * + * With the IPv4 header being 20 bytes and the UDP header being 8 bytes, the payload + * of a UDP packet should be no larger than 1500 - 20 - 8 = 1472 bytes to avoid fragmentation. + */ +#define NBN_PACKET_MAX_SIZE 1400 + +#define NBN_MAX_MESSAGES_PER_PACKET UINT8_MAX +#define NBN_MESSAGE_HEADER_SIZE 6 /* See NBN_MessageHeader struct */ +#define NBN_PACKET_HEADER_SIZE 13 + +/* Maximum size of packet's data (NBN_PACKET_MAX_DATA_SIZE + NBN_PACKET_HEADER_SIZE = NBN_PACKET_MAX_SIZE) */ +#define NBN_PACKET_MAX_DATA_SIZE (NBN_PACKET_MAX_SIZE - NBN_PACKET_HEADER_SIZE) + +#define NBN_MAX_PACKET_ENTRIES 1024 + +#define NBN_MAX_CHANNEL_COUNT 16 +#define NBN_CHANNEL_DEFAULT_BUFFER_SIZE 128 +#define NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE 256 + +typedef enum NBN_PacketResult { + NBN_PACKET_WRITE_ERROR = -1, + NBN_PACKET_WRITE_OK, + NBN_PACKET_WRITE_NO_SPACE, +} NBN_PacketResult; + +typedef enum NBN_PacketMode { NBN_PACKET_MODE_WRITE = 1, NBN_PACKET_MODE_READ } NBN_PacketMode; + +// IMPORTANT: don't forget to update NBN_PACKET_HEADER_SIZE after modifying this structure +typedef struct NBN_PacketHeader { + uint32_t protocol_id; + uint32_t ack_bits; + uint16_t seq_number; + uint16_t ack; + uint8_t messages_count; +} NBN_PacketHeader; + +typedef struct NBN_Packet { + NBN_PacketHeader header; + NBN_PacketMode mode; + struct NBN_Connection *sender; /* not serialized, filled by the network driver upon reception */ + uint8_t buffer[NBN_PACKET_MAX_SIZE]; + unsigned int size; /* in bytes */ + bool sealed; +} NBN_Packet; + +typedef struct NBN_MessageEntry { + uint16_t id; + uint8_t channel_id; +} NBN_MessageEntry; + +typedef struct NBN_PacketEntry { + bool acked; + bool flagged_as_lost; + unsigned int messages_count; + double send_time; + NBN_MessageEntry messages[NBN_MAX_MESSAGES_PER_PACKET]; +} NBN_PacketEntry; + +// IMPORTANT: make sure you update NBN_MESSAGE_HEADER_SIZE if you modify NBN_MessageHeader struct +typedef struct NBN_MessageHeader { + uint16_t id; + uint16_t length; + uint8_t type; + uint8_t channel_id; +} NBN_MessageHeader; + +typedef struct NBN_Message { + NBN_MessageHeader header; + NBN_Connection *sender; + uint8_t *data; +} NBN_Message; + +typedef struct NBN_OutgoingMessage { + NBN_Message message; + NBN_Writer writer; + double last_send_time; + bool free; +} NBN_OutgoingMessage; + +typedef struct NBN_IncomingMessage { + NBN_Message message; + bool free; +} NBN_IncomingMessage; + +struct NBN_Channel { + uint8_t id; + NBN_Channel_Mode mode; + uint16_t next_outgoing_message_id; + uint16_t next_recv_message_id; + uint16_t oldest_unacked_message_id; + uint16_t most_recent_message_id; + uint16_t last_received_message_id; + unsigned int next_outgoing_message_slot; + unsigned int outgoing_message_count; + unsigned int buffer_size; + unsigned int max_message_len; + unsigned int current_capacity; + NBN_OutgoingMessage *outgoing_messages_buffer; + NBN_IncomingMessage *incoming_messages_buffer; + bool *ack_buffer; // TODO: needed? +}; + +typedef struct NBN_IPAddress { + uint32_t host; + uint16_t port; +} NBN_IPAddress; + +#ifdef __EMSCRIPTEN__ + +typedef uint32_t NBN_WebRTC_Peer_ID; + +#endif + +#ifdef NBN_WEBRTC_NATIVE + +typedef int NBN_WebRTC_Peer_ID; + +#endif + +struct NBN_Connection { + NBN_ConnectionHandle handle; + double last_recv_packet_time; /* Used to detect stale connections */ + double last_flush_time; /* Last time the send queue was flushed */ + double last_read_packets_time; /* Last time packets were read from the network driver */ + /* Keep track of bytes read from the socket (used for download bandwith calculation) */ + unsigned int downloaded_bytes; + uint8_t is_accepted : 1; + uint8_t is_stale : 1; + uint8_t is_closed : 1; + NBN_Driver *driver; /* Network driver used for that connection */ + NBN_Channel *channels; /* Message channels (sending & receiving) */ + unsigned int channel_count; + NBN_ConnectionStats stats; + + /* + * Packet sequencing & acking + */ + uint16_t next_packet_seq_number; + uint16_t last_received_packet_seq_number; + uint32_t packet_send_seq_buffer[NBN_MAX_PACKET_ENTRIES]; + NBN_PacketEntry packet_send_buffer[NBN_MAX_PACKET_ENTRIES]; + uint32_t packet_recv_seq_buffer[NBN_MAX_PACKET_ENTRIES]; + + /* Driver-related data attached to the connection */ + struct { +#ifdef NBN_UDP + struct { + NBN_IPAddress ip_address; + } udp; +#endif // NBN_UDP + +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + struct { + NBN_WebRTC_Peer_ID peer_id; +#ifdef NBN_WEBRTC_NATIVE + int channel_id; + int ws; +#endif // NBN_WEBRTC_NATIVE + } webrtc; +#endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + + void *endpoint_ptr; + } driver_data; +}; + +typedef union NBN_EventData { + NBN_MessageInfo message_info; + NBN_DisconnectionInfo disconnection; + NBN_Connection *connection; +} NBN_EventData; + +typedef struct NBN_Event { + int type; + NBN_EventData data; +} NBN_Event; + +/** + * ====== DATA STRUCTURES ====== + */ + +typedef struct NBN_ConnectionListNode NBN_ConnectionListNode; + +/* Linked list of connections */ +struct NBN_ConnectionListNode { + NBN_Connection *conn; + NBN_ConnectionListNode *next; + NBN_ConnectionListNode *prev; +}; + +typedef struct NBN_EventQueue { + NBN_Event events[NBN_EVENT_QUEUE_CAPACITY]; + unsigned int head; + unsigned int tail; + unsigned int count; +} NBN_EventQueue; + +static void EventQueue_Init(NBN_EventQueue *event_queue) { + event_queue->head = 0; + event_queue->tail = 0; + event_queue->count = 0; +} + +static bool EventQueue_Enqueue(NBN_EventQueue *event_queue, NBN_Event ev) { + if (event_queue->count >= NBN_EVENT_QUEUE_CAPACITY) + return false; + + event_queue->events[event_queue->tail] = ev; + + event_queue->tail = (event_queue->tail + 1) % NBN_EVENT_QUEUE_CAPACITY; + event_queue->count++; + + return true; +} + +static bool EventQueue_IsEmpty(NBN_EventQueue *event_queue) { return event_queue->count == 0; } + +static bool EventQueue_Dequeue(NBN_EventQueue *event_queue, NBN_Event *ev) { + if (EventQueue_IsEmpty(event_queue)) + return false; + + memcpy(ev, &event_queue->events[event_queue->head], sizeof(NBN_Event)); + event_queue->head = (event_queue->head + 1) % NBN_EVENT_QUEUE_CAPACITY; + event_queue->count--; + + return true; +} + +// END DATA STRUCTURES +// =================================================== + +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + +#ifndef NBN_PLATFORM_WINDOWS +#include +#endif /* NBN_PLATFORM_WINDOWS */ + +typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; + +struct NBN_PacketSimulatorEntry { + NBN_Packet packet; + NBN_Connection *receiver; + double delay; + double enqueued_at; + struct NBN_PacketSimulatorEntry *next; + struct NBN_PacketSimulatorEntry *prev; +}; + +typedef struct NBN_PacketSimulator { + NBN_Endpoint *endpoint; + NBN_PacketSimulatorEntry *head_packet; + NBN_PacketSimulatorEntry *tail_packet; + unsigned int packet_count; + +#ifdef NBN_PLATFORM_WINDOWS + HANDLE queue_mutex; + HANDLE thread; +#else + pthread_mutex_t queue_mutex; + pthread_t thread; +#endif + + bool running; + unsigned int total_dropped_packets; + + /* Settings */ + float packet_loss_ratio; + float current_packet_loss_ratio; + float packet_duplication_ratio; + double ping; + double jitter; +} NBN_PacketSimulator; + +static void PacketSimulator_Init(NBN_PacketSimulator *, NBN_Endpoint *); +static int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *); +static void PacketSimulator_Start(NBN_PacketSimulator *); +static void PacketSimulator_Stop(NBN_PacketSimulator *); +static int PacketSimulator_SendPacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *receiver); +static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *); + +#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ + +typedef struct NBN_Channel_Config { + NBN_Channel_Mode mode; + unsigned int buffer_size; + unsigned int max_message_len; +} NBN_Channel_Config; + +struct NBN_Endpoint { + NBN_EventQueue event_queue; + uint32_t protocol_id; + bool is_server; + double time; + NBN_Reader message_reader; + uint8_t server_initial_data_buffer[NBN_SERVER_INITIAL_DATA_MAX_SIZE]; + uint8_t connection_request_data_buffer[NBN_CONNECTION_REQUEST_DATA_MAX_SIZE]; + unsigned int client_connection_request_data_len; + unsigned int server_initial_data_len; + unsigned int channel_count; + NBN_Channel_Config *channels; + uint8_t default_reliable_channel; + uint8_t default_unreliable_channel; + NBN_Packet read_packet; +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + NBN_PacketSimulator packet_simulator; +#endif +}; + +typedef struct NBN_Server_Config { + const char *protocol_name; + uint16_t port; + NBN_Channel_Config channels[NBN_MAX_CHANNEL_COUNT]; + unsigned int channel_count; +} NBN_Server_Config; + +struct NBN_Server { + NBN_Endpoint endpoint; + NBN_Server_Config config; + struct { + NBN_Connection_ID key; + NBN_Connection *value; + } *clients; + NBN_ConnectionListNode *closed_clients_head; + NBN_ServerStats stats; + NBN_Event last_event; + NBN_Writer server_data_writer; + NBN_Reader client_data_reader; + + struct { +#ifdef NBN_UDP + struct { + SOCKET sock; + } udp; +#endif + +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + struct { + NBN_WebRTC_Config cfg; + int ws_server; + } webrtc; +#endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + } driver_data; +}; + +typedef struct NBN_Client_Config { + const char *protocol_name; + const char *host; + uint16_t port; + NBN_Channel_Config channels[NBN_MAX_CHANNEL_COUNT]; + unsigned int channel_count; +} NBN_Client_Config; + +struct NBN_Client { + NBN_Endpoint endpoint; + NBN_Client_Config config; + NBN_Connection *server_connection; + bool is_connected; + NBN_Event last_event; + int closed_code; + NBN_Writer client_data_writer; + NBN_Reader server_data_reader; + + struct { +#ifdef NBN_UDP + struct { + SOCKET sock; + } udp; +#endif + +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + struct { + NBN_WebRTC_Config cfg; + bool is_connected; + } webrtc; +#endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + } driver_data; +}; + +typedef int (*NBN_Driver_Func_ClientStart)(NBN_Client *, const char *, uint16_t); +typedef void (*NBN_Driver_Func_ClientStop)(NBN_Client *); +typedef int (*NBN_Driver_Func_ClientSendPacket)(NBN_Client *, NBN_Packet *, NBN_Connection *); +typedef int (*NBN_Driver_Func_ClientRecvPackets)(NBN_Client *); + +typedef int (*NBN_Driver_Func_ServerStart)(NBN_Server *, uint16_t); +typedef void (*NBN_Driver_Func_ServerStop)(NBN_Server *); +typedef int (*NBN_Driver_Func_ServerSendPacketTo)(NBN_Server *, NBN_Packet *, NBN_Connection *); +typedef void (*NBN_Driver_Func_ServerCleanupConnection)(NBN_Server *, NBN_Connection *); +typedef int (*NBN_Driver_Func_ServerRecvPackets)(NBN_Server *); + +typedef struct NBN_Driver_Implementation { + /* Client functions */ + NBN_Driver_Func_ClientStart cli_start; + NBN_Driver_Func_ClientStop cli_stop; + NBN_Driver_Func_ClientRecvPackets cli_recv_packets; + NBN_Driver_Func_ClientSendPacket cli_send_packet; + + /* Server functions */ + NBN_Driver_Func_ServerStart serv_start; + NBN_Driver_Func_ServerStop serv_stop; + NBN_Driver_Func_ServerRecvPackets serv_recv_packets; + NBN_Driver_Func_ServerSendPacketTo serv_send_packet_to; + NBN_Driver_Func_ServerCleanupConnection serv_cleanup_connection; +} NBN_Driver_Implementation; + +enum NBN_Driver_ID { NBN_DRIVER_UDP = 0x01, NBN_DRIVER_WEBRTC_EMSCRIPTEN = 0x02, NBN_DRIVER_WEBRTC_NATIVE = 0x03 }; + +struct NBN_Driver { + int id; + const char *name; + NBN_Driver_Implementation impl; +}; + +#ifdef NBN_UDP + +static int UDP_Client_Start(NBN_Client *client, const char *host, uint16_t port); +static void UDP_Client_Stop(NBN_Client *client); +static int UDP_Client_RecvPackets(NBN_Client *client); +static int UDP_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection); + +static int UDP_Server_Start(NBN_Server *server, uint16_t port); +static void UDP_Server_Stop(NBN_Server *server); +static int UDP_Server_RecvPackets(NBN_Server *server); +static int UDP_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *connection); +static void UDP_Server_CleanupConnection(NBN_Server *server, NBN_Connection *connection); + +static NBN_Driver nbn_udp_driver = {.id = NBN_DRIVER_UDP, + .name = "UDP", + .impl = {// Client implementation + .cli_start = UDP_Client_Start, + .cli_stop = UDP_Client_Stop, + .cli_recv_packets = UDP_Client_RecvPackets, + .cli_send_packet = UDP_Client_SendPacket, + + // Server implementation + .serv_start = UDP_Server_Start, + .serv_stop = UDP_Server_Stop, + .serv_recv_packets = UDP_Server_RecvPackets, + .serv_send_packet_to = UDP_Server_SendPacketTo, + .serv_cleanup_connection = UDP_Server_CleanupConnection}}; +#endif // NBN_UDP + +#ifdef __EMSCRIPTEN__ + +#ifdef NBN_UDP +#error "Cannot compile UDP driver with emscripten" +#endif + +#ifdef NBN_WEBRTC_NATIVE +#error "Cannot compile native WebRTC driver with emscripten" +#endif + +// TODO: add a check for webrtc native as well + +#include + +static int WebRTC_Client_Start(NBN_Client *client, const char *host, uint16_t port); +static void WebRTC_Client_Stop(NBN_Client *client); +static int WebRTC_Client_RecvPackets(NBN_Client *client); +static int WebRTC_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection); + +static int WebRTC_Server_Start(NBN_Server *server, uint16_t port); +static void WebRTC_Server_Stop(NBN_Server *server); +static int WebRTC_Server_RecvPackets(NBN_Server *server); +static int WebRTC_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *connection); +static void WebRTC_Server_CleanupConnection(NBN_Server *server, NBN_Connection *connection); + +static NBN_Driver nbn_webrtc_em_driver = {.id = NBN_DRIVER_WEBRTC_EMSCRIPTEN, + .name = "WebRTC_EMSCRIPTEN", + .impl = {// Client implementation + .cli_start = WebRTC_Client_Start, + .cli_stop = WebRTC_Client_Stop, + .cli_recv_packets = WebRTC_Client_RecvPackets, + .cli_send_packet = WebRTC_Client_SendPacket, + + // Server implementation + .serv_start = WebRTC_Server_Start, + .serv_stop = WebRTC_Server_Stop, + .serv_recv_packets = WebRTC_Server_RecvPackets, + .serv_send_packet_to = WebRTC_Server_SendPacketTo, + .serv_cleanup_connection = WebRTC_Server_CleanupConnection}}; + +#endif // __EMSCRIPTEN__ + +#ifdef NBN_WEBRTC_NATIVE + +static int WebRTC_Native_Client_Start(NBN_Client *client, const char *host, uint16_t port); +static void WebRTC_Native_Client_Stop(NBN_Client *client); +static int WebRTC_Native_Client_RecvPackets(NBN_Client *client); +static int WebRTC_Native_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection); + +static int WebRTC_Native_Server_Start(NBN_Server *server, uint16_t port); +static void WebRTC_Native_Server_Stop(NBN_Server *server); +static int WebRTC_Native_Server_RecvPackets(NBN_Server *server); +static int WebRTC_Native_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *connection); +static void WebRTC_Native_Server_CleanupConnection(NBN_Server *server, NBN_Connection *connection); + +static NBN_Driver nbn_webrtc_native_driver = { + .id = NBN_DRIVER_WEBRTC_NATIVE, + .name = "WebRTC_NATIVE", + .impl = {// Client implementation + .cli_start = WebRTC_Native_Client_Start, + .cli_stop = WebRTC_Native_Client_Stop, + .cli_recv_packets = WebRTC_Native_Client_RecvPackets, + .cli_send_packet = WebRTC_Native_Client_SendPacket, + + // Server implementation + .serv_start = WebRTC_Native_Server_Start, + .serv_stop = WebRTC_Native_Server_Stop, + .serv_recv_packets = WebRTC_Native_Server_RecvPackets, + .serv_send_packet_to = WebRTC_Native_Server_SendPacketTo, + .serv_cleanup_connection = WebRTC_Native_Server_CleanupConnection}}; + +#endif // NBN_WEBRTC_NATIVE + +/** + * ====== LOGGING ====== + */ + +#define LogInfo(msg, ...) Log(NBN_LOG_INFO, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogWarning(msg, ...) Log(NBN_LOG_WARNING, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#define LogError(msg, ...) Log(NBN_LOG_ERROR, __FILE__, __LINE__, msg, ##__VA_ARGS__) + +#ifdef NBN_DEBUG +#define LogDebug(msg, ...) Log(NBN_LOG_DEBUG, __FILE__, __LINE__, msg, ##__VA_ARGS__) +#else +#define LogDebug(...) \ + ; \ + ; +#endif // NBN_DEBUG + +static void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); + +// END OF LOGGING +// =================================================== + +/** + * ====== SERIALIZATION ====== + */ + +static union { + uint64_t v; + uint8_t bytes[8]; +} swap; + +static uint64_t SwapBytes64(uint64_t v) { + if (htonl(1) == 1) { + return v; + } + + swap.v = v; + + return ((uint64_t)swap.bytes[0] << 56) | ((uint64_t)swap.bytes[1] << 48) | ((uint64_t)swap.bytes[2] << 40) | + ((uint64_t)swap.bytes[3] << 32) | ((uint64_t)swap.bytes[4] << 24) | ((uint64_t)swap.bytes[5] << 16) | + ((uint64_t)swap.bytes[6] << 8) | (uint64_t)swap.bytes[7]; +} + +void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length) { + writer->buffer = buffer; + writer->length = length; + writer->position = 0; +} + +void NBN_Writer_WriteInt8(NBN_Writer *writer, int8_t value) { NBN_Writer_WriteUInt8(writer, value); } + +void NBN_Writer_WriteInt16(NBN_Writer *writer, int16_t value) { NBN_Writer_WriteUInt16(writer, value); } + +void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value) { NBN_Writer_WriteUInt32(writer, value); } + +void NBN_Writer_WriteInt64(NBN_Writer *writer, int64_t value) { NBN_Writer_WriteUInt64(writer, value); } + +void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value) { + NBN_Assert(writer->position + 1 <= writer->length); + + writer->buffer[writer->position] = value; + writer->position++; +} + +void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value) { + NBN_Assert(writer->position + 2 <= writer->length); + + *((uint16_t *)(writer->buffer + writer->position)) = htons(value); + writer->position += 2; +} + +void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value) { + NBN_Assert(writer->position + 4 <= writer->length); + + *((uint32_t *)(writer->buffer + writer->position)) = htonl(value); + writer->position += 4; +} + +void NBN_Writer_WriteUInt64(NBN_Writer *writer, uint64_t value) { + NBN_Assert(writer->position + 8 <= writer->length); + + *((uint64_t *)(writer->buffer + writer->position)) = htonl(1) == 1 ? value : SwapBytes64(value); + writer->position += 8; +} + +void NBN_Writer_WriteFloat(NBN_Writer *writer, float value) { + NBN_Assert(writer->position + 4 <= writer->length); + + uint32_t *val_u = (uint32_t *)&value; + NBN_Writer_WriteUInt32(writer, htonl(*val_u)); +} + +void NBN_Writer_WriteBool(NBN_Writer *writer, bool value) { NBN_Writer_WriteUInt8(writer, value); } + +void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length) { + NBN_Assert(writer->position + length <= writer->length); + + memcpy(writer->buffer + writer->position, bytes, length); + writer->position += length; +} + +void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len) { + unsigned int len = strnlen(str, max_len); + + NBN_Writer_WriteUInt32(writer, len); + NBN_Writer_WriteBytes(writer, (uint8_t *)str, len); +} + +void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length) { + reader->buffer = buffer; + reader->length = length; + reader->position = 0; +} + +int NBN_Reader_ReadInt8(NBN_Reader *reader, int8_t *value) { return NBN_Reader_ReadUInt8(reader, (uint8_t *)value); } + +int NBN_Reader_ReadInt16(NBN_Reader *reader, int16_t *value) { + return NBN_Reader_ReadUInt16(reader, (uint16_t *)value); +} + +int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value) { + return NBN_Reader_ReadUInt32(reader, (uint32_t *)value); +} + +int NBN_Reader_ReadInt64(NBN_Reader *reader, int64_t *value) { + return NBN_Reader_ReadUInt64(reader, (uint64_t *)value); +} + +int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value) { + if (reader->position + 1 > reader->length) { + return NBN_ERROR; + } + + *value = reader->buffer[reader->position]; + reader->position++; + + return 0; +} + +int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value) { + if (reader->position + 2 > reader->length) { + return NBN_ERROR; + } + + *value = ntohs(*((uint16_t *)(reader->buffer + reader->position))); + reader->position += 2; + + return 0; +} + +int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value) { + if (reader->position + 4 > reader->length) { + return NBN_ERROR; + } + + *value = ntohl(*((uint32_t *)(reader->buffer + reader->position))); + reader->position += 4; + + return 0; +} + +int NBN_Reader_ReadUInt64(NBN_Reader *reader, uint64_t *value) { + if (reader->position + 8 > reader->length) { + return NBN_ERROR; + } + + *value = *((uint64_t *)(reader->buffer + reader->position)); + + if (htonl(1) != 1) { + *value = SwapBytes64(*value); + } + + reader->position += 8; + + return 0; +} + +int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value) { + if (NBN_Reader_ReadUInt32(reader, (uint32_t *)value) < 0) { + return -1; + } + + return 0; +} + +int NBN_Reader_ReadBool(NBN_Reader *reader, bool *value) { + if (NBN_Reader_ReadUInt8(reader, (uint8_t *)value) < 0) { + return -1; + } + + return 0; +} + +int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length) { + if (reader->position + length > reader->length) { + LogError("reader->position = %d, length = %d, reader->length = %d", reader->position, length, reader->length); + return NBN_ERROR; + } + + memcpy(bytes, reader->buffer + reader->position, length); + reader->position += length; + + return 0; +} + +int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len) { + unsigned int len; + + if (NBN_Reader_ReadUInt32(reader, &len) < 0) { + return NBN_ERROR; + } + + if (len > max_len - 1) { + return NBN_ERROR; + } + + NBN_Reader_ReadBytes(reader, (uint8_t *)str, len); + str[len] = 0; + + return 0; +} + +// END OF SERIALIZATION +// =================================================== + +/** + * ====== PACKET ====== + */ + +static void Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); +static NBN_PacketResult Packet_WriteMessage(NBN_Packet *, NBN_Message *); +static int Packet_Seal(NBN_Packet *); +static int Packet_InitRead(NBN_Packet *, uint32_t, unsigned int); + +static void Packet_InitWrite(NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, + uint32_t ack_bits) { + packet->header.protocol_id = protocol_id; + packet->header.messages_count = 0; + packet->header.seq_number = seq_number; + packet->header.ack = ack; + packet->header.ack_bits = ack_bits; + + packet->mode = NBN_PACKET_MODE_WRITE; + packet->sender = NULL; + packet->size = NBN_PACKET_HEADER_SIZE; + packet->sealed = false; + + memset(packet->buffer, 0, sizeof(packet->buffer)); +} + +static NBN_PacketResult Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message) { + LogDebug("Write message %d (type: %d, length: %d, channel: %d) to packet %d", message->header.id, + message->header.type, message->header.length, message->header.channel_id, packet->header.seq_number); + + if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) + return NBN_PACKET_WRITE_ERROR; + + unsigned int message_size = NBN_MESSAGE_HEADER_SIZE + message->header.length; + + if (packet->header.messages_count >= NBN_MAX_MESSAGES_PER_PACKET || + packet->size + message_size > NBN_PACKET_MAX_SIZE) { + return NBN_PACKET_WRITE_NO_SPACE; + } + + NBN_Writer writer; + + NBN_Writer_Init(&writer, packet->buffer + packet->size, sizeof(packet->buffer) - packet->size); + + NBN_Writer_WriteUInt16(&writer, message->header.id); + NBN_Writer_WriteUInt16(&writer, message->header.length); + NBN_Writer_WriteUInt8(&writer, message->header.type); + NBN_Writer_WriteUInt8(&writer, message->header.channel_id); + + NBN_Assert(writer.position == NBN_MESSAGE_HEADER_SIZE); + + if (message->header.length > 0) { + NBN_Writer_WriteBytes(&writer, message->data, message->header.length); + } + + NBN_Assert(writer.position == message_size); + + packet->size += writer.position; + packet->header.messages_count++; + + return NBN_PACKET_WRITE_OK; +} + +static int Packet_Seal(NBN_Packet *packet) { + if (packet->mode != NBN_PACKET_MODE_WRITE) + return NBN_ERROR; + + NBN_Writer writer; + + NBN_Writer_Init(&writer, packet->buffer, NBN_PACKET_HEADER_SIZE); + + NBN_Writer_WriteUInt32(&writer, packet->header.protocol_id); + NBN_Writer_WriteUInt32(&writer, packet->header.ack_bits); + NBN_Writer_WriteUInt16(&writer, packet->header.seq_number); + NBN_Writer_WriteUInt16(&writer, packet->header.ack); + NBN_Writer_WriteUInt8(&writer, packet->header.messages_count); + + NBN_Assert(writer.position == NBN_PACKET_HEADER_SIZE); + + packet->sealed = true; + + return 0; +} + +static int Packet_InitRead(NBN_Packet *packet, uint32_t protocol_id, unsigned int size) { + if (size < NBN_PACKET_HEADER_SIZE || size > NBN_PACKET_MAX_SIZE) { + return NBN_ERROR; + } + + packet->mode = NBN_PACKET_MODE_READ; + packet->size = size; + packet->sender = NULL; // IMPORTANT: must be set by the drivers + packet->sealed = false; + + NBN_Reader reader; + + NBN_Reader_Init(&reader, packet->buffer, NBN_PACKET_HEADER_SIZE); + + if (NBN_Reader_ReadUInt32(&reader, &packet->header.protocol_id) < 0) { + LogDebug("Failed to read packet's protocol id"); + return NBN_ERROR; + } + + if (packet->header.protocol_id != protocol_id) { + LogDebug("Packet's protocol id did not match (expected: %d, received: %d)", protocol_id, + packet->header.protocol_id); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt32(&reader, &packet->header.ack_bits) < 0) { + LogDebug("Failed to read packet's acked bits"); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt16(&reader, &packet->header.seq_number) < 0) { + LogDebug("Failed to read packet's sequence number"); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt16(&reader, &packet->header.ack) < 0) { + LogDebug("Failed to read packet's ack"); + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt8(&reader, &packet->header.messages_count) < 0) { + LogDebug("Failed to read packet's message count"); + return NBN_ERROR; + } + + return 0; +} + +// END OF PACKET +// =================================================== + +/** + * ====== CHANNEL ====== + */ + +static unsigned int Channel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) { + if (SEQUENCE_NUMBER_GT(id1, id2)) + return (id1 >= id2) ? id1 - id2 : ((0xFFFF + 1) - id2) + id1; + else + return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; +} + +static void Channel_Init(NBN_Channel *channel, uint8_t id, NBN_Channel_Config cfg) { + channel->id = id; + channel->mode = cfg.mode; + channel->next_outgoing_message_id = 0; + channel->next_recv_message_id = 0; + channel->outgoing_message_count = 0; + channel->last_received_message_id = -1; + channel->next_outgoing_message_slot = 0; + channel->oldest_unacked_message_id = 0; + channel->most_recent_message_id = 0; + channel->buffer_size = cfg.buffer_size; + channel->max_message_len = cfg.max_message_len; + channel->current_capacity = cfg.buffer_size; + channel->outgoing_messages_buffer = malloc(sizeof(NBN_OutgoingMessage) * cfg.buffer_size); + channel->incoming_messages_buffer = malloc(sizeof(NBN_IncomingMessage) * cfg.buffer_size); + channel->ack_buffer = malloc(sizeof(bool) * cfg.buffer_size); + + for (unsigned int i = 0; i < cfg.buffer_size; i++) { + channel->incoming_messages_buffer[i].message.data = malloc(cfg.max_message_len); + channel->incoming_messages_buffer[i].free = true; + + channel->outgoing_messages_buffer[i].message.data = malloc(cfg.max_message_len); + channel->outgoing_messages_buffer[i].free = true; + } + + for (unsigned int i = 0; i < cfg.buffer_size; i++) { + channel->ack_buffer[i] = false; + } +} + +static void Channel_UpdateMessageSendTime(NBN_Channel *channel, uint16_t msg_id, double time) { + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % channel->buffer_size]; + + NBN_Assert(msg_id == out_msg->message.header.id); + out_msg->last_send_time = time; +} + +static NBN_IncomingMessage *Channel_AddReceivedMessage(NBN_Channel *channel, NBN_MessageHeader *header) { + // TODO: check if current slot not free + // buffer ran out of slots + + NBN_Assert(header->length <= channel->max_message_len); // TODO: replace with channel msg len + + if (channel->mode == NBN_CHANNEL_UNRELIABLE) { + if (SEQUENCE_NUMBER_GT(header->id, channel->last_received_message_id)) { + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[header->id % channel->buffer_size]; + + inc_msg->free = false; + inc_msg->message.header = *header; + + channel->last_received_message_id = header->id; + + LogDebug("Add incomoing message %d of type %d to unreliable channel %d (last received msg id: %d)", + header->id, header->type, channel->id, channel->last_received_message_id); + + return inc_msg; + } + + return NULL; + } else if (channel->mode == NBN_CHANNEL_RELIABLE) { + unsigned int dt = Channel_ComputeMessageIdDelta(header->id, channel->most_recent_message_id); + + if (SEQUENCE_NUMBER_GT(header->id, channel->most_recent_message_id)) { + NBN_Assert(dt < channel->buffer_size); + + channel->most_recent_message_id = header->id; + } else { + if (dt >= channel->buffer_size) + return NULL; + + if (SEQUENCE_NUMBER_LT(header->id, channel->next_recv_message_id)) { + return NULL; + } + } + + LogDebug("Add incomoing message %d of type %d to reliable channel %d (most recent msg id: %d, dt: %d)", + header->id, header->type, channel->id, channel->most_recent_message_id, dt); + + NBN_IncomingMessage *inc_msg = &channel->incoming_messages_buffer[header->id % channel->buffer_size]; + + inc_msg->free = false; + inc_msg->message.header = *header; + + return inc_msg; + } + + NBN_Abort(); +} + +static NBN_Writer *Channel_AddOutgoingMessage(NBN_Channel *channel, uint8_t type) { + NBN_Assert(channel->mode == NBN_CHANNEL_UNRELIABLE || channel->mode == NBN_CHANNEL_RELIABLE); + + uint16_t msg_id = channel->next_outgoing_message_id; + int index = msg_id % channel->buffer_size; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; + + // make sure the outgoing message is not already in use + if (channel->current_capacity == 0) { + LogError("Channel %d outgoing buffer reached it's capacity (mode: %d, outgoing message count: %d, msg_id: %d, " + "index: %d, oldest unacked msg: %d)", + channel->id, channel->mode, channel->outgoing_message_count, msg_id, index, + channel->oldest_unacked_message_id); +#ifdef NBN_DEBUG + NBN_Abort(); +#endif + + return NULL; + } + + NBN_Assert(out_msg->free); + + out_msg->free = false; + out_msg->last_send_time = -1; + out_msg->message.header.id = msg_id; + out_msg->message.header.channel_id = channel->id; + out_msg->message.header.type = type; + out_msg->message.header.length = 0; + + channel->next_outgoing_message_id++; + channel->outgoing_message_count++; + channel->current_capacity--; + + NBN_Writer_Init(&out_msg->writer, out_msg->message.data, channel->max_message_len); + + LogDebug("Outgoing message %d (type: %d) added to channel %d", msg_id, type, channel->id); + + return &out_msg->writer; +} + +static NBN_Message *Channel_GetNextRecvedMessage(NBN_Channel *channel) { + if (channel->mode == NBN_CHANNEL_UNRELIABLE) { + while (SEQUENCE_NUMBER_LTE(channel->next_recv_message_id, channel->last_received_message_id)) { + NBN_IncomingMessage *inc_msg = + &channel->incoming_messages_buffer[channel->next_recv_message_id % channel->buffer_size]; + uint16_t msg_id = channel->next_recv_message_id; + + channel->next_recv_message_id++; + + if (!inc_msg->free && inc_msg->message.header.id == msg_id) { + inc_msg->free = true; + + return &inc_msg->message; + } + } + + return NULL; + } else if (channel->mode == NBN_CHANNEL_RELIABLE) { + NBN_IncomingMessage *inc_msg = + &channel->incoming_messages_buffer[channel->next_recv_message_id % channel->buffer_size]; + + if (!inc_msg->free && inc_msg->message.header.id == channel->next_recv_message_id) { + inc_msg->free = true; + channel->next_recv_message_id++; + + return &inc_msg->message; + } + + return NULL; + } else { + NBN_Abort(); + } +} + +static bool Channel_GetNextOutgoingMessage(NBN_Channel *channel, NBN_Message *res_msg, double time) { + if (channel->mode == NBN_CHANNEL_UNRELIABLE) { + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[channel->next_outgoing_message_slot]; + + if (out_msg->free) + return false; + + *res_msg = out_msg->message; + res_msg->header.length = out_msg->writer.position; + out_msg->free = true; + + channel->next_outgoing_message_slot++; + channel->next_outgoing_message_slot %= channel->buffer_size; + + return true; + } else if (channel->mode == NBN_CHANNEL_RELIABLE) { + int max_message_id = (channel->oldest_unacked_message_id + (channel->buffer_size - 1)) % (0xFFFF + 1); + + if (SEQUENCE_NUMBER_LT(channel->next_outgoing_message_id, max_message_id)) + max_message_id = channel->next_outgoing_message_id; + + uint16_t msg_id = channel->oldest_unacked_message_id; + + while (SEQUENCE_NUMBER_LT(msg_id, max_message_id)) { + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[msg_id % channel->buffer_size]; + + if (!out_msg->free && + (out_msg->last_send_time < 0 || time - out_msg->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) { + *res_msg = out_msg->message; + res_msg->header.length = out_msg->writer.position; + return true; + } + + msg_id++; + } + + return false; + } else { + NBN_Abort(); + } +} + +static int Channel_OnMessageSent(NBN_Channel *channel) { + if (channel->mode == NBN_CHANNEL_UNRELIABLE) { + channel->outgoing_message_count--; + channel->current_capacity++; + } + + return 0; +} + +static int Channel_OnOutgoingMessageAcked(NBN_Channel *channel, uint16_t msg_id) { + if (channel->mode != NBN_CHANNEL_RELIABLE) { + return 0; + } + + int index = msg_id % channel->buffer_size; + NBN_OutgoingMessage *out_msg = &channel->outgoing_messages_buffer[index]; + + if (out_msg->free || out_msg->message.header.id != msg_id) + return 0; + + out_msg->free = true; + + LogDebug("Message %d acked on channel %d (buffer index: %d, oldest unacked: %d)", msg_id, channel->id, index, + channel->oldest_unacked_message_id); + + channel->ack_buffer[index] = true; + channel->outgoing_message_count--; + + if (msg_id == channel->oldest_unacked_message_id) { + for (unsigned int i = 0; i < channel->buffer_size; i++) { + uint16_t ack_msg_id = msg_id + i; + int index = ack_msg_id % channel->buffer_size; + + if (channel->ack_buffer[index]) { + channel->ack_buffer[index] = false; + channel->oldest_unacked_message_id++; + channel->current_capacity++; + NBN_Assert(channel->current_capacity <= channel->buffer_size); + } else { + break; + } + } + + LogDebug("Updated oldest unacked message id on channel %d: %d", channel->id, + channel->oldest_unacked_message_id); + } + + return 0; +} + +// END OF CHANNEL +// =================================================== + +/** + * ====== CONNECTION ====== + */ + +static void Connection_Destroy(NBN_Connection *); +static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); +static int Connection_DecodePacketHeader(NBN_Connection *, NBN_Packet *, double); +static int Connection_AckPacket(NBN_Connection *, uint16_t, double time); +static void Connection_InitOutgoingPacket(NBN_Connection *, uint32_t, NBN_Packet *, NBN_PacketEntry **); +static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, uint16_t); +static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); +static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); +static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); +static int Connection_SendPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double, bool); +static int Connection_ReadNextMessageHeader(NBN_Reader *, NBN_MessageHeader *); +static void Connection_UpdateAveragePing(NBN_Connection *, double); +static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); +static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); +static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); +static int Connection_ProcessReceivedPacket(NBN_Endpoint *, NBN_Connection *, NBN_Packet *, double); +static int Connection_FlushChannels(NBN_Endpoint *, NBN_Connection *, uint32_t, double); +static bool Connection_CheckIfStale(NBN_Connection *, double); + +static int Connection_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, + double time) { + if (Connection_DecodePacketHeader(connection, packet, time) < 0) { + LogError("Failed to decode packet %d header", packet->header.seq_number); + + return NBN_ERROR; + } + + Connection_UpdateAveragePacketLoss(connection, packet->header.ack); + + if (!Connection_InsertReceivedPacketEntry(connection, packet->header.seq_number)) + return 0; + + if (SEQUENCE_NUMBER_GT(packet->header.seq_number, connection->last_received_packet_seq_number)) + connection->last_received_packet_seq_number = packet->header.seq_number; + + NBN_Reader msg_reader; + + NBN_Reader_Init(&msg_reader, packet->buffer + NBN_PACKET_HEADER_SIZE, packet->size - NBN_PACKET_HEADER_SIZE); + + LogDebug("Processing received packet %d (message count: %d)", packet->header.seq_number, + packet->header.messages_count); + + for (int i = 0; i < packet->header.messages_count; i++) { + LogDebug("Reading message number %d from packet %d", i, packet->header.seq_number); + + NBN_MessageHeader header = {0}; + int msg_len = Connection_ReadNextMessageHeader(&msg_reader, &header); + + if (msg_len < 0) { + LogError("Failed to read packet: invalid message header"); + + return NBN_ERROR; + } + + uint8_t channel_id = header.channel_id; + + if (channel_id > endpoint->channel_count - 1) { + LogError("Failed to read packet: invalid channel %d", channel_id); + + return NBN_ERROR; + } + + NBN_Channel *channel = &connection->channels[channel_id]; + + if ((unsigned int)msg_len > channel->max_message_len) { + LogError("Failed to read packet: message %d too large for channel %d (%d > %d)", header.id, channel_id, + header.length, channel->max_message_len); + + return NBN_ERROR; + } + + NBN_IncomingMessage *inc_msg = Channel_AddReceivedMessage(channel, &header); + + if (inc_msg) { + if (msg_len > 0) { + if (NBN_Reader_ReadBytes(&msg_reader, inc_msg->message.data, msg_len) < 0) { + LogError("Failed to read message data"); + + return NBN_ERROR; + } + } + + LogDebug("Received message %d (type: %d) on channel %d", header.id, header.type, channel->id); + } else { + LogDebug("Message %d was discarded by channel %d", header.id, channel->id); + + // NBN_Reader_ReadBytes is not called for discarded messages, so we need to + // advance the reader position "manually" + msg_reader.position += msg_len; + } + } + + return 0; +} + +static int Connection_FlushChannels(NBN_Endpoint *endpoint, NBN_Connection *connection, uint32_t protocol_id, + double time) { + LogDebug("Flushing all channels"); + + NBN_PacketEntry *packet_entry; + NBN_Packet *packet = &endpoint->read_packet; + + unsigned int sent_packet_count = 0; + unsigned int sent_bytes = 0; + + Connection_InitOutgoingPacket(connection, protocol_id, packet, &packet_entry); + + for (unsigned int i = 0; i < endpoint->channel_count; i++) { + NBN_Channel *channel = &connection->channels[i]; + + LogDebug("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); + + NBN_Message out_msg = {0}; + unsigned int j = 0; + + // TODO: use bandwidth to determine how many packets to send at most + while (j < channel->outgoing_message_count && sent_packet_count < 16 && + Channel_GetNextOutgoingMessage(channel, &out_msg, time)) { + uint8_t msg_type = out_msg.header.type; + uint16_t msg_id = out_msg.header.id; + uint16_t msg_len = out_msg.header.length; + bool message_sent = false; + NBN_PacketResult ret = Packet_WriteMessage(packet, &out_msg); + + if (ret == NBN_PACKET_WRITE_OK) { + message_sent = true; + } else if (ret == NBN_PACKET_WRITE_NO_SPACE) { + if (Connection_SendPacket(endpoint, connection, packet, packet_entry, time, endpoint->is_server) < 0) { + LogError("Failed to send packet %d", packet->header.seq_number); + + return NBN_ERROR; + } + + sent_packet_count++; + sent_bytes += packet->size; + + Connection_InitOutgoingPacket(connection, protocol_id, packet, &packet_entry); + + NBN_PacketResult ret = Packet_WriteMessage(packet, &out_msg); + + if (ret != NBN_PACKET_WRITE_OK) { + LogError("Failed to send packet %d", packet->header.seq_number); + + return NBN_ERROR; + } + + message_sent = true; + } else if (ret == NBN_PACKET_WRITE_ERROR) { + LogError("Failed to write message %d of type %d to packet %d", msg_id, msg_type, + packet->header.seq_number); + + return NBN_ERROR; + } + + if (message_sent) { + LogDebug("Message %d added to packet %d (length: %d, type: %d)", msg_id, packet->header.seq_number, + msg_len, msg_type); + + Channel_UpdateMessageSendTime(channel, msg_id, time); + + packet_entry->messages[packet_entry->messages_count++] = (NBN_MessageEntry){msg_id, channel->id}; + + Channel_OnMessageSent(channel); + } + + j++; + } + } + + if (Connection_SendPacket(endpoint, connection, packet, packet_entry, time, endpoint->is_server) < 0) { + LogError("Failed to send packet %d to connection %lld", packet->header.seq_number, connection->handle.id); + + return NBN_ERROR; + } + + sent_bytes += packet->size; + sent_packet_count++; + + double t = time - connection->last_flush_time; + + if (t > 0) + Connection_UpdateAverageUploadBandwidth(connection, sent_bytes / t); + + connection->last_flush_time = time; + + return 0; +} + +static bool Connection_CheckIfStale(NBN_Connection *connection, double time) { +#if defined(NBN_DEBUG) && defined(NBN_DISABLE_STALE_CONNECTION_DETECTION) + /* When testing under bad network conditions (in soak test for instance), we don't want to deal + with stale connections */ + return false; +#else + return time - connection->last_recv_packet_time > NBN_CONNECTION_STALE_TIME_THRESHOLD; +#endif +} + +static int Connection_DecodePacketHeader(NBN_Connection *connection, NBN_Packet *packet, double time) { + if (Connection_AckPacket(connection, packet->header.ack, time) < 0) { + LogError("Failed to ack packet %d", packet->header.seq_number); + + return NBN_ERROR; + } + + for (unsigned int i = 0; i < 32; i++) { + if (B_IS_UNSET(packet->header.ack_bits, i)) + continue; + + if (Connection_AckPacket(connection, packet->header.ack - (i + 1), time) < 0) { + LogError("Failed to ack packet %d", packet->header.seq_number); + + return NBN_ERROR; + } + } + + return 0; +} + +static void Connection_Destroy(NBN_Connection *connection) { + for (unsigned int i = 0; i < connection->channel_count; i++) { + NBN_Channel *channel = &connection->channels[i]; + + for (unsigned int j = 0; j < channel->buffer_size; j++) { + free(channel->incoming_messages_buffer[j].message.data); + free(channel->outgoing_messages_buffer[j].message.data); + } + + free(channel->incoming_messages_buffer); + free(channel->outgoing_messages_buffer); + free(channel->ack_buffer); + } + + free(connection->channels); + free(connection); +} + +static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) { + uint32_t ack_bits = 0; + + for (int i = 0; i < 32; i++) { + /* + when last_received_packet_seq_number is lower than 32, the value of acked_packet_seq_number will + eventually wrap around, which means the packets from before the wrap around will naturally be acked + */ + + uint16_t acked_packet_seq_number = connection->last_received_packet_seq_number - (i + 1); + + if (Connection_IsPacketReceived(connection, acked_packet_seq_number)) + B_SET(ack_bits, i); + } + + return ack_bits; +} + +static int Connection_AckPacket(NBN_Connection *connection, uint16_t ack_packet_seq_number, double time) { + NBN_PacketEntry *packet_entry = Connection_FindSendPacketEntry(connection, ack_packet_seq_number); + + if (packet_entry && !packet_entry->acked) { + LogDebug("Packet %d acked (connection: %lld)", ack_packet_seq_number, connection->handle.id); + + packet_entry->acked = true; + + Connection_UpdateAveragePing(connection, time - packet_entry->send_time); + + for (unsigned int i = 0; i < packet_entry->messages_count; i++) { + NBN_MessageEntry *msg_entry = &packet_entry->messages[i]; + NBN_Channel *channel = &connection->channels[msg_entry->channel_id]; + + NBN_Assert(channel != NULL); + + if (Channel_OnOutgoingMessageAcked(channel, msg_entry->id) < 0) { + return NBN_ERROR; + } + } + } + + return 0; +} + +static void Connection_InitOutgoingPacket(NBN_Connection *connection, uint32_t protocol_id, NBN_Packet *outgoing_packet, + NBN_PacketEntry **packet_entry) { + Packet_InitWrite(outgoing_packet, protocol_id, connection->next_packet_seq_number++, + connection->last_received_packet_seq_number, Connection_BuildPacketAckBits(connection)); + + *packet_entry = Connection_InsertOutgoingPacketEntry(connection, outgoing_packet->header.seq_number); +} + +static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *connection, uint16_t seq_number) { + uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; + NBN_PacketEntry entry = { + .acked = false, .flagged_as_lost = false, .messages_count = 0, .send_time = 0, .messages = {{0, 0}}}; + + connection->packet_send_seq_buffer[index] = seq_number; + connection->packet_send_buffer[index] = entry; + + return &connection->packet_send_buffer[index]; +} + +static bool Connection_InsertReceivedPacketEntry(NBN_Connection *connection, uint16_t seq_number) { + uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; + + /* Ignore duplicated packets */ + if (connection->packet_recv_seq_buffer[index] != 0xFFFFFFFF && + connection->packet_recv_seq_buffer[index] == seq_number) + return false; + + /* + Clear entries between the previous highest sequence numbers and new highest one + to avoid entries staying inside the sequence buffer from before the sequence wrap around + and break the packet acking logic. + */ + if (SEQUENCE_NUMBER_GT(seq_number, connection->last_received_packet_seq_number)) { + for (uint16_t seq = connection->last_received_packet_seq_number + 1; SEQUENCE_NUMBER_LT(seq, seq_number); seq++) + connection->packet_recv_seq_buffer[seq % NBN_MAX_PACKET_ENTRIES] = 0xFFFFFFFF; + } + + connection->packet_recv_seq_buffer[index] = seq_number; + + return true; +} + +static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *connection, uint16_t seq_number) { + uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; + + if (connection->packet_send_seq_buffer[index] == seq_number) + return &connection->packet_send_buffer[index]; + + return NULL; +} + +static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t packet_seq_number) { + uint16_t index = packet_seq_number % NBN_MAX_PACKET_ENTRIES; + + return connection->packet_recv_seq_buffer[index] == packet_seq_number; +} + +static int Connection_SendPacket(NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_Packet *packet, + NBN_PacketEntry *packet_entry, double time, bool is_server) { + LogDebug("Send packet %d to connection %lld (messages count: %d)", packet->header.seq_number, connection->handle.id, + packet->header.messages_count); + + NBN_Assert(packet_entry->messages_count == packet->header.messages_count); + + if (Packet_Seal(packet) < 0) { + LogError("Failed to seal packet"); + + return NBN_ERROR; + } + + packet_entry->send_time = time; + + if (is_server) { +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + return PacketSimulator_EnqueuePacket(&endpoint->packet_simulator, packet, connection); +#else + if (connection->is_stale) + return 0; + + return connection->driver->impl.serv_send_packet_to((NBN_Server *)endpoint, packet, connection); +#endif + } else { +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + return PacketSimulator_EnqueuePacket(&endpoint->packet_simulator, packet, connection); +#else + return connection->driver->impl.cli_send_packet((NBN_Client *)endpoint, packet, connection); +#endif + } +} + +static int Connection_ReadNextMessageHeader(NBN_Reader *reader, NBN_MessageHeader *header) { + if (NBN_Reader_ReadUInt16(reader, &header->id) < 0) { + LogError("Failed to read message id"); + + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt16(reader, &header->length) < 0) { + LogError("Failed to read message length"); + + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt8(reader, &header->type) < 0) { + LogError("Failed to read message type"); + + return NBN_ERROR; + } + + if (NBN_Reader_ReadUInt8(reader, &header->channel_id) < 0) { + LogError("Failed to read message channel"); + + return NBN_ERROR; + } + + return header->length; +} + +static void Connection_UpdateAveragePing(NBN_Connection *connection, double ping) { + /* exponential smoothing with a factor of 0.05 */ + connection->stats.ping = connection->stats.ping + .05f * (ping - connection->stats.ping); +} + +static void Connection_UpdateAveragePacketLoss(NBN_Connection *connection, uint16_t seq) { + unsigned int lost_packet_count = 0; + uint16_t start_seq = seq - 64; + + for (int i = 0; i < 100; i++) { + uint16_t s = start_seq - i; + NBN_PacketEntry *entry = Connection_FindSendPacketEntry(connection, s); + + if (entry && !entry->acked) { + lost_packet_count++; + + if (!entry->flagged_as_lost) { + entry->flagged_as_lost = true; + connection->stats.total_lost_packets++; + } + } + } + + float packet_loss = lost_packet_count / 100.f; + + /* exponential smoothing with a factor of 0.1 */ + connection->stats.packet_loss = connection->stats.packet_loss + .1f * (packet_loss - connection->stats.packet_loss); +} + +static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *connection, float bytes_per_sec) { + /* exponential smoothing with a factor of 0.1 */ + connection->stats.upload_bandwidth = + connection->stats.upload_bandwidth + .1f * (bytes_per_sec - connection->stats.upload_bandwidth); +} + +static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection, double time) { + double t = time - connection->last_read_packets_time; + + if (t == 0) + return; + + float bytes_per_sec = connection->downloaded_bytes / t; + + /* exponential smoothing with a factor of 0.1 */ + connection->stats.download_bandwidth = + connection->stats.download_bandwidth + .1f * (bytes_per_sec - connection->stats.download_bandwidth); + + connection->downloaded_bytes = 0; +} + +// END OF CONNECTION +// =================================================== + +/** + * ====== ENDPOINT ====== + */ + +static void Endpoint_Init(NBN_Endpoint *, uint32_t, bool, NBN_Channel_Config *, unsigned int); +static void Endpoint_Deinit(NBN_Endpoint *); +static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, NBN_Connection_ID, NBN_Driver_ID); +static uint32_t Endpoint_BuildProtocolId(const char *); +static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); +static void Endpoint_UpdateTime(NBN_Endpoint *); +static NBN_Writer *Endpoint_CreateOutgoingMessage(NBN_Endpoint *, NBN_Connection *, uint8_t, uint8_t); + +static void Endpoint_Init(NBN_Endpoint *endpoint, uint32_t protocol_id, bool is_server, NBN_Channel_Config *channels, + unsigned int channel_count) { + NBN_Assert(channel_count >= 2 && channel_count <= NBN_MAX_CHANNEL_COUNT); + + endpoint->is_server = is_server; + endpoint->protocol_id = protocol_id; + endpoint->channel_count = channel_count; + endpoint->channels = malloc(sizeof(NBN_Channel_Config) * channel_count); + + memcpy(endpoint->channels, channels, sizeof(NBN_Channel_Config) * channel_count); + + EventQueue_Init(&endpoint->event_queue); + +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + PacketSimulator_Init(&endpoint->packet_simulator, endpoint); + PacketSimulator_Start(&endpoint->packet_simulator); +#endif + + Endpoint_UpdateTime(endpoint); +} + +static void Endpoint_Deinit(NBN_Endpoint *endpoint) { +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + PacketSimulator_Stop(&endpoint->packet_simulator); +#endif +} + +static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, NBN_Connection_ID id, + NBN_Driver_ID driver_id) { + NBN_Connection *connection = (NBN_Connection *)malloc(sizeof(NBN_Connection)); + + connection->handle.id = id; + connection->handle.user_data = NULL; + connection->last_recv_packet_time = endpoint->time; + connection->next_packet_seq_number = 1; + connection->last_received_packet_seq_number = 0; + connection->last_flush_time = endpoint->time; + connection->last_read_packets_time = endpoint->time; + connection->downloaded_bytes = 0; + connection->is_accepted = false; + connection->is_stale = false; + connection->is_closed = false; + + for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) { + connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; + connection->packet_recv_seq_buffer[i] = 0xFFFFFFFF; + } + + NBN_ConnectionStats stats = {0}; + + connection->stats = stats; + connection->channels = malloc(sizeof(NBN_Channel) * endpoint->channel_count); + connection->channel_count = endpoint->channel_count; + + for (unsigned int i = 0; i < endpoint->channel_count; i++) { + Channel_Init(&connection->channels[i], i, endpoint->channels[i]); + } + + switch (driver_id) { +#ifdef NBN_UDP + case NBN_DRIVER_UDP: + connection->driver = &nbn_udp_driver; + break; +#endif // NBN_UDP + +#ifdef NBN_WEBRTC_NATIVE + case NBN_DRIVER_WEBRTC_NATIVE: + connection->driver = &nbn_webrtc_native_driver; + break; +#endif // NBN_WEBRTC_NATIVE + +#ifdef __EMSCRIPTEN__ + case NBN_DRIVER_WEBRTC_EMSCRIPTEN: + connection->driver = &nbn_webrtc_em_driver; + break; +#endif + default: + LogError("Unsupported driver: %d", driver_id); + NBN_Abort(); + } + + return connection; +} + +static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) { + uint32_t protocol_id = 2166136261; + + for (unsigned int i = 0; i < strlen(protocol_name); i++) { + protocol_id *= 16777619; + protocol_id ^= protocol_name[i]; + } + + return protocol_id; +} + +static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *packet, NBN_Connection *connection) { + (void)endpoint; + + LogDebug("Received packet %d (conn id: %lld, ack: %d, messages count: %d)", packet->header.seq_number, + connection->handle.id, packet->header.ack, packet->header.messages_count); + + if (Connection_ProcessReceivedPacket(endpoint, connection, packet, endpoint->time) < 0) { + LogError("Error when processing packet"); + return NBN_ERROR; + } + + connection->last_recv_packet_time = endpoint->time; + connection->downloaded_bytes += packet->size; + + return 0; +} + +static NBN_Writer *Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, NBN_Connection *connection, uint8_t type, + uint8_t channel_id) { + NBN_Assert(channel_id < endpoint->channel_count); + NBN_Assert(!connection->is_closed || type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); + NBN_Assert(!connection->is_stale); + + LogDebug("Create outgoing message of type %d on channel %d", type, channel_id); + + NBN_Channel *channel = &connection->channels[channel_id]; + NBN_Writer *writer = Channel_AddOutgoingMessage(channel, type); + + if (!writer) { + LogError("Failed to enqueue outgoing message of type %d on channel %d", type, channel_id); + + return NULL; + } + + return writer; +} + +static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) { +#if defined(NBN_PLATFORM_WINDOWS) + endpoint->time = GetTickCount64() / 1000.0; +#elif defined(__EMSCRIPTEN__) + endpoint->time = emscripten_get_now() / 1000; +#else + static struct timespec tp; + + if (clock_gettime(CLOCK_MONOTONIC_RAW, &tp) < 0) { + LogError("gettimeofday() failed"); + NBN_Abort(); + } + + endpoint->time = tp.tv_sec + (tp.tv_nsec / (double)1e9); +#endif // NBN_PLATFORM_WINDOWS +} + +// END OF ENDPOINT +// =================================================== + +/** + * ====== CLIENT ====== + */ + +static int Client_ProcessReceivedMessage(NBN_Client *, NBN_Message *, NBN_Connection *); +static NBN_Client_Event Client_HandleEvent(NBN_Client *); +static NBN_Client_Event Client_HandleMessageReceivedEvent(NBN_Client *); +static NBN_Connection *CreateServerConnection(NBN_Client *client, NBN_Driver_ID driver_id); + +NBN_Client *NBN_Client_Create(const char *protocol_name, const char *host, uint16_t port) { + NBN_Client *client = malloc(sizeof(NBN_Client)); + + client->config = (NBN_Client_Config){.protocol_name = protocol_name, .host = host, .port = port}; + client->client_data_writer.position = 0; + + client->endpoint.default_reliable_channel = NBN_Client_CreateChannel( + client, NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + client->endpoint.default_unreliable_channel = NBN_Client_CreateChannel( + client, NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + client->driver_data.webrtc.cfg = NBN_WEBRTC_DEFAULT_CONFIG; + client->driver_data.webrtc.is_connected = false; +#endif + + return client; +} + +uint8_t NBN_Client_CreateChannel(NBN_Client *client, NBN_Channel_Mode mode, unsigned int buffer_size, + unsigned int max_message_len) { + NBN_Client_Config *cfg = &client->config; + + NBN_Assert(cfg->channel_count < NBN_MAX_CHANNEL_COUNT); + + uint8_t channel_id = cfg->channel_count; + + cfg->channels[channel_id] = + (NBN_Channel_Config){.mode = mode, .buffer_size = buffer_size, .max_message_len = max_message_len}; + cfg->channel_count++; + + return channel_id; +} + +unsigned int NBN_Client_GetChannelCurrentCapacity(NBN_Client *client, uint8_t channel_id) { + NBN_Assert(channel_id < client->endpoint.channel_count); + + NBN_Channel *channel = &client->server_connection->channels[channel_id]; + + return channel->current_capacity; +} + +NBN_Writer *NBN_Client_WriteConnectionRequestData(NBN_Client *client) { + NBN_Writer_Init(&client->client_data_writer, client->endpoint.connection_request_data_buffer, + sizeof(client->endpoint.connection_request_data_buffer)); + + return &client->client_data_writer; +} + +static int StartClientDrivers(NBN_Client *client, const char *host, uint16_t port) { + int driver_count = 0; + +#ifdef NBN_UDP + client->server_connection = CreateServerConnection(client, NBN_DRIVER_UDP); + + if (nbn_udp_driver.impl.cli_start(client, host, port) < 0) { + LogError("Failed to start driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } + + LogInfo("%s driver started", nbn_udp_driver.name); + driver_count++; +#endif // NBN_UDP + +#ifdef NBN_WEBRTC_NATIVE + client->server_connection = CreateServerConnection(client, NBN_DRIVER_WEBRTC_NATIVE); + + if (nbn_webrtc_native_driver.impl.cli_start(client, host, port) < 0) { + LogError("Failed to start driver %s", nbn_webrtc_native_driver.name); + return NBN_ERROR; + } + + LogInfo("%s driver started", nbn_webrtc_native_driver.name); + driver_count++; +#endif // NBN_WEBRTC_NATIVE + +#ifdef __EMSCRIPTEN__ + client->server_connection = CreateServerConnection(client, NBN_DRIVER_WEBRTC_EMSCRIPTEN); + + if (nbn_webrtc_em_driver.impl.cli_start(client, host, port) < 0) { + LogError("Failed to start driver %s", nbn_webrtc_em_driver.name); + return NBN_ERROR; + } + + LogInfo("%s driver started", nbn_webrtc_em_driver.name); + driver_count++; +#endif // __EMSCRIPTEN__ + + client->server_connection->driver_data.endpoint_ptr = client; + + return driver_count; +} + +int NBN_Client_Start(NBN_Client *client) { + NBN_Client_Config config = client->config; + const char *protocol_name = config.protocol_name; + const char *host = config.host; + uint16_t port = config.port; + uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); + + Endpoint_Init(&client->endpoint, protocol_id, false, config.channels, config.channel_count); + + int driver_count = StartClientDrivers(client, host, port); + + if (driver_count < 1) { + LogError("At least one network driver has to be activated"); + NBN_Abort(); + } else if (driver_count > 1) { + LogError("Only one network driver can be activated for the client"); + NBN_Abort(); + } + + client->is_connected = false; + client->closed_code = -1; + + unsigned int connection_data_len = client->client_data_writer.position; + + NBN_Writer *writer = NBN_Client_CreateReliableMessage(client, NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + + if (!writer) { + return NBN_ERROR; + } + + if (connection_data_len > 0) { + NBN_Assert(connection_data_len <= sizeof(client->endpoint.connection_request_data_buffer)); + + NBN_Writer_WriteUInt32(writer, connection_data_len); + NBN_Writer_WriteBytes(writer, client->endpoint.connection_request_data_buffer, connection_data_len); + } else { + NBN_Writer_WriteUInt32(writer, 0); + } + + LogInfo("Started"); + + return 0; +} + +void NBN_Client_Stop(NBN_Client *client) { + // Poll remaining events to clear the event queue + while (NBN_Client_Poll(client) != NBN_CLIENT_NO_EVENT) { + } + + if (client->server_connection) { + if (!client->server_connection->is_closed && !client->server_connection->is_stale) { + LogInfo("Disconnecting..."); + + if (!NBN_Client_CreateReliableMessage(client, NBN_DISCONNECTION_MESSAGE_TYPE)) { + LogError("Failed to send disconnection message"); + } + + if (NBN_Client_Flush(client) < 0) { + LogError("Failed to send packets"); + } + + client->server_connection->is_closed = true; + + LogInfo("Disconnected"); + } + + Connection_Destroy(client->server_connection); + client->server_connection = NULL; + } + + LogInfo("Stopping all drivers..."); + +#ifdef NBN_UDP + nbn_udp_driver.impl.cli_stop(client); +#endif // NBN_UDP + +#ifdef NBN_WEBRTC_NATIVE + nbn_webrtc_native_driver.impl.cli_stop(client); +#endif // NBN_WEBRTC_NATIVE + +#ifdef __EMSCRIPTEN__ + nbn_webrtc_em_driver.impl.cli_stop(client); +#endif // __EMSCRIPTEN__ + + client->is_connected = false; + client->closed_code = -1; + client->endpoint.server_initial_data_len = 0; + + Endpoint_Deinit(&client->endpoint); + free(client); + + LogInfo("Stopped"); +} + +NBN_Reader *NBN_Client_ReadServerData(NBN_Client *client) { + NBN_Endpoint *endpoint = &client->endpoint; + + NBN_Reader_Init(&client->server_data_reader, endpoint->server_initial_data_buffer, + endpoint->server_initial_data_len); + + return &client->server_data_reader; +} + +static int ReadPacketsFromClientDrivers(NBN_Client *client) { +#ifdef NBN_UDP + if (nbn_udp_driver.impl.cli_recv_packets(client) < 0) { + LogError("Failed to read packets from driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } +#endif // NBN_UDP + +#ifdef NBN_WEBRTC_NATIVE + if (nbn_webrtc_native_driver.impl.cli_recv_packets(client) < 0) { + LogError("Failed to read packets from driver %s", nbn_webrtc_native_driver.name); + return NBN_ERROR; + } +#endif // NBN_WEBRTC_NATIVE + +#ifdef __EMSCRIPTEN__ + if (nbn_webrtc_em_driver.impl.cli_recv_packets(client) < 0) { + LogError("Failed to read packets from driver %s", nbn_webrtc_em_driver.name); + return NBN_ERROR; + } +#endif // __EMSCRIPTEN__ + + return 0; +} + +NBN_Client_Event NBN_Client_Poll(NBN_Client *client) { + NBN_Endpoint *endpoint = &client->endpoint; + + Endpoint_UpdateTime(endpoint); + + if (client->server_connection->is_stale) + return NBN_CLIENT_NO_EVENT; + + if (EventQueue_IsEmpty(&endpoint->event_queue)) { + if (Connection_CheckIfStale(client->server_connection, client->endpoint.time)) { + client->server_connection->is_stale = true; + client->is_connected = false; + + LogInfo("Server connection is stale. Disconnected."); + + NBN_Event e; + + e.type = NBN_CLIENT_DISCONNECTED; + e.data.connection = (NBN_Connection *)NULL; + + if (!EventQueue_Enqueue(&endpoint->event_queue, e)) + return NBN_ERROR; + } else { + if (ReadPacketsFromClientDrivers(client) < 0) { + return NBN_ERROR; + } + + NBN_Connection *server_conn = client->server_connection; + + for (unsigned int i = 0; i < endpoint->channel_count; i++) { + NBN_Channel *channel = &server_conn->channels[i]; + NBN_Message *msg; + + while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { + LogDebug("Got message %d of type %d from channel %d", msg->header.id, msg->header.type, + channel->id); + + if (Client_ProcessReceivedMessage(client, msg, server_conn) < 0) { + LogError("Failed to process received message"); + + return NBN_ERROR; + } + } + } + + Connection_UpdateAverageDownloadBandwidth(server_conn, client->endpoint.time); + + server_conn->last_read_packets_time = client->endpoint.time; + } + } + + bool ret = EventQueue_Dequeue(&endpoint->event_queue, &client->last_event); + + return ret ? Client_HandleEvent(client) : NBN_CLIENT_NO_EVENT; +} + +int NBN_Client_Flush(NBN_Client *client) { + return Connection_FlushChannels((NBN_Endpoint *)client, client->server_connection, client->endpoint.protocol_id, + client->endpoint.time); +} + +NBN_Writer *NBN_Client_CreateMessage(NBN_Client *client, uint8_t type, uint8_t channel_id) { + return Endpoint_CreateOutgoingMessage(&client->endpoint, client->server_connection, type, channel_id); +} + +NBN_Writer *NBN_Client_CreateReliableMessage(NBN_Client *client, uint8_t type) { + return NBN_Client_CreateMessage(client, type, client->endpoint.default_reliable_channel); +} + +NBN_Writer *NBN_Client_CreateUnreliableMessage(NBN_Client *client, uint8_t type) { + return NBN_Client_CreateMessage(client, type, client->endpoint.default_unreliable_channel); +} + +NBN_Reader *NBN_Client_ReadMessage(NBN_Client *client) { + NBN_Assert(client->last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); + + NBN_MessageInfo msg_info = client->last_event.data.message_info; + NBN_Reader *reader = &client->endpoint.message_reader; + + NBN_Reader_Init(reader, msg_info.data, msg_info.length); + + return reader; +} + +static NBN_Connection *CreateServerConnection(NBN_Client *client, NBN_Driver_ID driver_id) { + NBN_Connection *server_connection = Endpoint_CreateConnection(&client->endpoint, 0, driver_id); + + client->server_connection = server_connection; + + return server_connection; +} + +NBN_MessageInfo NBN_Client_GetMessageInfo(NBN_Client *client) { + NBN_Assert(client->last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); + + return client->last_event.data.message_info; +} + +NBN_ConnectionStats NBN_Client_GetStats(NBN_Client *client) { return client->server_connection->stats; } + +int NBN_Client_GetServerCloseCode(NBN_Client *client) { return client->closed_code; } + +bool NBN_Client_IsConnected(NBN_Client *client) { return client->is_connected; } + +static int Client_ProcessReceivedMessage(NBN_Client *client, NBN_Message *message, NBN_Connection *server_connection) { + NBN_Assert(client->server_connection == server_connection); + + NBN_Event ev; + + ev.type = NBN_CLIENT_MESSAGE_RECEIVED; + + NBN_MessageInfo msg_info; + + msg_info.type = message->header.type; + msg_info.channel_id = message->header.channel_id; + msg_info.length = message->header.length; + msg_info.sender = (NBN_ConnectionHandle *)server_connection; + msg_info.data = message->data; + + ev.data.message_info = msg_info; + + if (!EventQueue_Enqueue(&client->endpoint.event_queue, ev)) + return NBN_ERROR; + + return 0; +} + +static NBN_Client_Event Client_HandleEvent(NBN_Client *client) { + switch (client->last_event.type) { + case NBN_CLIENT_MESSAGE_RECEIVED: + return Client_HandleMessageReceivedEvent(client); + + default: + return client->last_event.type; + } +} + +static NBN_Client_Event Client_HandleMessageReceivedEvent(NBN_Client *client) { + NBN_MessageInfo message_info = client->last_event.data.message_info; + NBN_Endpoint *endpoint = &client->endpoint; + + int ret = NBN_CLIENT_NO_EVENT; + + if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) { + client->is_connected = false; + NBN_Reader *reader = NBN_Client_ReadMessage(client); + + if (NBN_Reader_ReadInt32(reader, &client->closed_code) < 0) { + LogError("Failed to read code from client closed message"); + + return NBN_ERROR; + } + + ret = NBN_CLIENT_DISCONNECTED; + } else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) { + if (message_info.length < 4) { + LogError("Accept message invalid length"); + + return NBN_ERROR; + } + + NBN_Reader *reader = NBN_Client_ReadMessage(client); + unsigned int data_length; + + if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { + LogError("Failed to read client data length"); + + return NBN_ERROR; + } + + if (data_length > 0) { + if (data_length > sizeof(endpoint->server_initial_data_buffer)) { + LogError("Received invalid connection data from the server"); + + return NBN_ERROR; + } + + if (NBN_Reader_ReadBytes(reader, endpoint->server_initial_data_buffer, data_length) < 0) { + LogError("Failed to read server data"); + + return NBN_ERROR; + } + } + + endpoint->server_initial_data_len = data_length; + client->is_connected = true; + ret = NBN_CLIENT_CONNECTED; + } else { + ret = NBN_CLIENT_MESSAGE_RECEIVED; + } + + return ret; +} + +static void ClientDriver_OnPacketReceived(NBN_Client *client, NBN_Packet *packet) { + if (Endpoint_ProcessReceivedPacket(&client->endpoint, packet, client->server_connection) < 0) { + // packets from the server should always be valid + LogError("Received invalid packet from server"); + NBN_Abort(); + } +} + +// END OF CLIENT +// =================================================== + +/** + * ====== SERVER ====== + */ + +static void Server_AddClient(NBN_Server *, NBN_Connection *); +static int Server_CloseClientWithCode(NBN_Server *, NBN_Connection *, int, bool); +static void Server_AddClientToClosedList(NBN_Server *, NBN_Connection *); +static int Server_ProcessReceivedMessage(NBN_Server *, NBN_Message *, NBN_Connection *); +static int Server_CloseStaleClientConnections(NBN_Server *); +static void Server_RemoveClosedClientConnections(NBN_Server *); +static bool Server_HandleEvent(NBN_Server *, NBN_Server_Event *); +static bool Server_HandleMessageReceivedEvent(NBN_Server *, NBN_Server_Event *); + +NBN_Server *NBN_Server_Create(const char *protocol_name, uint16_t port) { + NBN_Server *server = malloc(sizeof(NBN_Server)); + + server->config = (NBN_Server_Config){.protocol_name = protocol_name, .port = port}; + server->server_data_writer.position = 0; + server->clients = NULL; + + hmdefault(server->clients, NULL); + + server->endpoint.default_reliable_channel = NBN_Server_CreateChannel( + server, NBN_CHANNEL_RELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + server->endpoint.default_unreliable_channel = NBN_Server_CreateChannel( + server, NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_DEFAULT_BUFFER_SIZE, NBN_CHANNEL_DEFAULT_MAX_MESSAGE_SIZE); + +#if defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + server->driver_data.webrtc.ws_server = -1; + server->driver_data.webrtc.cfg = NBN_WEBRTC_DEFAULT_CONFIG; +#endif // defined(__EMSCRIPTEN__) || defined(NBN_WEBRTC_NATIVE) + + return server; +} + +uint8_t NBN_Server_CreateChannel(NBN_Server *server, NBN_Channel_Mode mode, unsigned int buffer_size, + unsigned int max_message_len) { + NBN_Server_Config *cfg = &server->config; + + NBN_Assert(cfg->channel_count < NBN_MAX_CHANNEL_COUNT); + + uint8_t channel_id = cfg->channel_count; + + cfg->channels[channel_id] = + (NBN_Channel_Config){.mode = mode, .buffer_size = buffer_size, .max_message_len = max_message_len}; + cfg->channel_count++; + + return channel_id; +} + +unsigned int NBN_Server_GetChannelCurrentCapacity(NBN_Server *server, uint8_t channel_id, NBN_ConnectionHandle *conn) { + NBN_Assert(channel_id < server->endpoint.channel_count); + + NBN_Channel *channel = &HANDLE_TO_CONN(conn)->channels[channel_id]; + + return channel->current_capacity; +} + +static int StartServerDrivers(NBN_Server *server, uint16_t port) { + int driver_count = 0; + +#ifdef NBN_UDP + if (nbn_udp_driver.impl.serv_start(server, port) < 0) { + LogError("Failed to start driver %s", nbn_udp_driver.name); + return NBN_ERROR; + } + + LogInfo("%s driver started", nbn_udp_driver.name); + driver_count++; +#endif // NBN_UDP + +#ifdef NBN_WEBRTC_NATIVE + if (nbn_webrtc_native_driver.impl.serv_start(server, port) < 0) { + LogError("Failed to start driver %s", nbn_webrtc_native_driver.name); + return NBN_ERROR; + } + + LogInfo("%s driver started", nbn_webrtc_native_driver.name); + driver_count++; +#endif // NBN_WEBRTC_NATIVE + +#ifdef __EMSCRIPTEN__ + if (nbn_webrtc_em_driver.impl.serv_start(server, port) < 0) { + LogError("Failed to start driver %s", nbn_webrtc_em_driver.name); + return NBN_ERROR; + } + + LogInfo("%s driver started", nbn_webrtc_em_driver.name); + driver_count++; +#endif // __EMSCRIPTEN__ + + return driver_count; +} + +int NBN_Server_Start(NBN_Server *server) { + NBN_Server_Config config = server->config; + const char *protocol_name = config.protocol_name; + uint16_t port = config.port; + uint32_t protocol_id = Endpoint_BuildProtocolId(protocol_name); + + Endpoint_Init(&server->endpoint, protocol_id, true, config.channels, config.channel_count); + + server->closed_clients_head = NULL; + + int driver_count = StartServerDrivers(server, port); + + if (driver_count < 1) { + LogError("At least one network driver has to be activated"); + NBN_Abort(); + } + + LogInfo("Started (channel count: %d)", server->endpoint.channel_count); + + return 0; +} + +void NBN_Server_Stop(NBN_Server *server) { + // Poll remaning events to clear the event queue + while (NBN_Server_Poll(server) != NBN_SERVER_NO_EVENT) { + } + + for (unsigned int i = 0; i < hmlen(server->clients); i++) { + NBN_Connection *conn = server->clients[i].value; + + conn->driver->impl.serv_cleanup_connection(server, conn); + Connection_Destroy(conn); + } + + hmfree(server->clients); + +#ifdef NBN_UDP + nbn_udp_driver.impl.serv_stop(server); +#endif // NBN_UDP + +#ifdef NBN_WEBRTC_NATIVE + nbn_webrtc_native_driver.impl.serv_stop(server); +#endif // NBN_WEBRTC_NATIVE + +#ifdef __EMSCRIPTEN__ + nbn_webrtc_em_driver.impl.serv_stop(server); +#endif // __EMSCRIPTEN__ + + // Free closed clients list + NBN_ConnectionListNode *current = server->closed_clients_head; + + while (current) { + NBN_ConnectionListNode *next = current->next; + + free(current); + + current = next; + } + + server->closed_clients_head = NULL; + Endpoint_Deinit(&server->endpoint); + free(server); + + LogInfo("Stopped"); +} + +static NBN_Connection_ID NBN_BuildConnectionHash(NBN_Connection_ID id, NBN_Driver_ID driver_id) { + NBN_Assert(id <= UINT64_MAX - 0xFF); + uint8_t driver_byte = driver_id; + + return ((NBN_Connection_ID)driver_byte << 56) | id; +} + +NBN_ConnectionHandle *NBN_Server_GetConnection(NBN_Server *server, NBN_Connection_ID id) { + return (NBN_ConnectionHandle *)hmget(server->clients, id); +} + +unsigned int NBN_Server_GetClientCount(NBN_Server *server) { return hmlen(server->clients); } + +NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Server *server, NBN_Client_Iterator *it) { + for (; *it < hmlen(server->clients);) { + NBN_Connection *conn = (NBN_Connection *)server->clients[*it].value; + + (*it)++; + + if (conn->is_accepted) { + return (NBN_ConnectionHandle *)conn; + } + } + + return NULL; +} + +static void ReadPacketsFromServerDrivers(NBN_Server *server) { +#ifdef NBN_UDP + if (nbn_udp_driver.impl.serv_recv_packets(server) < 0) { + LogError("Failed to read packets from driver %s", nbn_udp_driver.name); + } +#endif // NBN_UDP + +#ifdef NBN_WEBRTC_NATIVE + if (nbn_webrtc_native_driver.impl.serv_recv_packets(server) < 0) { + LogError("Failed to read packets from driver %s", nbn_webrtc_native_driver.name); + } +#endif // NBN_WEBRTC_NATIVE + +#ifdef __EMSCRIPTEN__ + if (nbn_webrtc_em_driver.impl.serv_recv_packets(server) < 0) { + LogError("Failed to read packets from driver %s", nbn_webrtc_em_driver.name); + } +#endif // __EMSCRIPTEN__ +} + +NBN_Server_Event NBN_Server_Poll(NBN_Server *server) { + Endpoint_UpdateTime(&server->endpoint); + + NBN_Endpoint *endpoint = &server->endpoint; + + if (EventQueue_IsEmpty(&endpoint->event_queue)) { + if (Server_CloseStaleClientConnections(server) < 0) + return NBN_ERROR; + + ReadPacketsFromServerDrivers(server); + + server->stats.download_bandwidth = 0; + + for (unsigned int i = 0; i < hmlen(server->clients); i++) { + NBN_Connection *client = server->clients[i].value; + + for (unsigned int i = 0; i < endpoint->channel_count; i++) { + NBN_Channel *channel = &client->channels[i]; + + if (channel) { + NBN_Message *msg; + + while ((msg = Channel_GetNextRecvedMessage(channel)) != NULL) { + if (Server_ProcessReceivedMessage(server, msg, client) < 0) { + LogError("Failed to process received message"); + + return NBN_ERROR; + } + } + } + } + + if (!client->is_closed) + Connection_UpdateAverageDownloadBandwidth(client, endpoint->time); + + server->stats.download_bandwidth += client->stats.download_bandwidth; + client->last_read_packets_time = endpoint->time; + } + + Server_RemoveClosedClientConnections(server); + } + + NBN_Server_Event ev; + + while (EventQueue_Dequeue(&endpoint->event_queue, &server->last_event)) { + if (Server_HandleEvent(server, &ev)) { + return ev; + } + } + + return NBN_SERVER_NO_EVENT; +} + +int NBN_Server_Flush(NBN_Server *server) { + server->stats.upload_bandwidth = 0; + + Server_RemoveClosedClientConnections(server); + + for (unsigned int i = 0; i < hmlen(server->clients); i++) { + NBN_Connection *client = server->clients[i].value; + + NBN_Assert(!(client->is_closed && client->is_stale)); + + if (!client->is_stale && Connection_FlushChannels((NBN_Endpoint *)server, client, server->endpoint.protocol_id, + server->endpoint.time) < 0) { + return NBN_ERROR; + } + + server->stats.upload_bandwidth += client->stats.upload_bandwidth; + } + + return 0; +} + +static NBN_Connection *CreateClientConnection(NBN_Server *server, NBN_Driver_ID driver_id, NBN_Connection_ID conn_id) { + // write the driver ID to the first byte of the connection ID to avoid collisions between drivers + conn_id = NBN_BuildConnectionHash(conn_id, driver_id); + NBN_Connection *client = Endpoint_CreateConnection(&server->endpoint, conn_id, driver_id); + + return client; +} + +int NBN_Server_CloseClientWithCode(NBN_Server *server, NBN_ConnectionHandle *conn, int code) { + return Server_CloseClientWithCode(server, HANDLE_TO_CONN(conn), code, false); +} + +int NBN_Server_CloseClient(NBN_Server *server, NBN_ConnectionHandle *conn) { + return Server_CloseClientWithCode(server, HANDLE_TO_CONN(conn), -1, false); +} + +NBN_Writer *NBN_Server_CreateMessage(NBN_Server *server, uint8_t type, uint8_t channel_id, + NBN_ConnectionHandle *receiver) { + NBN_Connection *conn = HANDLE_TO_CONN(receiver); + + NBN_Assert(conn->is_accepted || type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); + + NBN_Writer *writer = Endpoint_CreateOutgoingMessage(&server->endpoint, conn, type, channel_id); + + if (!writer) { + LogError("Failed to create outgoing message for client %lld", receiver->id); + + /* Do not close the client if we failed to send the close client message to avoid infinite loops */ + if (type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) { + Server_CloseClientWithCode(server, conn, -1, false); + + return NULL; + } + } + + return writer; +} + +NBN_Writer *NBN_Server_CreateReliableMessage(NBN_Server *server, uint8_t type, NBN_ConnectionHandle *receiver) { + return NBN_Server_CreateMessage(server, type, server->endpoint.default_reliable_channel, receiver); +} + +NBN_Writer *NBN_Server_CreateUnreliableMessage(NBN_Server *server, uint8_t type, NBN_ConnectionHandle *receiver) { + return NBN_Server_CreateMessage(server, type, server->endpoint.default_unreliable_channel, receiver); +} + +NBN_Reader *NBN_Server_ReadMessage(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); + + NBN_MessageInfo msg_info = server->last_event.data.message_info; + NBN_Reader *reader = &server->endpoint.message_reader; + + NBN_Reader_Init(reader, msg_info.data, msg_info.length); + + return reader; +} + +NBN_Writer *NBN_Server_WriteConnectionData(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); + NBN_Assert(server->last_event.data.connection != NULL); + + NBN_Endpoint *endpoint = &server->endpoint; + + NBN_Writer_Init(&server->server_data_writer, endpoint->server_initial_data_buffer, + sizeof(endpoint->server_initial_data_buffer)); + + return &server->server_data_writer; +} + +int NBN_Server_AcceptIncomingConnection(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); + NBN_Assert(server->last_event.data.connection != NULL); + + unsigned data_length = server->server_data_writer.position; + NBN_Connection *client = server->last_event.data.connection; + NBN_Writer *writer = + NBN_Server_CreateReliableMessage(server, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); + + if (!writer) { + return NBN_ERROR; + } + + if (data_length > 0) { + NBN_Assert(data_length <= sizeof(server->endpoint.server_initial_data_buffer)); + + NBN_Writer_WriteUInt32(writer, data_length); + NBN_Writer_WriteBytes(writer, server->endpoint.server_initial_data_buffer, data_length); + } else { + NBN_Writer_WriteUInt32(writer, 0); + } + + client->is_accepted = true; + + LogInfo("Client %lld has been accepted into the server", client->handle.id); + + return 0; +} + +int NBN_Server_RejectIncomingConnectionWithCode(NBN_Server *server, int code) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); + NBN_Assert(server->last_event.data.connection != NULL); + + NBN_Connection *conn = server->last_event.data.connection; + LogDebug("Rejecting incoming connection %lld (code: %d)", conn->handle.id, code); + + return Server_CloseClientWithCode(server, conn, code, false); +} + +int NBN_Server_RejectIncomingConnection(NBN_Server *server) { + return NBN_Server_RejectIncomingConnectionWithCode(server, -1); +} + +NBN_ConnectionHandle *NBN_Server_GetIncomingConnection(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); + NBN_Assert(server->last_event.data.connection != NULL); + + return (NBN_ConnectionHandle *)server->last_event.data.connection; +} + +NBN_Reader *NBN_Server_ReadConnectionRequestData(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_NEW_CONNECTION); + + NBN_Endpoint *endpoint = &server->endpoint; + + NBN_Reader_Init(&server->client_data_reader, endpoint->connection_request_data_buffer, + endpoint->client_connection_request_data_len); + + return &server->client_data_reader; +} + +NBN_DisconnectionInfo NBN_Server_GetDisconnectionInfo(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_DISCONNECTION); + + return server->last_event.data.disconnection; +} + +NBN_MessageInfo NBN_Server_GetMessageInfo(NBN_Server *server) { + NBN_Assert(server->last_event.type == NBN_SERVER_MESSAGE_RECEIVED); + + return server->last_event.data.message_info; +} + +NBN_ServerStats NBN_Server_GetStats(NBN_Server *server) { return server->stats; } + +static void Server_AddClient(NBN_Server *server, NBN_Connection *client) { + NBN_Assert(hmgeti(server->clients, client->handle.id) == -1); + + hmput(server->clients, client->handle.id, client); + LogDebug("New client %lld", client->handle.id); +} + +static int Server_CloseClientWithCode(NBN_Server *server, NBN_Connection *client, int code, bool disconnection) { + if (!client->is_closed && client->is_accepted) { + if (!disconnection) { + NBN_Event e; + + e.type = NBN_CLIENT_DISCONNECTED; + e.data.disconnection = (NBN_DisconnectionInfo){client->handle.id, client->handle.user_data}; + + if (!EventQueue_Enqueue(&server->endpoint.event_queue, e)) + return NBN_ERROR; + } + } + + if (client->is_stale) { + LogDebug("Closing stale connection %lld", client->handle.id); + + Server_AddClientToClosedList(server, client); + client->is_closed = true; + + return 0; + } + + LogDebug("Closing active connection %lld (will send a disconnection message)", client->handle.id); + + Server_AddClientToClosedList(server, client); + client->is_closed = true; + + if (!disconnection) { + LogDebug("Send close message for client %lld (code: %d)", client->handle.id, code); + + NBN_Writer *writer = + NBN_Server_CreateReliableMessage(server, NBN_CLIENT_CLOSED_MESSAGE_TYPE, (NBN_ConnectionHandle *)client); + + if (!writer) { + return NBN_ERROR; + } + + NBN_Writer_WriteInt32(writer, code); + } + + return 0; +} + +static void Server_AddClientToClosedList(NBN_Server *server, NBN_Connection *client) { + if (client->is_closed) + return; + + // TODO: do we need to use a linked list, maybe use stb dynamic array? + NBN_ConnectionListNode *node = (NBN_ConnectionListNode *)malloc(sizeof(NBN_ConnectionListNode)); + + node->conn = client; + node->next = NULL; + + if (server->closed_clients_head == NULL) { + // list is empty + server->closed_clients_head = node; + node->prev = NULL; + } else { + // list is not empty, add node at the end + NBN_ConnectionListNode *tail = server->closed_clients_head; + + while (tail->next != NULL) + tail = tail->next; + + node->prev = tail; + tail->next = node; + } +} + +static int Server_ProcessReceivedMessage(NBN_Server *server, NBN_Message *message, NBN_Connection *client) { + NBN_Event ev; + + ev.type = NBN_CLIENT_MESSAGE_RECEIVED; + + NBN_MessageInfo msg_info; + + msg_info.type = message->header.type; + msg_info.channel_id = message->header.channel_id; + msg_info.length = message->header.length; + msg_info.sender = (NBN_ConnectionHandle *)client; + msg_info.data = message->data; + + LogDebug("Received message (type: %d, id: %d) from client %lld", message->header.type, message->header.id, + client->handle.id); + ev.data.message_info = msg_info; + + if (!EventQueue_Enqueue(&server->endpoint.event_queue, ev)) + return NBN_ERROR; + + return 0; +} + +static int Server_CloseStaleClientConnections(NBN_Server *server) { + for (unsigned int i = 0; i < hmlen(server->clients); i++) { + NBN_Connection *client = server->clients[i].value; + + if (!client->is_stale && Connection_CheckIfStale(client, server->endpoint.time)) { + LogInfo("Client %lld connection is stale, closing it.", client->handle.id); + + client->is_stale = true; + + if (Server_CloseClientWithCode(server, client, -1, false) < 0) + return NBN_ERROR; + } + } + + return 0; +} + +static void Server_RemoveClosedClientConnections(NBN_Server *server) { + NBN_ConnectionListNode *current = server->closed_clients_head; + + while (current) { + NBN_ConnectionListNode *prev = current->prev; + NBN_ConnectionListNode *next = current->next; + NBN_Connection *client = current->conn; + + NBN_Assert(client->handle.id > 0); + + if (client->is_stale) { + LogDebug("Remove closed client connection (ID: %lld)", client->handle.id); + + // Notify the driver to clean up the connection + client->driver->impl.serv_cleanup_connection(server, client); + + int ret = hmdel(server->clients, client->handle.id); + NBN_Assert(ret == 1); + + // Destroy the connection + + Connection_Destroy(client); + + // Remove the connection from the closed clients list + + free(current); + + if (current == server->closed_clients_head) { + // delete the head of the list + NBN_ConnectionListNode *new_head = next; + + if (new_head) { + new_head->prev = NULL; + } + + server->closed_clients_head = new_head; + } else { + // delete a node in the middle of the list + prev->next = next; + + if (next) + next->prev = prev; + } + } + + current = next; + } +} + +static bool Server_HandleEvent(NBN_Server *server, NBN_Server_Event *ev) { + if (server->last_event.type == NBN_SERVER_MESSAGE_RECEIVED) { + return Server_HandleMessageReceivedEvent(server, ev); + } + + *ev = server->last_event.type; + return true; +} + +// TODO: big ass function +static bool Server_HandleMessageReceivedEvent(NBN_Server *server, NBN_Server_Event *ev) { + NBN_Event *last_event = &server->last_event; + NBN_MessageInfo message_info = last_event->data.message_info; + NBN_Connection *sender = HANDLE_TO_CONN(message_info.sender); + NBN_Endpoint *endpoint = &server->endpoint; + + if (sender->is_closed || sender->is_stale) { + return false; + } + + if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) { + LogInfo("Received a disconnection request from client %lld (user_data: %p)", sender->handle.id, + sender->handle.user_data); + + if (Server_CloseClientWithCode(server, sender, -1, true) < 0) { + *ev = NBN_ERROR; + return true; + } + + sender->is_stale = true; + + last_event->type = NBN_SERVER_DISCONNECTION; + last_event->data.disconnection = (NBN_DisconnectionInfo){sender->handle.id, sender->handle.user_data}; + + Server_RemoveClosedClientConnections(server); + + *ev = NBN_SERVER_DISCONNECTION; + return true; + } + + if (message_info.type != NBN_CONNECTION_REQUEST_MESSAGE_TYPE) { + server->server_data_writer.position = 0; + + *ev = NBN_SERVER_MESSAGE_RECEIVED; + return true; + } + + // at this point we know it's a connection request + NBN_Assert(message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE); + + LogDebug("Received a connection request from client %lld", sender->handle.id); + + if (message_info.length < 4) { + LogError("Connection request invalid length"); + + *ev = NBN_ERROR; + return true; + } + + NBN_Reader *reader = NBN_Server_ReadMessage(server); + unsigned int data_length; + + if (NBN_Reader_ReadUInt32(reader, &data_length) < 0) { + LogError("Failed to read client data length"); + + *ev = NBN_ERROR; + return true; + } + + if (data_length > 0) { + if (data_length > sizeof(endpoint->connection_request_data_buffer)) { + LogError("Received invalid connection request data"); + + *ev = NBN_ERROR; + return true; + } + + if (NBN_Reader_ReadBytes(reader, server->endpoint.connection_request_data_buffer, data_length) < 0) { + LogError("Failed to read client data"); + + *ev = NBN_ERROR; + return true; + } + } + + server->endpoint.client_connection_request_data_len = data_length; + + NBN_Event e; + + e.type = NBN_SERVER_NEW_CONNECTION; + e.data.connection = sender; + + if (!EventQueue_Enqueue(&endpoint->event_queue, e)) { + *ev = NBN_ERROR; + return true; + } + + *ev = NBN_SERVER_NO_EVENT; + return true; +} + +static void ServerDriver_OnClientConnected(NBN_Server *server, NBN_Connection *client) { + Server_AddClient(server, client); +} + +static int ServerDriver_OnClientPacketReceived(NBN_Server *server, NBN_Packet *packet) { + if (Endpoint_ProcessReceivedPacket(&server->endpoint, packet, packet->sender) < 0) { + LogError("An error occured while processing packet from client %d, closing the client", + packet->sender->handle.id); + + return Server_CloseClientWithCode(server, packet->sender, -1, false); + } + + return 0; +} + +// END OF SERVER +// =================================================== + +/** + * ====== UDP DRIVER ====== * + */ + +#ifdef NBN_UDP + +#ifdef NBN_PLATFORM_WINDOWS + +static char err_msg[32]; + +#endif + +static SOCKET UDP_InitSocket(void); +static void UDP_DeinitSocket(int); +static int UDP_BindSocket(int, uint16_t); +static char *UDP_GetLastErrorMessage(void); + +static SOCKET UDP_InitSocket(void) { +#ifdef NBN_PLATFORM_WINDOWS + WSADATA wsa; + int err = WSAStartup(MAKEWORD(2, 2), &wsa); + if (err < 0) { + LogError("WSAStartup() failed"); + + return NBN_ERROR; + } +#endif + + SOCKET sock; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) + return NBN_ERROR; + +#if defined(NBN_PLATFORM_WINDOWS) + DWORD non_blocking = 1; + + if (ioctlsocket(nbn_udp_sock, FIONBIO, &non_blocking) != 0) { + LogError("ioctlsocket() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } +#elif defined(NBN_PLATFORM_MAC) || defined(NBN_PLATFORM_UNIX) + int non_blocking = 1; + + if (fcntl(sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) { + LogError("fcntl() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } +#endif + + return sock; +} + +static void UDP_DeinitSocket(SOCKET sock) { + closesocket(sock); + +#ifdef NBN_PLATFORM_WINDOWS + WSACleanup(); +#endif +} + +static int UDP_BindSocket(SOCKET sock, uint16_t port) { + SOCKADDR_IN sin; + + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + + if (bind(sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) { + LogError("bind() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } + + return 0; +} + +static NBN_Connection_ID UDP_BuildConnectionID(NBN_IPAddress address) { + return ((NBN_Connection_ID)address.host << 2) | address.port; +} + +static NBN_Connection *UDP_FindOrCreateClientConnectionByAddress(NBN_Server *server, NBN_IPAddress address) { + NBN_Connection_ID conn_id = UDP_BuildConnectionID(address); + conn_id = NBN_BuildConnectionHash(conn_id, NBN_DRIVER_UDP); + NBN_ConnectionHandle *handle = NBN_Server_GetConnection(server, conn_id); + + if (handle) { + return HANDLE_TO_CONN(handle); + } + + // this is a new connection + NBN_Connection *conn = CreateClientConnection(server, NBN_DRIVER_UDP, conn_id); + conn->driver_data.udp.ip_address = address; + conn->driver_data.endpoint_ptr = server; + + LogInfo("New UDP connection (id: %lld, addr: %d, port: %d)", conn->handle.id, address.host, address.port); + + ServerDriver_OnClientConnected(server, conn); + return conn; +} + +#define MAX_IP_ADDR_LEN 15 + +static void UDP_ParseIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) { + uint8_t arr[4]; + char *dup_host = strndup(host, MAX_IP_ADDR_LEN + 1); + + char *s; + int i = 0; + + while ((s = strsep(&dup_host, ".")) != NULL && i < 4) { + char *end = NULL; + int v = strtol(s, &end, 10); + + if (*end != '\0' || v < 0 || v > 255) { + LogError("Invalid IP address: %s", host); + NBN_Abort(); + } + + arr[i++] = (uint8_t)v; + } + + free(dup_host); + + address->host = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]; + address->port = port; +} + +static char *UDP_GetLastErrorMessage(void) { +#ifdef NBN_PLATFORM_WINDOWS + snprintf(err_msg, sizeof(err_msg), "%d", WSAGetLastError()); + + return err_msg; +#else + return strerror(errno); +#endif +} + +static int UDP_Server_Start(NBN_Server *server, uint16_t port) { + if ((server->driver_data.udp.sock = UDP_InitSocket()) < 0) + return NBN_ERROR; + + if (UDP_BindSocket(server->driver_data.udp.sock, port) < 0) + return NBN_ERROR; + + return 0; +} + +static void UDP_Server_Stop(NBN_Server *server) { UDP_DeinitSocket(server->driver_data.udp.sock); } + +static int UDP_Server_RecvPackets(NBN_Server *server) { + NBN_Packet *packet = &server->endpoint.read_packet; + SOCKADDR_IN src_addr; + socklen_t src_addr_len = sizeof(src_addr); + + while (true) { + int bytes = recvfrom(server->driver_data.udp.sock, (char *)packet->buffer, sizeof(packet->buffer), 0, + (SOCKADDR *)&src_addr, &src_addr_len); + + if (bytes <= 0) + break; + + if (bytes < NBN_PACKET_HEADER_SIZE) + continue; + + if (Packet_InitRead(packet, server->endpoint.protocol_id, bytes) < 0) { + LogDebug("Discarded invalid packet"); + continue; + } + + NBN_IPAddress ip_address; + ip_address.host = ntohl(src_addr.sin_addr.s_addr); + ip_address.port = ntohs(src_addr.sin_port); + + LogDebug("Received valid UDP packet from %d:%d", ip_address.host, ip_address.port); + + packet->sender = UDP_FindOrCreateClientConnectionByAddress(server, ip_address); + + ServerDriver_OnClientPacketReceived(server, packet); + } + + return 0; +} + +static void UDP_Server_CleanupConnection(NBN_Server *server, NBN_Connection *connection) { + (void)server; + (void)connection; +} + +static int UDP_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *connection) { + NBN_IPAddress dest_address = connection->driver_data.udp.ip_address; + SOCKADDR_IN dest_addr; + + dest_addr.sin_addr.s_addr = htonl(dest_address.host); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(dest_address.port); + + if (sendto(server->driver_data.udp.sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + sizeof(dest_addr)) == SOCKET_ERROR) { + LogError("sendto() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } + + return 0; +} + +static int UDP_Client_Start(NBN_Client *client, const char *host, uint16_t port) { + NBN_IPAddress *ip_address = &client->server_connection->driver_data.udp.ip_address; + + UDP_ParseIpAddress(host, port, ip_address); + + if ((client->driver_data.udp.sock = UDP_InitSocket()) < 0) + return NBN_ERROR; + + if (UDP_BindSocket(client->driver_data.udp.sock, 0) < 0) + return NBN_ERROR; + + return 0; +} + +static void UDP_Client_Stop(NBN_Client *client) { UDP_DeinitSocket(client->driver_data.udp.sock); } + +static int UDP_Client_RecvPackets(NBN_Client *client) { + NBN_IPAddress server_address = client->server_connection->driver_data.udp.ip_address; + NBN_Packet *packet = &client->endpoint.read_packet; + SOCKADDR_IN src_addr; + socklen_t src_addr_len = sizeof(src_addr); + + while (true) { + int bytes = recvfrom(client->driver_data.udp.sock, (char *)packet->buffer, sizeof(packet->buffer), 0, + (SOCKADDR *)&src_addr, &src_addr_len); + + if (bytes <= 0) + break; + + if (bytes < NBN_PACKET_HEADER_SIZE) + continue; + + uint32_t host = ntohl(src_addr.sin_addr.s_addr); + uint16_t port = ntohs(src_addr.sin_port); + + if (host != server_address.host || port != server_address.port) + continue; + + if (Packet_InitRead(packet, client->endpoint.protocol_id, bytes) < 0) { + LogDebug("Discarded invalid packet"); + continue; + } + + packet->sender = client->server_connection; + + ClientDriver_OnPacketReceived(client, packet); + } + + return 0; +} + +static int UDP_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection) { + NBN_IPAddress server_address = connection->driver_data.udp.ip_address; + SOCKADDR_IN dest_addr; + + dest_addr.sin_addr.s_addr = htonl(server_address.host); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(server_address.port); + + if (sendto(client->driver_data.udp.sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, + sizeof(dest_addr)) == SOCKET_ERROR) { + LogError("sendto() failed: %s", UDP_GetLastErrorMessage()); + + return NBN_ERROR; + } + + return 0; +} + +#endif // NBN_UDP + +// END OF UDP DRIVER +// =================================================== + +/** + * ====== WEBRTC EMSCRIPTEN DRIVER ====== * + */ + +#ifdef __EMSCRIPTEN__ + +/** + * JS API + * + * See webrtc/js folder for the implementation of these functions. + */ + +extern void __js_game_server_init(uint32_t, bool, const char *, const char *); +extern int __js_game_server_start(uint16_t); +extern int __js_game_server_dequeue_packet(uint32_t *, uint8_t *); +extern int __js_game_server_send_packet_to(uint8_t *, unsigned int, uint32_t); +extern void __js_game_server_close_client_peer(unsigned int); +extern void __js_game_server_stop(void); + +extern void __js_game_client_init(uint32_t, bool); +extern int __js_game_client_start(const char *, uint16_t); +extern int __js_game_client_dequeue_packet(uint8_t *); +extern int __js_game_client_send_packet(uint8_t *, unsigned int); +extern void __js_game_client_close(void); + +void NBN_Client_SetWebRTC_Config(NBN_Client *client, NBN_WebRTC_Config config) { + client->driver_data.webrtc.cfg = config; +} + +void NBN_Server_SetWebRTC_Config(NBN_Server *server, NBN_WebRTC_Config config) { + server->driver_data.webrtc.cfg = config; +} + +static int WebRTC_Server_Start(NBN_Server *server, uint16_t port) { + NBN_WebRTC_Config cfg = server->driver_data.webrtc.cfg; + + __js_game_server_init(server->endpoint.protocol_id, cfg.enable_tls, cfg.key_path, cfg.cert_path); + + if (__js_game_server_start(port) < 0) + return -1; + + return 0; +} + +static void WebRTC_Server_Stop(NBN_Server *server) { + (void)server; + + __js_game_server_stop(); +} + +static int WebRTC_Server_RecvPackets(NBN_Server *server) { + NBN_Packet *packet = &server->endpoint.read_packet; + uint32_t peer_id; + unsigned int len; + + while ((len = __js_game_server_dequeue_packet(&peer_id, packet->buffer)) > 0) { + NBN_Connection_ID conn_id = NBN_BuildConnectionHash(peer_id, NBN_DRIVER_WEBRTC_EMSCRIPTEN); + NBN_ConnectionHandle *handle = NBN_Server_GetConnection(server, conn_id); + NBN_Connection *conn = NULL; + + if (handle == NULL) { + LogInfo("Peer %d has connected", peer_id); + + conn = CreateClientConnection(server, NBN_DRIVER_WEBRTC_EMSCRIPTEN, conn_id); + conn->driver_data.webrtc.peer_id = peer_id; + conn->driver_data.endpoint_ptr = server; + + ServerDriver_OnClientConnected(server, conn); + } else { + conn = HANDLE_TO_CONN(handle); + } + + if (Packet_InitRead(packet, server->endpoint.protocol_id, len) < 0) + continue; + + packet->sender = conn; + + ServerDriver_OnClientPacketReceived(server, packet); + } + + return 0; +} + +static void WebRTC_Server_CleanupConnection(NBN_Server *server, NBN_Connection *conn) { + (void)server; + + NBN_Assert(conn != NULL); + __js_game_server_close_client_peer(conn->driver_data.webrtc.peer_id); +} + +static int WebRTC_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *conn) { + (void)server; + + return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->driver_data.webrtc.peer_id); +} + +static int WebRTC_Client_Start(NBN_Client *client, const char *host, uint16_t port) { + NBN_WebRTC_Config cfg = client->driver_data.webrtc.cfg; + + __js_game_client_init(client->endpoint.protocol_id, cfg.enable_tls); + + int res; + + if ((res = __js_game_client_start(host, port)) < 0) + return NBN_ERROR; + + return 0; +} + +static void WebRTC_Client_Stop(NBN_Client *client) { + (void)client; + + __js_game_client_close(); +} + +static int WebRTC_Client_RecvPackets(NBN_Client *client) { + NBN_Packet *packet = &client->endpoint.read_packet; + unsigned int len; + + while ((len = __js_game_client_dequeue_packet(packet->buffer)) > 0) { + if (Packet_InitRead(packet, client->endpoint.protocol_id, len) < 0) + continue; + + packet->sender = client->server_connection; + + ClientDriver_OnPacketReceived(client, packet); + } + + return 0; +} + +static int WebRTC_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection) { + (void)client; + (void)connection; + + return __js_game_client_send_packet(packet->buffer, packet->size); +} + +#endif // __EMSCRIPTEN__ + +// END OF WEBRTC EMSCRIPTEN DRIVER +// =================================================== + +/** + * ====== WEBRTC NATIVE DRIVER ====== * + * + * WARNING: libdatachannel callbacks can be triggered from different threads. + * Beware of race conditions in those callbacks. + * The callbacks used on the server start with WS_Server_ + * The callbacks used on the client start with WS_Client_ + */ + +#ifdef NBN_WEBRTC_NATIVE + +#include "json.h" + +void NBN_Server_SetWebRTC_Config(NBN_Server *server, NBN_WebRTC_Config config) { + server->driver_data.webrtc.cfg = config; +} + +void NBN_Client_SetWebRTC_Config(NBN_Client *client, NBN_WebRTC_Config config) { + client->driver_data.webrtc.cfg = config; +} + +static void WS_OnError(int ws, const char *err_msg, void *user_ptr) { + (void)user_ptr; + + LogError("Error on WS %d: %s", ws, err_msg); +} + +static void WebRTC_Native_Log(rtcLogLevel level, const char *msg) { + switch (level) { + case RTC_LOG_FATAL: + case RTC_LOG_ERROR: + LogError("%s", msg); + break; + + case RTC_LOG_WARNING: + LogWarning("%s", msg); + break; + + case RTC_LOG_INFO: + LogInfo("%s", msg); + break; + + case RTC_LOG_DEBUG: + LogDebug("%s", msg); + break; + + case RTC_LOG_VERBOSE: + LogDebug("%s", msg); + break; + + case RTC_LOG_NONE: + break; + } +} + +static char *ParseSignalingMessage(const char *msg, size_t msg_len, const char *type) { + char *sdp = NULL; + struct json_value_s *root = json_parse(msg, msg_len); // this has to be freed + struct json_object_s *object = (struct json_object_s *)root->payload; + struct json_object_element_s *curr = object->start; + + if (root->type != json_type_object) { + LogDebug("Received an invalid signaling message: %s", msg); + goto leave_free_root; + } + + while (curr != NULL) { + if (strncmp(curr->name->string, "type", 4) == 0) { + struct json_string_s *str = json_value_as_string(curr->value); + + if (strncmp(str->string, type, str->string_size)) { + // unexpected type + LogDebug("Received a signaling message with an unexpected type: %s (expected: %s)", str->string, type); + sdp = NULL; + goto leave_free_root; + } + } else if (strncmp(curr->name->string, "sdp", 3) == 0) { + struct json_string_s *str = json_value_as_string(curr->value); + + if (str) { + size_t len = strnlen(str->string, str->string_size); + + sdp = (char *)malloc(len + 1); + memcpy(sdp, str->string, len + 1); + } + } + + curr = curr->next; + } + +leave_free_root: + free(root); + + return sdp; +} + +static void ProcessSignalingMessage(NBN_WebRTC_Peer_ID peer_id, int ws, const char *msg, int size, const char *type) { + // for some reason the size of the message is negative + // in libdatachannel documentation (https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md) there + // is mention of: size: if size >= 0, data is interpreted as a binary message of length size, otherwise it is + // interpreted as a null-terminated UTF-8 string. so I guess in this case msg is a null terminated string? I + // could not find more information about this so I decided to go with flipping the size to positive even though + // it feels weird, but it works so... ¯\_(ツ)_/¯ + + if (size < 0) + size *= -1; + size -= 1; + + LogDebug("Received signaling message on WS %d (size: %d): %s", ws, size, msg); + + char *sdp = ParseSignalingMessage(msg, size, type); + + if (!sdp) { + LogWarning("Failed to parse signaling data for WS %d", ws); + return; + } + + LogDebug("Successfully parsed signaling payload (sdp: %s)", sdp); + + int ret = rtcSetRemoteDescription(peer_id, sdp, type); + + if (ret < 0) { + LogError("Failed to set remote description for peer %d (WS: %d): %d", peer_id, ws, ret); + rtcClose(ws); + } + + // IMPORTANT: not sure I can free this because it's passed to rtcSetRemoteDescription + free(sdp); +} + +char *String_ReplaceAll(const char *str, const char *orig, const char *stub) { + const char *s = str; + size_t str_len = strlen(str); + size_t orig_len = strlen(orig); + size_t stub_len = strlen(stub); + size_t occurences = 0; + + while ((s = strstr(s, orig)) != NULL) { + s += orig_len; + occurences++; + } + + size_t res_len = str_len - (occurences * orig_len) + (occurences * stub_len); + char *res = malloc(res_len + 1); + size_t res_offset = 0; + + while ((s = strstr(str, orig)) != NULL) { + size_t len = s - str; + + memcpy(res + res_offset, str, len); + res_offset += len; + memcpy(res + res_offset, stub, stub_len); + res_offset += stub_len; + str = s + orig_len; + } + + res[res_len] = 0; + + return res; +} + +static int ProcessLocalDescription(int ws, const char *sdp, const char *type) { + char *escaped_sdp = String_ReplaceAll(sdp, "\r\n", "\\r\\n"); + size_t signaling_json_size = snprintf(NULL, 0, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp) + 1; + char *signaling_json = (char *)malloc(signaling_json_size); + snprintf(signaling_json, signaling_json_size, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp); + + LogDebug("Send signaling message of type %s to remote connection: %s", type, signaling_json); + + int ret = 0; + + // pass -1 as the size (assume signaling_json to be a null-terminated string) + if (rtcSendMessage(ws, signaling_json, -1) < 0) { + ret = NBN_ERROR; + } + + free(signaling_json); + free(escaped_sdp); + + return ret; +} + +static void ClosePeer(NBN_Connection *conn) { + int channel_id = conn->driver_data.webrtc.channel_id; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + int ws = conn->driver_data.webrtc.ws; + + LogDebug("Closing peer %d (ws: %d)", peer_id, ws); + + if (channel_id >= 0) { + rtcDeleteDataChannel(channel_id); + } + + rtcDeletePeerConnection(peer_id); + rtcDelete(ws); +} + +static void WS_Server_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { + (void)pc; + + LogDebug("Processing local description of type '%s'", type); + + if (strncmp(type, "answer", strlen("answer")) != 0) { + LogWarning("Ignoring local description of type '%s' (expected 'answer')", type); + return; + } + + NBN_Connection *conn = (NBN_Connection *)user_ptr; + int ws = conn->driver_data.webrtc.ws; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + + if (ProcessLocalDescription(ws, sdp, "answer") < 0) { + LogError("Failed to process local description for peer %d, closing peer", peer_id); + ClosePeer(conn); + } +} + +static void WS_Server_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { + LogDebug("Peer %d state changed to %d", pc, state); + + if (state == RTC_CONNECTED) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + NBN_Server *server = (NBN_Server *)conn->driver_data.endpoint_ptr; + + LogDebug("Peer %d is connected !", pc); + ServerDriver_OnClientConnected(server, conn); + } +} + +static int CreatePeer(NBN_WebRTC_Peer_ID *peer_id, int *channel_id, NBN_WebRTC_Config cfg, + rtcDescriptionCallbackFunc on_rtc_description_cb, rtcStateChangeCallbackFunc state_changed_cb) { + rtcConfiguration rtcCfg = { + .iceServers = cfg.ice_servers, .iceServersCount = (int)cfg.ice_servers_count, .disableAutoNegotiation = false}; + *peer_id = rtcCreatePeerConnection(&rtcCfg); + + if (*peer_id < 0) { + LogError("Failed to create peer: %d", *peer_id); + return NBN_ERROR; + } + + int ret = rtcSetLocalDescriptionCallback(*peer_id, on_rtc_description_cb); + + if (ret < 0) { + LogError("Failed to register local description callback for peer %d: %d", *peer_id, ret); + return NBN_ERROR; + } + + ret = rtcSetStateChangeCallback(*peer_id, state_changed_cb); + + if (ret < 0) { + LogError("Failed to register state change callback for peer %d: %d", *peer_id, ret); + return NBN_ERROR; + } + rtcDataChannelInit rtcDataChannel = { + .reliability = {.unordered = true, .unreliable = true, .maxPacketLifeTime = 1000, .maxRetransmits = 0}, + .negotiated = true, + .manualStream = true, + .stream = 0}; + *channel_id = rtcCreateDataChannelEx(*peer_id, "unreliable", &rtcDataChannel); + + if (*channel_id < 0) { + LogError("Failed to create data channel for peer %d: %d", *peer_id, *channel_id); + return NBN_ERROR; + } + + LogDebug("Successfully created data channel for peer %d: %d", *peer_id, *channel_id); + + return 0; +} + +static void WS_Server_OnOpen(int ws, void *user_ptr) { + LogDebug("WS %d is open", ws); + + NBN_Server *server = (NBN_Server *)user_ptr; + NBN_WebRTC_Peer_ID peer_id; + int channel_id; + + if (CreatePeer(&peer_id, &channel_id, server->driver_data.webrtc.cfg, WS_Server_OnLocalDescription, + WS_Server_OnPeerStateChanged) < 0) { + LogError("Failed to create peer"); + return; + } + + NBN_Connection_ID conn_id = NBN_BuildConnectionHash(ws, NBN_DRIVER_WEBRTC_NATIVE); + NBN_Connection *conn = CreateClientConnection(server, NBN_DRIVER_WEBRTC_NATIVE, conn_id); + + conn->driver_data.webrtc.peer_id = peer_id; + conn->driver_data.webrtc.ws = ws; + conn->driver_data.webrtc.channel_id = channel_id; + conn->driver_data.endpoint_ptr = server; + + rtcSetUserPointer(peer_id, conn); + rtcSetUserPointer(ws, conn); +} + +static void WS_Server_OnClosed(int ws, void *user_ptr) { + LogDebug("WS %d has closed", ws); + + if (user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + int channel_id = conn->driver_data.webrtc.channel_id; + + LogDebug("Closing WebRTC peer and channel (peer: %d, channel: %d)", peer_id, channel_id); + + ClosePeer(conn); + } +} + +static void WS_Server_OnMessage(int ws, const char *msg, int size, void *user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + + ProcessSignalingMessage(peer_id, ws, msg, size, "offer"); +} + +static void WS_Server_OnConnection(int wsserver, int ws, void *user_ptr) { + (void)wsserver; + + LogDebug("New WS connection %d (user_ptr: %p)", ws, user_ptr); + + rtcSetUserPointer(ws, user_ptr); + rtcSetOpenCallback(ws, WS_Server_OnOpen); + rtcSetClosedCallback(ws, WS_Server_OnClosed); + rtcSetErrorCallback(ws, WS_OnError); + rtcSetMessageCallback(ws, WS_Server_OnMessage); +} + +static int WebRTC_Native_Server_Start(NBN_Server *server, uint16_t port) { + NBN_WebRTC_Config cfg = server->driver_data.webrtc.cfg; + + rtcInitLogger(cfg.log_level, WebRTC_Native_Log); + rtcPreload(); + + rtcWsServerConfiguration rtc_cfg = {.port = port, + .enableTls = cfg.enable_tls, + .certificatePemFile = cfg.cert_path, + .keyPemFile = cfg.key_path, + .keyPemPass = cfg.passphrase}; + + int ws_server = rtcCreateWebSocketServer(&rtc_cfg, WS_Server_OnConnection); + + if (ws_server < 0) { + LogError("Failed to start WS server (code: %d)", ws_server); + + return NBN_ERROR; + } + + rtcSetUserPointer(ws_server, server); + server->driver_data.webrtc.ws_server = ws_server; + + return 0; +} + +static void WebRTC_Native_Server_Stop(NBN_Server *server) { + int ws_server = server->driver_data.webrtc.ws_server; + + if (ws_server >= 0) { + rtcDeleteWebSocketServer(ws_server); + } + + rtcCleanup(); +} + +static int WebRTC_Native_Server_RecvPackets(NBN_Server *server) { + NBN_Packet *packet = &server->endpoint.read_packet; + const int buffer_size = sizeof(packet->buffer); + int size = buffer_size; + + for (unsigned int i = 0; i < hmlen(server->clients); i++) { + NBN_Connection *conn = server->clients[i].value; + + if (conn->driver->id != NBN_DRIVER_WEBRTC_NATIVE) + continue; + + int channel_id = conn->driver_data.webrtc.channel_id; + + while (rtcReceiveMessage(channel_id, (char *)packet->buffer, &size) == RTC_ERR_SUCCESS) { + if (Packet_InitRead(packet, server->endpoint.protocol_id, size) < 0) + continue; + + packet->sender = conn; + size = buffer_size; + + ServerDriver_OnClientPacketReceived(server, packet); + } + } + + return 0; +} + +static void WebRTC_Native_Server_CleanupConnection(NBN_Server *server, NBN_Connection *conn) { + (void)server; + + NBN_Assert(conn != NULL); + ClosePeer(conn); +} + +static int WebRTC_Native_Server_SendPacketTo(NBN_Server *server, NBN_Packet *packet, NBN_Connection *conn) { + (void)server; + + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + int channel_id = conn->driver_data.webrtc.channel_id; + + if (rtcSendMessage(channel_id, (char *)packet->buffer, packet->size) < 0) { + LogError("rtcSendMessage failed for peer %d", peer_id); + + return NBN_ERROR; + } + + return 0; +} + +static void WS_Client_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) { + (void)pc; + + LogDebug("Processing local description of type '%s'", type); + + if (strncmp(type, "offer", strlen("offer")) != 0) { + LogWarning("Ignoring local description of type '%s' (expected 'offer')", type); + return; + } + + NBN_Connection *conn = (NBN_Connection *)user_ptr; + int ws = conn->driver_data.webrtc.ws; + + ProcessLocalDescription(ws, sdp, "offer"); +} + +static void WS_Client_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + + LogDebug("Server peer state changed to %d", pc, state); + + if (state == RTC_CONNECTED) { + LogDebug("Server peer is connected !", pc); + NBN_Client *client = (NBN_Client *)conn->driver_data.endpoint_ptr; + client->driver_data.webrtc.is_connected = true; + } +} + +static void WS_Client_OnOpen(int ws, void *user_ptr) { + NBN_Client *client = (NBN_Client *)user_ptr; + + LogDebug("WS %d is open, creating peer...", ws); + + NBN_WebRTC_Peer_ID peer_id; + int channel_id; + + if (CreatePeer(&peer_id, &channel_id, client->driver_data.webrtc.cfg, WS_Client_OnLocalDescription, + WS_Client_OnPeerStateChanged) < 0) { + LogError("Failed to create peer"); + return; + } + + LogDebug("Successfully created peer: %d", peer_id); + + NBN_Connection *server_conn = client->server_connection; + + NBN_Assert(server_conn != NULL); + rtcSetUserPointer(peer_id, server_conn); + rtcSetUserPointer(ws, server_conn); +} + +static void WS_Client_OnClosed(int ws, void *user_ptr) { + LogDebug("WS %d has closed", ws); + + if (user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + + ClosePeer(conn); + } +} + +static void WS_Client_OnMessage(int ws, const char *msg, int size, void *user_ptr) { + NBN_Connection *conn = (NBN_Connection *)user_ptr; + NBN_WebRTC_Peer_ID peer_id = conn->driver_data.webrtc.peer_id; + + ProcessSignalingMessage(peer_id, ws, msg, size, "answer"); +} + +static int WebRTC_Native_Client_Start(NBN_Client *client, const char *host, uint16_t port) { + NBN_WebRTC_Config cfg = client->driver_data.webrtc.cfg; + + rtcInitLogger(cfg.log_level, WebRTC_Native_Log); + rtcPreload(); + + char ws_addr[256] = {0}; + // TODO: wss? + snprintf(ws_addr, sizeof(ws_addr), "ws://%s:%d", host, port); + + int cli_ws; + + if ((cli_ws = rtcCreateWebSocket(ws_addr)) < 0) { + LogError("Failed to create websocket"); + return NBN_ERROR; + } + + LogDebug("Successfully created client WS: %d", cli_ws); + + rtcSetUserPointer(cli_ws, client); + rtcSetOpenCallback(cli_ws, WS_Client_OnOpen); + rtcSetClosedCallback(cli_ws, WS_Client_OnClosed); + rtcSetErrorCallback(cli_ws, WS_OnError); + rtcSetMessageCallback(cli_ws, WS_Client_OnMessage); + + // wait for the connection to be established + const float delay = 0.3f; + const long timeout = 5; // 5 seconds to connect + float current_time_sec = 0; + + while (true) { +#if defined(_WIN32) || defined(_WIN64) + Sleep(delay * 1000); +#elif _POSIX_C_SOURCE >= 199309L + struct timespec rqtp; + rqtp.tv_sec = 0; + rqtp.tv_nsec = delay * 1e9; + + if (nanosleep(&rqtp, NULL) < 0) { + LogError("nanosleep failed"); + NBN_Abort(); + } +#else + if (usleep(delay * 1e6) < 0) { + LogError("usleep failed"); + NBN_Abort(); + } +#endif + + current_time_sec += delay; + + if (current_time_sec >= timeout || client->driver_data.webrtc.is_connected) { + break; + } + } + + return client->driver_data.webrtc.is_connected ? 0 : NBN_ERROR; +} + +static void WebRTC_Native_Client_Stop(NBN_Client *client) { + if (client->driver_data.webrtc.is_connected) { + ClosePeer(client->server_connection); + } + + client->driver_data.webrtc.is_connected = false; + rtcCleanup(); +} + +static int WebRTC_Native_Client_RecvPackets(NBN_Client *client) { + NBN_Packet *packet = &client->endpoint.read_packet; + const int buffer_size = sizeof(packet->buffer); + int size = buffer_size; + int channel_id = client->server_connection->driver_data.webrtc.channel_id; + + while (rtcReceiveMessage(channel_id, (char *)packet->buffer, &size) == RTC_ERR_SUCCESS) { + if (Packet_InitRead(packet, client->endpoint.protocol_id, size) < 0) + continue; + + packet->sender = NULL; + size = buffer_size; + + ClientDriver_OnPacketReceived(client, packet); + } + + return 0; +} + +static int WebRTC_Native_Client_SendPacket(NBN_Client *client, NBN_Packet *packet, NBN_Connection *connection) { + (void)client; + + int channel_id = connection->driver_data.webrtc.channel_id; + + if (rtcSendMessage(channel_id, (char *)packet->buffer, packet->size) < 0) { + LogError("rtcSendMessage failed"); + return NBN_ERROR; + } + + return 0; +} + +#endif // NBN_WEBRTC_NATIVE + +// END OF WEBRTC NATIVE DRIVER +// =================================================== + +/** + * ====== PACKET SIMULATOR ====== + */ + +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) + +#define RAND_RATIO_BETWEEN(min, max) (((rand() % (int)((max * 100.f) - (min * 100.f) + 1)) + (min * 100.f)) / 100.f) +#define RAND_RATIO RAND_RATIO_BETWEEN(0, 1) + +#ifdef NBN_PLATFORM_WINDOWS +DWORD WINAPI PacketSimulator_Routine(LPVOID); +#else +static void *PacketSimulator_Routine(void *); +#endif + +void NBN_Client_SetPing(NBN_Client *client, float v) { client->endpoint.packet_simulator.ping = v; } +void NBN_Client_SetJitter(NBN_Client *client, float v) { client->endpoint.packet_simulator.jitter = v; } +void NBN_Client_SetPacketLoss(NBN_Client *client, float v) { client->endpoint.packet_simulator.packet_loss_ratio = v; } +void NBN_Client_SetPacketDuplication(NBN_Client *client, float v) { + client->endpoint.packet_simulator.packet_duplication_ratio = v; +} + +void NBN_Server_SetPing(NBN_Server *server, float v) { server->endpoint.packet_simulator.ping = v; } +void NBN_Server_SetJitter(NBN_Server *server, float v) { server->endpoint.packet_simulator.jitter = v; } +void NBN_Server_SetPacketLoss(NBN_Server *server, float v) { server->endpoint.packet_simulator.packet_loss_ratio = v; } +void NBN_Server_SetPacketDuplication(NBN_Server *server, float v) { + server->endpoint.packet_simulator.packet_duplication_ratio = v; +} + +static void PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *endpoint) { + packet_simulator->endpoint = endpoint; + packet_simulator->running = false; + packet_simulator->ping = 0; + packet_simulator->jitter = 0; + packet_simulator->packet_loss_ratio = 0; + packet_simulator->total_dropped_packets = 0; + packet_simulator->packet_duplication_ratio = 0; + packet_simulator->head_packet = NULL; + packet_simulator->tail_packet = NULL; + packet_simulator->packet_count = 0; + +#ifdef NBN_PLATFORM_WINDOWS + packet_simulator->queue_mutex = CreateMutex(NULL, FALSE, NULL); +#else + packet_simulator->queue_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; +#endif +} + +static int PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, + NBN_Connection *receiver) { +#ifdef NBN_PLATFORM_WINDOWS + WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); +#else + pthread_mutex_lock(&packet_simulator->queue_mutex); +#endif + + /* Compute jitter in range [ -jitter, +jitter ]. + * Jitter is converted from seconds to milliseconds for the random operation below. + */ + + int jitter = packet_simulator->jitter * 1000; + + jitter = (jitter > 0) ? (rand() % (jitter * 2)) - jitter : 0; + + NBN_PacketSimulatorEntry *entry = (NBN_PacketSimulatorEntry *)malloc(sizeof(NBN_PacketSimulatorEntry)); + + entry->delay = packet_simulator->ping + (double)jitter / 1000; /* and converted back to seconds */ + entry->receiver = receiver; + entry->enqueued_at = packet_simulator->endpoint->time; + + memcpy(&entry->packet, packet, sizeof(NBN_Packet)); + + if (packet_simulator->packet_count > 0) { + entry->prev = packet_simulator->tail_packet; + entry->next = NULL; + + packet_simulator->tail_packet->next = entry; + packet_simulator->tail_packet = entry; + } else // the list is empty + { + entry->prev = NULL; + entry->next = NULL; + + packet_simulator->head_packet = entry; + packet_simulator->tail_packet = entry; + } + + packet_simulator->packet_count++; + +#ifdef NBN_PLATFORM_WINDOWS + ReleaseMutex(packet_simulator->queue_mutex); +#else + pthread_mutex_unlock(&packet_simulator->queue_mutex); +#endif + + return 0; +} + +static void PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) { +#ifdef NBN_PLATFORM_WINDOWS + packet_simulator->thread = CreateThread(NULL, 0, PacketSimulator_Routine, packet_simulator, 0, NULL); +#else + pthread_create(&packet_simulator->thread, NULL, PacketSimulator_Routine, packet_simulator); +#endif + + packet_simulator->running = true; + + LogDebug("Packet simulator started (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", + packet_simulator->packet_loss_ratio, packet_simulator->packet_duplication_ratio, packet_simulator->ping, + packet_simulator->jitter); +} + +void PacketSimulator_Stop(NBN_PacketSimulator *packet_simulator) { + packet_simulator->running = false; + +#ifdef NBN_PLATFORM_WINDOWS + WaitForSingleObject(packet_simulator->thread, INFINITE); +#else + pthread_join(packet_simulator->thread, NULL); +#endif +} + +#ifdef NBN_PLATFORM_WINDOWS +DWORD WINAPI PacketSimulator_Routine(LPVOID arg) +#else +static void *PacketSimulator_Routine(void *arg) +#endif +{ + NBN_PacketSimulator *packet_simulator = (NBN_PacketSimulator *)arg; + + while (packet_simulator->running) { +#ifdef NBN_PLATFORM_WINDOWS + WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); +#else + pthread_mutex_lock(&packet_simulator->queue_mutex); +#endif + + NBN_PacketSimulatorEntry *entry = packet_simulator->head_packet; + + while (entry) { + NBN_PacketSimulatorEntry *next = entry->next; + + if (packet_simulator->endpoint->time - entry->enqueued_at < entry->delay) { + entry = next; + + continue; + } + + PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); + + for (unsigned int i = 0; i < PacketSimulator_GetRandomDuplicatePacketCount(packet_simulator); i++) { + LogDebug("Duplicate packet %d (count: %d)", entry->packet.header.seq_number, i + 1); + + PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); + } + + // remove the entry from the packet list + if (entry == packet_simulator->head_packet) // it's the head of the list + { + NBN_PacketSimulatorEntry *new_head = entry->next; + + if (new_head) + new_head->prev = NULL; + else + packet_simulator->tail_packet = NULL; + + packet_simulator->head_packet = new_head; + } else if (entry == packet_simulator->tail_packet) // it's the tail of the list + { + NBN_PacketSimulatorEntry *new_tail = entry->prev; + + new_tail->next = NULL; + packet_simulator->tail_packet = new_tail; + } else // it's in the middle of the list + { + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + } + + packet_simulator->packet_count--; + + // release the memory allocated for the entry + free(entry); + + entry = next; + } + +#ifdef NBN_PLATFORM_WINDOWS + ReleaseMutex(packet_simulator->queue_mutex); +#else + pthread_mutex_unlock(&packet_simulator->queue_mutex); +#endif + } + +#ifdef NBN_PLATFORM_WINDOWS + return 0; +#else + return NULL; +#endif +} + +static int PacketSimulator_SendPacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, + NBN_Connection *receiver) { + if (RAND_RATIO < packet_simulator->packet_loss_ratio) { + packet_simulator->total_dropped_packets++; + LogDebug("Drop packet %d (Total dropped packets: %d)", packet->header.seq_number, + packet_simulator->total_dropped_packets); + + return 0; + } + + NBN_Driver *driver = receiver->driver; + bool is_server = packet_simulator->endpoint->is_server; + + if (is_server) { + if (receiver->is_stale) + return 0; + + return driver->impl.serv_send_packet_to((NBN_Server *)packet_simulator->endpoint, packet, receiver); + } else { + return driver->impl.cli_send_packet((NBN_Client *)packet_simulator->endpoint, packet, receiver); + } +} + +static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *packet_simulator) { + if (RAND_RATIO < packet_simulator->packet_duplication_ratio) + return rand() % 10 + 1; + + return 0; +} + +#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ + +// END OF PACKET SIMULATOR +// =================================================== + +/** + * ====== LOGGING ====== + */ + +#ifdef NBN_DEBUG + +#define NBN_DEFAULT_LOG_LEVEL NBN_LOG_DEBUG + +#else + +#define NBN_DEFAULT_LOG_LEVEL NBN_LOG_INFO + +#endif // NBN_DEBUG + +static NBN_LogLevel log_level = NBN_DEFAULT_LOG_LEVEL; + +void NBN_SetLogLevel(NBN_LogLevel level) { log_level = level; } + +#ifdef NBN_LOG_CUSTOM_FUNCTION + +extern void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...); + +#else + +/** + * Default logging function + */ + +/** + * Copyright (c) 2017 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#include + +static const char *level_names[] = {"ERROR", "INFO", "WARNING", "DEBUG"}; + +static void Log(NBN_LogLevel level, const char *filename, int line, const char *msg, ...) { + if (log_level < level) { + return; + } + + time_t t = time(NULL); + struct tm *lt = localtime(&t); + FILE *fp = level == NBN_LOG_ERROR ? stderr : stdout; + + va_list args; + char buf[32]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; + fprintf(fp, "%s %-5s %s:%d: ", buf, level_names[level], filename, line); + va_start(args, msg); + vfprintf(fp, msg, args); + va_end(args); + fprintf(fp, "\n"); + fflush(fp); +} + +#endif // NBN_CUSTOM_LOG_FUNCTION + +// END OF LOGGING +// =================================================== diff --git a/nbnet.h b/nbnet.h index 34b894e..14ece59 100644 --- a/nbnet.h +++ b/nbnet.h @@ -1,6 +1,6 @@ /* - Copyright (C) 2024 BIAGINI Nathan + Copyright (C) 2026 BIAGINI Nathan This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -25,6044 +25,520 @@ #ifndef NBNET_H #define NBNET_H -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_WIN32) || defined(_WIN64) - -#include -#include -#include - -#define NBNET_WINDOWS - +#ifdef __cplusplus +extern "C" { #endif -#ifndef NBNET_WINDOWS - -#include -#include +#include +#include -#ifndef CLOCK_MONOTONIC_RAW -#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC -#endif +#define NBN_ERROR -1 +// TODO: doc +#ifndef NBN_SERVER_INITIAL_DATA_MAX_SIZE +#define NBN_SERVER_INITIAL_DATA_MAX_SIZE 256 #endif -#ifndef NBN_Allocator -#define NBN_Allocator malloc +// TODO: doc +#ifndef NBN_CONNECTION_REQUEST_DATA_MAX_SIZE +#define NBN_CONNECTION_REQUEST_DATA_MAX_SIZE 256 #endif -#ifndef NBN_Reallocator -#define NBN_Reallocator realloc +// TODO: doc +#ifndef NBN_MESSAGE_RESEND_DELAY +#define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ #endif -#ifndef NBN_Deallocator -#define NBN_Deallocator free +/** + * Number of seconds before a connection is considered stale and closes + */ +#ifndef NBN_CONNECTION_STALE_TIME_THRESHOLD +#define NBN_CONNECTION_STALE_TIME_THRESHOLD 3 #endif -#pragma region Declarations - -#ifndef NBN_Abort -#define NBN_Abort abort -#endif /* NBN_Abort */ +typedef struct NBN_Client NBN_Client; +typedef struct NBN_Server NBN_Server; -#define NBN_ERROR -1 - -typedef struct NBN_Endpoint NBN_Endpoint; -typedef struct NBN_Connection NBN_Connection; -typedef struct NBN_Channel NBN_Channel; -typedef struct NBN_Driver NBN_Driver; -typedef uint32_t NBN_ConnectionHandle; +typedef uint64_t NBN_Connection_ID; -#pragma region NBN_ConnectionVector +typedef struct NBN_ConnectionHandle { + NBN_Connection_ID id; + void *user_data; +} NBN_ConnectionHandle; -typedef struct NBN_ConnectionVector -{ - NBN_Connection **connections; - unsigned int count; - unsigned int capacity; -} NBN_ConnectionVector; +typedef struct NBN_ConnectionStats { + double ping; + unsigned int total_lost_packets; + float packet_loss; + float upload_bandwidth; + float download_bandwidth; +} NBN_ConnectionStats; -#pragma endregion // NBN_ConnectionVector +/** + * Information about a received message. + */ +typedef struct NBN_MessageInfo { + /** + * Type of the message + */ + uint8_t type; -#pragma region NBN_ConnectionTable + /** + * Channel the message was received on + */ + uint8_t channel_id; -typedef struct NBN_ConnectionTable -{ - NBN_Connection **connections; - unsigned int capacity; - unsigned int count; - float load_factor; -} NBN_ConnectionTable; + /** + * Pointer to the internal message data buffer + */ + uint8_t *data; -#pragma endregion // NBN_ConnectionTable + /* + * Length of the message data in bytes + */ + uint16_t length; -#pragma region Memory management + /** + * A handle to the connection that sent the message. + * + * On the client, it will always be NULL as all messages are received from the server. + */ + NBN_ConnectionHandle *sender; +} NBN_MessageInfo; -enum -{ - NBN_MEM_MESSAGE_CHUNK, - NBN_MEM_BYTE_ARRAY_MESSAGE, - NBN_MEM_CONNECTION, +// TODO: doc +typedef enum NBN_Channel_Mode { NBN_CHANNEL_UNRELIABLE, NBN_CHANNEL_RELIABLE } NBN_Channel_Mode; -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - NBN_MEM_PACKET_SIMULATOR_ENTRY -#endif -}; +typedef enum NBN_Client_Event { + NBN_CLIENT_ERROR = NBN_ERROR, -typedef struct NBN_MemPoolFreeBlock -{ - struct NBN_MemPoolFreeBlock *next; -} NBN_MemPoolFreeBlock; + NBN_CLIENT_NO_EVENT = 0, -typedef struct NBN_MemPool -{ - uint8_t **blocks; - size_t block_size; - unsigned int block_count; - unsigned int block_idx; - NBN_MemPoolFreeBlock *free; -} NBN_MemPool; + /* Client is connected to server */ + NBN_CLIENT_CONNECTED, -typedef struct NBN_MemoryManager -{ -#ifdef NBN_DISABLE_MEMORY_POOLING - size_t mem_sizes[16]; -#else - NBN_MemPool mem_pools[16]; -#endif /* NBN_DISABLE_MEMORY_POOLING */ -} NBN_MemoryManager; + /* Client is disconnected from the server */ + NBN_CLIENT_DISCONNECTED, -extern NBN_MemoryManager nbn_mem_manager; + /* Client has received a message from the server */ + NBN_CLIENT_MESSAGE_RECEIVED +} NBN_Client_Event; -#pragma endregion /* Memory management */ +typedef enum NBN_Server_Event { + NBN_SERVER_ERROR = NBN_ERROR, -#pragma region Serialization + NBN_SERVER_NO_EVENT = 0, -typedef uint32_t Word; + /* A new client has connected */ + NBN_SERVER_NEW_CONNECTION, -#define WORD_BYTES (sizeof(Word)) -#define WORD_BITS (WORD_BYTES * 8) -#define BITS_REQUIRED(min, max) (min == max) ? 0 : GetRequiredNumberOfBitsFor(max - min) + /* A client has disconnected */ + NBN_SERVER_DISCONNECTION, -#define B_MASK(n) (1u << (n)) -#define B_SET(mask, n) (mask |= B_MASK(n)) -#define B_UNSET(mask, n) (mask &= ~B_MASK(n)) -#define B_IS_SET(mask, n) ((B_MASK(n) & mask) == B_MASK(n)) -#define B_IS_UNSET(mask, n) ((B_MASK(n) & mask) == 0) + /* A message has been received from a client */ + NBN_SERVER_MESSAGE_RECEIVED +} NBN_Server_Event; -#define ASSERT_VALUE_IN_RANGE(v, min, max) assert(v >= (int64_t)min && v <= (int64_t)max) -#define ASSERTED_SERIALIZE(stream, v, min, max, func) \ -{ \ - if (stream->type == NBN_STREAM_WRITE) \ - ASSERT_VALUE_IN_RANGE(v, min, max); \ - if ((func) < 0) \ - NBN_Abort(); \ - if (stream->type == NBN_STREAM_READ) \ - ASSERT_VALUE_IN_RANGE(v, min, max); \ -} +typedef struct NBN_ServerStats { + float upload_bandwidth; /* Total upload bandwith of the game server */ + float download_bandwidth; /* Total download bandwith of the game server */ +} NBN_ServerStats; -#define NBN_SerializeUInt(stream, v, min, max) \ - ASSERTED_SERIALIZE((stream), v, min, max, (stream)->serialize_uint_func((stream), (unsigned int *)&(v), min, max)) -#define NBN_SerializeUInt64(stream, v) (stream)->serialize_uint64_func((stream), (uint64_t *)&(v)) -#define NBN_SerializeInt(stream, v, min, max) \ - ASSERTED_SERIALIZE((stream), v, min, max, (stream)->serialize_int_func((stream), &(v), min, max)) -#define NBN_SerializeFloat(stream, v, min, max, precision) \ - ASSERTED_SERIALIZE((stream), v, min, max, (stream)->serialize_float_func((stream), &(v), min, max, precision)) -#define NBN_SerializeBool(stream, v) ASSERTED_SERIALIZE((stream), v, 0, 1, (stream)->serialize_bool_func((stream), &(v))) -#define NBN_SerializeString(stream, v, length) NBN_SerializeBytes((stream), v, length) -#define NBN_SerializeBytes(stream, v, length) (stream)->serialize_bytes_func((stream), (uint8_t *)v, length) -#define NBN_SerializePadding(stream) (stream)->serialize_padding_func(stream) +typedef struct NBN_DisconnectionInfo { + NBN_Connection_ID conn_id; /* ID if the disconnected connection */ + void *user_data; /* Pointer to user-defined data associated with this connection */ +} NBN_DisconnectionInfo; -#pragma region NBN_BitReader +typedef unsigned int NBN_Client_Iterator; -typedef struct NBN_BitReader -{ - unsigned int size; +typedef struct NBN_Writer { uint8_t *buffer; - uint64_t scratch; - unsigned int scratch_bits_count; - unsigned int byte_cursor; -} NBN_BitReader; - -void NBN_BitReader_Init(NBN_BitReader *, uint8_t *, unsigned int); -int NBN_BitReader_Read(NBN_BitReader *, Word *, unsigned int); - -#pragma endregion /* NBN_BitReader */ - -#pragma region NBN_BitWriter + uint16_t length; + uint16_t position; +} NBN_Writer; -typedef struct NBN_BitWriter -{ - unsigned int size; +typedef struct NBN_Reader { uint8_t *buffer; - uint64_t scratch; - unsigned int scratch_bits_count; - unsigned int byte_cursor; -} NBN_BitWriter; - -void NBN_BitWriter_Init(NBN_BitWriter*, uint8_t *, unsigned int); -int NBN_BitWriter_Write(NBN_BitWriter *, Word, unsigned int); -int NBN_BitWriter_Flush(NBN_BitWriter *); - -#pragma endregion /* NBN_BitWriter */ - -#pragma region NBN_Stream - -typedef struct NBN_Stream NBN_Stream; - -typedef int (*NBN_Stream_SerializeUInt)(NBN_Stream *, unsigned int *, unsigned int, unsigned int); -typedef int (*NBN_Stream_SerializeUInt64)(NBN_Stream *, uint64_t *); -typedef int (*NBN_Stream_SerializeInt)(NBN_Stream *, int *, int, int); -typedef int (*NBN_Stream_SerializeFloat)(NBN_Stream *, float *, float, float, int); -typedef int (*NBN_Stream_SerializeBool)(NBN_Stream *, bool *); -typedef int (*NBN_Stream_SerializePadding)(NBN_Stream *); -typedef int (*NBN_Stream_SerializeBytes)(NBN_Stream *, uint8_t *, unsigned int); - -typedef enum NBN_StreamType -{ - NBN_STREAM_WRITE, - NBN_STREAM_READ, - NBN_STREAM_MEASURE -} NBN_StreamType; - -struct NBN_Stream -{ - NBN_StreamType type; - NBN_Stream_SerializeUInt serialize_uint_func; - NBN_Stream_SerializeUInt64 serialize_uint64_func; - NBN_Stream_SerializeInt serialize_int_func; - NBN_Stream_SerializeFloat serialize_float_func; - NBN_Stream_SerializeBool serialize_bool_func; - NBN_Stream_SerializePadding serialize_padding_func; - NBN_Stream_SerializeBytes serialize_bytes_func; -}; - -#pragma endregion /* NBN_Stream */ - -#pragma region NBN_ReadStream - -typedef struct NBN_ReadStream -{ - NBN_Stream base; - NBN_BitReader bit_reader; -} NBN_ReadStream; - -void NBN_ReadStream_Init(NBN_ReadStream *, uint8_t *, unsigned int); -int NBN_ReadStream_SerializeUint(NBN_ReadStream *, unsigned int *, unsigned int, unsigned int); -int NBN_ReadStream_SerializeUint64(NBN_ReadStream *read_stream, uint64_t *value); -int NBN_ReadStream_SerializeInt(NBN_ReadStream *, int *, int, int); -int NBN_ReadStream_SerializeFloat(NBN_ReadStream *, float *, float, float, int); -int NBN_ReadStream_SerializeBool(NBN_ReadStream *, bool *); -int NBN_ReadStream_SerializePadding(NBN_ReadStream *); -int NBN_ReadStream_SerializeBytes(NBN_ReadStream *, uint8_t *, unsigned int); - -#pragma endregion /* NBN_ReadStream */ - -#pragma region NBN_WriteStream - -typedef struct NBN_WriteStream -{ - NBN_Stream base; - NBN_BitWriter bit_writer; -} NBN_WriteStream; - -void NBN_WriteStream_Init(NBN_WriteStream *, uint8_t *, unsigned int); -int NBN_WriteStream_SerializeUint(NBN_WriteStream *, unsigned int *, unsigned int, unsigned int); -int NBN_WriteStream_SerializeUint64(NBN_WriteStream *write_stream, uint64_t *value); -int NBN_WriteStream_SerializeInt(NBN_WriteStream *, int *, int, int); -int NBN_WriteStream_SerializeFloat(NBN_WriteStream *, float *, float, float, int); -int NBN_WriteStream_SerializeBool(NBN_WriteStream *, bool *); -int NBN_WriteStream_SerializePadding(NBN_WriteStream *); -int NBN_WriteStream_SerializeBytes(NBN_WriteStream *, uint8_t *, unsigned int); -int NBN_WriteStream_Flush(NBN_WriteStream *); - -#pragma endregion /* NBN_WriteStream */ - -#pragma region NBN_MeasureStream - -typedef struct NBN_MeasureStream -{ - NBN_Stream base; - unsigned int number_of_bits; -} NBN_MeasureStream; - -void NBN_MeasureStream_Init(NBN_MeasureStream *); -int NBN_MeasureStream_SerializeUint(NBN_MeasureStream *, unsigned int *, unsigned int, unsigned int); -int NBN_MeasureStream_SerializeUint64(NBN_MeasureStream *measure_stream, unsigned int *value); -int NBN_MeasureStream_SerializeInt(NBN_MeasureStream *, int *, int, int); -int NBN_MeasureStream_SerializeFloat(NBN_MeasureStream *, float *, float, float, int); -int NBN_MeasureStream_SerializeBool(NBN_MeasureStream *, bool *); -int NBN_MeasureStream_SerializePadding(NBN_MeasureStream *); -int NBN_MeasureStream_SerializeBytes(NBN_MeasureStream *, uint8_t *, unsigned int); -void NBN_MeasureStream_Reset(NBN_MeasureStream *); - -#pragma endregion /* NBN_MeasureStream */ - -#pragma endregion /* Serialization */ - -#pragma region NBN_Message + uint16_t length; + uint16_t position; +} NBN_Reader; + +typedef enum NBN_LogLevel { NBN_LOG_ERROR, NBN_LOG_INFO, NBN_LOG_WARNING, NBN_LOG_DEBUG } NBN_LogLevel; + +void NBN_SetLogLevel(NBN_LogLevel); + +void NBN_Writer_Init(NBN_Writer *writer, uint8_t *buffer, unsigned int length); +void NBN_Writer_WriteInt8(NBN_Writer *writer, int8_t value); +void NBN_Writer_WriteInt16(NBN_Writer *writer, int16_t value); +void NBN_Writer_WriteInt32(NBN_Writer *writer, int32_t value); +void NBN_Writer_WriteInt64(NBN_Writer *writer, int64_t value); +void NBN_Writer_WriteUInt8(NBN_Writer *writer, uint8_t value); +void NBN_Writer_WriteUInt16(NBN_Writer *writer, uint16_t value); +void NBN_Writer_WriteUInt32(NBN_Writer *writer, uint32_t value); +void NBN_Writer_WriteUInt64(NBN_Writer *writer, uint64_t value); +void NBN_Writer_WriteFloat(NBN_Writer *writer, float value); +void NBN_Writer_WriteBool(NBN_Writer *writer, bool value); +void NBN_Writer_WriteBytes(NBN_Writer *writer, uint8_t *bytes, unsigned int length); +void NBN_Writer_WriteString(NBN_Writer *writer, const char *str, unsigned int max_len); + +void NBN_Reader_Init(NBN_Reader *reader, uint8_t *buffer, unsigned int length); +int NBN_Reader_ReadInt8(NBN_Reader *reader, int8_t *value); +int NBN_Reader_ReadInt16(NBN_Reader *reader, int16_t *value); +int NBN_Reader_ReadInt32(NBN_Reader *reader, int32_t *value); +int NBN_Reader_ReadInt64(NBN_Reader *reader, int64_t *value); +int NBN_Reader_ReadUInt8(NBN_Reader *reader, uint8_t *value); +int NBN_Reader_ReadUInt16(NBN_Reader *reader, uint16_t *value); +int NBN_Reader_ReadUInt32(NBN_Reader *reader, uint32_t *value); +int NBN_Reader_ReadUInt64(NBN_Reader *reader, uint64_t *value); +int NBN_Reader_ReadFloat(NBN_Reader *reader, float *value); +int NBN_Reader_ReadBool(NBN_Reader *reader, bool *value); +int NBN_Reader_ReadBytes(NBN_Reader *reader, uint8_t *bytes, unsigned int length); +int NBN_Reader_ReadString(NBN_Reader *reader, char *str, unsigned int max_len); + +/** + * Initialize the game client with minimal configuration. + * + * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able + * to communicate + * @param host Host to connect to + * @param port Port to connect to + */ +NBN_Client *NBN_Client_Create(const char *protocol_name, const char *host, uint16_t port); -#define NBN_MAX_CHANNELS 8 -#define NBN_LIBRARY_RESERVED_CHANNELS 3 -#define NBN_MAX_MESSAGE_TYPES 255 /* Maximum value of uint8_t, see message header */ -#define NBN_MESSAGE_RESEND_DELAY 0.1 /* Number of seconds before a message is resent (reliable messages redundancy) */ +// TODO: doc +uint8_t NBN_Client_CreateChannel(NBN_Client *client, NBN_Channel_Mode mode, unsigned int buffer_size, + unsigned int max_message_len); -typedef int (*NBN_MessageSerializer)(void *, NBN_Stream *); -typedef void *(*NBN_MessageBuilder)(void); -typedef void (*NBN_MessageDestructor)(void *); +// TODO: doc +unsigned int NBN_Client_GetChannelCurrentCapacity(NBN_Client *client, uint8_t channel_id); -typedef struct NBN_MessageHeader -{ - uint16_t id; - uint8_t type; - uint8_t channel_id; -} NBN_MessageHeader; +// TODO: doc +NBN_Writer *NBN_Client_WriteConnectionRequestData(NBN_Client *client); -/* - * Holds the user message's data as well as a reference count for message recycling +/** + * Start the game client. + * + * @return 0 when successully started, -1 otherwise */ -typedef struct NBN_OutgoingMessage -{ - uint8_t type; - unsigned int ref_count; - void *data; -} NBN_OutgoingMessage; - -typedef struct NBN_Message -{ - NBN_MessageHeader header; - NBN_Connection *sender; - NBN_OutgoingMessage *outgoing_msg; /* NULL for incoming messages */ - void *data; -} NBN_Message; +int NBN_Client_Start(NBN_Client *client); /** - * Information about a received message. + * Disconnect from the server. The client can be restarted by calling NBN_Client_Start or + * NBN_Client_StartWithData again. */ -typedef struct NBN_MessageInfo -{ - /** User defined message's type */ - uint8_t type; - - /** Channel the message was received on */ - uint8_t channel_id; - - /** Message's data */ - void *data; - - /** - * The message's sender. - * - * On the client side, it will always be 0 (all received messages come from the game server). - */ - NBN_ConnectionHandle sender; -} NBN_MessageInfo; - -int NBN_Message_SerializeHeader(NBN_MessageHeader *, NBN_Stream *); -int NBN_Message_Measure(NBN_Message *, NBN_MeasureStream *, NBN_MessageSerializer); -int NBN_Message_SerializeData(NBN_Message *, NBN_Stream *, NBN_MessageSerializer); - -#pragma endregion /* NBN_Message */ - -#pragma region RPC - -#define NBN_RPC_MAX_PARAM_COUNT 16 /* Maximum number of parameters a RPC signature can accept */ -#define NBN_RPC_MAX 32 /* Maximum number of registered RPCs */ -#define NBN_RPC_STRING_MAX_LENGTH 256 /* Maximum length of a RPC string parameter */ - -/* Helper macros */ -#define NBN_RPC_BuildSignature(pc, ...) ((NBN_RPC_Signature){.param_count = pc, .params = {__VA_ARGS__}}) -#define NBN_RPC_Int(v) ((NBN_RPC_Param){.type = NBN_RPC_PARAM_INT, .value = {.i = v}}) -#define NBN_RPC_Float(v) ((NBN_RPC_Param){.type = NBN_RPC_PARAM_FLOAT, .value = {.f = v}}) -#define NBN_RPC_GetInt(params, idx) (params[idx].value.i) -#define NBN_RPC_GetFloat(params, idx) (params[idx].value.f) -#define NBN_RPC_GetBool(params, idx) (params[idx].value.b) -#define NBN_RPC_GetString(params, idx) (params[idx].value.s) - -typedef enum NBN_RPC_ParamType -{ - NBN_RPC_PARAM_INT, - NBN_RPC_PARAM_FLOAT, - NBN_RPC_PARAM_BOOL, - NBN_RPC_PARAM_STRING -} NBN_RPC_ParamType; +void NBN_Client_Stop(NBN_Client *client); -typedef struct NBN_RPC_String -{ - char string[NBN_RPC_STRING_MAX_LENGTH]; - unsigned int length; -} NBN_RPC_String; +// TODO: doc +NBN_Reader *NBN_Client_ReadServerData(NBN_Client *client); -typedef union NBN_RPC_ParamValue -{ - int i; - float f; - bool b; - char s[NBN_RPC_STRING_MAX_LENGTH]; -} NBN_RPC_ParamValue; - -typedef struct NBN_RPC_Param -{ - NBN_RPC_ParamType type; - NBN_RPC_ParamValue value; -} NBN_RPC_Param; - -typedef struct NBN_RPC_Signature -{ - unsigned int param_count; - NBN_RPC_ParamType params[NBN_RPC_MAX_PARAM_COUNT]; -} NBN_RPC_Signature; - -typedef void (*NBN_RPC_Func)(unsigned int, NBN_RPC_Param[NBN_RPC_MAX_PARAM_COUNT], NBN_ConnectionHandle sender); - -typedef struct NBN_RPC -{ - unsigned int id; - NBN_RPC_Signature signature; - NBN_RPC_Func func; -} NBN_RPC; - -#pragma endregion /* RPC */ - -#pragma region NBN_Packet - -/* - * Maximum allowed packet size (including header) in bytes. - * The 1400 value has been chosen based on this statement: - * - * With the IPv4 header being 20 bytes and the UDP header being 8 bytes, the payload - * of a UDP packet should be no larger than 1500 - 20 - 8 = 1472 bytes to avoid fragmentation. +/** + * Poll game client events. + * + * This function should be called in a loop until it returns NBN_NO_EVENT. + * + * @return The code of the polled event or NBN_NO_EVENT when there is no more events. */ -#define NBN_PACKET_MAX_SIZE 1400 -#define NBN_MAX_MESSAGES_PER_PACKET 255 /* Maximum value of uint8_t, see packet header */ - -#define NBN_PACKET_HEADER_SIZE sizeof(NBN_PacketHeader) - -/* Maximum size of packet's data (NBN_PACKET_MAX_DATA_SIZE + NBN_PACKET_HEADER_SIZE = NBN_PACKET_MAX_SIZE) */ -#define NBN_PACKET_MAX_DATA_SIZE (NBN_PACKET_MAX_SIZE - NBN_PACKET_HEADER_SIZE) - -enum -{ - NBN_PACKET_WRITE_ERROR = -1, - NBN_PACKET_WRITE_OK, - NBN_PACKET_WRITE_NO_SPACE, -}; +NBN_Client_Event NBN_Client_Poll(NBN_Client *client); -typedef enum NBN_PacketMode -{ - NBN_PACKET_MODE_WRITE = 1, - NBN_PACKET_MODE_READ -} NBN_PacketMode; - -typedef struct NBN_PacketHeader -{ - uint32_t protocol_id; - uint16_t seq_number; - uint16_t ack; - uint32_t ack_bits; - uint8_t messages_count; -} NBN_PacketHeader; - -typedef struct NBN_Packet -{ - NBN_PacketHeader header; - NBN_PacketMode mode; - struct NBN_Connection *sender; /* not serialized, fill by the network driver upon reception */ - uint8_t buffer[NBN_PACKET_MAX_SIZE]; - unsigned int size; /* in bytes */ - bool sealed; +/** + * Pack all enqueued messages into packets and send them. + * + * This should be called at a relatively high frequency, probably at the end of + * every game tick. + * + * @return 0 when successful, -1 otherwise + */ +int NBN_Client_Flush(NBN_Client *client); - // streams - NBN_WriteStream w_stream; - NBN_ReadStream r_stream; - NBN_MeasureStream m_stream; -} NBN_Packet; +// TODO: doc +NBN_Writer *NBN_Client_CreateMessage(NBN_Client *client, uint8_t type, uint8_t channel_id); -void NBN_Packet_InitWrite(NBN_Packet *, uint32_t, uint16_t, uint16_t, uint32_t); -int NBN_Packet_InitRead(NBN_Packet *, NBN_Connection *, uint8_t[NBN_PACKET_MAX_SIZE], unsigned int); -uint32_t NBN_Packet_ReadProtocolId(uint8_t[NBN_PACKET_MAX_SIZE], unsigned int); -int NBN_Packet_WriteMessage(NBN_Packet *, NBN_Message *, NBN_MessageSerializer); -int NBN_Packet_Seal(NBN_Packet *, NBN_Connection *); +// TODO: doc +NBN_Writer *NBN_Client_CreateReliableMessage(NBN_Client *client, uint8_t type); -#pragma endregion /* NBN_Packet */ +// TODO: doc +NBN_Writer *NBN_Client_CreateUnreliableMessage(NBN_Client *client, uint8_t type); -#pragma region NBN_MessageChunk +// TODO: doc +NBN_Reader *NBN_Client_ReadMessage(NBN_Client *client); -/* Chunk max size is the number of bytes of data a packet can hold minus the size of a message header minus 2 bytes - * to hold the chunk id and total number of chunks. +/** + * Retrieve the info about the last received message. + * + * Call this function when receiveing a NBN_MESSAGE_RECEIVED event to access + * information about the message. + * + * @return A structure containing information about the received message */ -#define NBN_MESSAGE_CHUNK_SIZE (NBN_PACKET_MAX_DATA_SIZE - sizeof(NBN_MessageHeader) - 2) -#define NBN_MESSAGE_CHUNK_TYPE (NBN_MAX_MESSAGE_TYPES - 1) /* Reserved message type for chunks */ - -typedef struct NBN_MessageChunk -{ - uint8_t id; - uint8_t total; - uint8_t data[NBN_MESSAGE_CHUNK_SIZE]; - NBN_OutgoingMessage *outgoing_msg; -} NBN_MessageChunk; +NBN_MessageInfo NBN_Client_GetMessageInfo(NBN_Client *client); -NBN_MessageChunk *NBN_MessageChunk_Create(void); -void NBN_MessageChunk_Destroy(NBN_MessageChunk *); -int NBN_MessageChunk_Serialize(NBN_MessageChunk *, NBN_Stream *); +/** + * Retrieve network stats about the game client. + * + * @return A structure containing network related stats about the game client + */ +NBN_ConnectionStats NBN_Client_GetStats(NBN_Client *client); -#pragma endregion /* NBN_MessageChunk */ +/** + * Retrieve the code sent by the server when closing the connection. + * + * Call this function when receiving a NBN_DISCONNECTED event. + * + * @return The code used by the server when closing the connection or -1 (the default code) + */ +int NBN_Client_GetServerCloseCode(NBN_Client *client); -#pragma region NBN_ClientClosedMessage +/** + * @return true if connected, false otherwise + */ +bool NBN_Client_IsConnected(NBN_Client *client); -#define NBN_CLIENT_CLOSED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 2) /* Reserved message type */ +/** + * Initialize the game server with minimal configuration. + * + * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be + * able to communicate + * @param port The port clients will connect to + */ +NBN_Server *NBN_Server_Create(const char *protocol_name, uint16_t port); -typedef struct NBN_ClientClosedMessage -{ - int code; -} NBN_ClientClosedMessage; +// TODO: doc +uint8_t NBN_Server_CreateChannel(NBN_Server *server, NBN_Channel_Mode mode, unsigned int buffer_size, + unsigned int max_message_len); -NBN_ClientClosedMessage *NBN_ClientClosedMessage_Create(void); -void NBN_ClientClosedMessage_Destroy(NBN_ClientClosedMessage *); -int NBN_ClientClosedMessage_Serialize(NBN_ClientClosedMessage *, NBN_Stream *); +// TODO: doc +unsigned int NBN_Server_GetChannelCurrentCapacity(NBN_Server *server, uint8_t channel_id, NBN_ConnectionHandle *conn); -#pragma endregion /* NBN_ClientClosedMessage */ +/** + * Start the game server with the provided configuration. + * + * @return 0 when successfully started, -1 otherwise + */ +int NBN_Server_Start(NBN_Server *server); -#pragma region NBN_ClientAcceptedMessage +/** + * Stop the game server and clean everything up. + */ +void NBN_Server_Stop(NBN_Server *server); -#define NBN_CLIENT_ACCEPTED_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 3) /* Reserved message type */ -#define NBN_SERVER_DATA_MAX_SIZE 1024 -#define NBN_CONNECTION_DATA_MAX_SIZE 512 +// TODO: doc +NBN_ConnectionHandle *NBN_Server_GetConnection(NBN_Server *server, NBN_Connection_ID); -typedef struct NBN_ClientAcceptedMessage -{ - unsigned int length; - uint8_t data[NBN_SERVER_DATA_MAX_SIZE]; -} NBN_ClientAcceptedMessage; +// TODO: doc +unsigned int NBN_Server_GetClientCount(NBN_Server *server); -NBN_ClientAcceptedMessage *NBN_ClientAcceptedMessage_Create(void); -void NBN_ClientAcceptedMessage_Destroy(NBN_ClientAcceptedMessage *); -int NBN_ClientAcceptedMessage_Serialize(NBN_ClientAcceptedMessage *, NBN_Stream *); +NBN_ConnectionHandle *NBN_Server_GetNextClient(NBN_Server *server, NBN_Client_Iterator *it); -#pragma endregion /* NBN_ClientAcceptedMessage */ +/** + * Poll game server events. + * + * This function should be called in a loop until it returns NBN_NO_EVENT. + * + * @return The code of the polled event or NBN_NO_EVENT when there is no more events. + */ +NBN_Server_Event NBN_Server_Poll(NBN_Server *server); -#pragma region NBN_ByteArrayMessage +/** + * Pack all enqueued messages into packets and send them. + * + * This should be called at a relatively high frequency, probably at the end of + * every game tick. + * + * @return 0 when successful, -1 otherwise + */ +int NBN_Server_Flush(NBN_Server *server); -#define NBN_BYTE_ARRAY_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 4) /* Reserved message type */ -#define NBN_BYTE_ARRAY_MAX_SIZE 4096 +/** + * Close a client's connection without a specific code (default code is -1) + * + * @param conn The connection to close + * + * @return 0 when successful, -1 otherwise + */ +int NBN_Server_CloseClient(NBN_Server *server, NBN_ConnectionHandle *conn); -typedef struct NBN_ByteArrayMessage -{ - uint8_t bytes[NBN_BYTE_ARRAY_MAX_SIZE]; - unsigned int length; -} NBN_ByteArrayMessage; +/** + * Close a client's connection with a specific code. + * + * The code is an arbitrary integer to let the client knows + * why his connection was closed. + * + * @param conn The connection to close + * + * @return 0 when successful, -1 otherwise + */ +int NBN_Server_CloseClientWithCode(NBN_Server *server, NBN_ConnectionHandle *conn, int code); -NBN_ByteArrayMessage *NBN_ByteArrayMessage_Create(void); -void NBN_ByteArrayMessage_Destroy(NBN_ByteArrayMessage *); -int NBN_ByteArrayMessage_Serialize(NBN_ByteArrayMessage *, NBN_Stream *); +// TODO: doc +NBN_Writer *NBN_Server_CreateMessage(NBN_Server *server, uint8_t type, uint8_t channel_id, + NBN_ConnectionHandle *receiver); -#pragma endregion /* NBN_ByteArrayMessage */ +// TODO: doc +NBN_Writer *NBN_Server_CreateReliableMessage(NBN_Server *server, uint8_t type, NBN_ConnectionHandle *receiver); -#pragma region NBN_DisconnectionMessage +// TODO: doc +NBN_Writer *NBN_Server_CreateUnreliableMessage(NBN_Server *server, uint8_t type, NBN_ConnectionHandle *receiver); -#define NBN_DISCONNECTION_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 7) /* Reserved message type */ +// TODO: doc +NBN_Reader *NBN_Server_ReadMessage(NBN_Server *server); -void *NBN_DisconnectionMessage_Create(void); -void NBN_DisconnectionMessage_Destroy(void *); -int NBN_DisconnectionMessage_Serialize(void *, NBN_Stream *); +// TODO: doc +NBN_Writer *NBN_Server_WriteConnectionData(NBN_Server *server); -#pragma endregion /* NBN_DisconnectionMessage */ +// TODO: doc +int NBN_Server_AcceptIncomingConnection(NBN_Server *server); -#pragma region NBN_ConnectionRequestMessage +/** + * Reject the last client connection request with a specific code. + * + * The code is an arbitrary integer to let the client knows why his connection + * was rejected. + * + * Call this function after receiving a NBN_NEW_CONNECTION event. + * + * @return 0 when successful, -1 otherwise + */ +int NBN_Server_RejectIncomingConnectionWithCode(NBN_Server *server, int code); -#define NBN_CONNECTION_REQUEST_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 8) /* Reserved message type */ +/** + * Reject the last client connection request without any specific code (default code is -1) + * + * Call this function after receiving a NBN_NEW_CONNECTION event. + * + * @return 0 when successful, -1 otherwise + */ +int NBN_Server_RejectIncomingConnection(NBN_Server *server); -typedef struct NBN_ConnectionRequestMessage -{ - unsigned int length; - uint8_t data[NBN_CONNECTION_DATA_MAX_SIZE]; -} NBN_ConnectionRequestMessage; +/** + * Retrieve the last connection to the game server. + * + * Call this function after receiving a NBN_NEW_CONNECTION event. + * + * @return A pointer to a NBN_Connection representing the new connection + */ +NBN_ConnectionHandle *NBN_Server_GetIncomingConnection(NBN_Server *server); -NBN_ConnectionRequestMessage *NBN_ConnectionRequestMessage_Create(void); -void NBN_ConnectionRequestMessage_Destroy(NBN_ConnectionRequestMessage *); -int NBN_ConnectionRequestMessage_Serialize(NBN_ConnectionRequestMessage *, NBN_Stream *); +// TODO: doc +NBN_Reader *NBN_Server_ReadConnectionRequestData(NBN_Server *server); -#pragma endregion /* NBN_ConnectionRequestMessage */ +/** + * Return the information about the last disconnected client. + * + * Call this function after receiving a NBN_CLIENT_DISCONNECTED event. + * See NBN_DisconnectionInfo struct. + * + * @return information about the last disconnected client + */ +NBN_DisconnectionInfo NBN_Server_GetDisconnectionInfo(NBN_Server *server); -#pragma region NBN_RPC_Message +/** + * Retrieve the info about the last received message. + * + * Call this function when receiving a NBN_CLIENT_MESSAGE_RECEIVED event to access + * information about the message. + * + * @return A structure containing information about the received message + */ +NBN_MessageInfo NBN_Server_GetMessageInfo(NBN_Server *server); -#define NBN_RPC_MESSAGE_TYPE (NBN_MAX_MESSAGE_TYPES - 9) /* Reserved message type */ +/** + * Retrieve network stats about the game server. + * + * @return A structure containing network related stats about the game server + */ +NBN_ServerStats NBN_Server_GetStats(NBN_Server *server); -typedef struct NBN_RPC_Message -{ - unsigned int id; - unsigned int param_count; - NBN_RPC_Param params[NBN_RPC_MAX_PARAM_COUNT]; -} NBN_RPC_Message; +#ifdef __EMSCRIPTEN__ -void *NBN_RPC_Message_Create(void); -void NBN_RPC_Message_Destroy(NBN_RPC_Message *); -int NBN_RPC_Message_Serialize(NBN_RPC_Message *, NBN_Stream *); +/* EMSCRIPTEN WEBRTC DRIVER SPECIFIC API */ -#pragma endregion /* NBN_RPC_Message */ +typedef struct NBN_WebRTC_Config { + bool enable_tls; + const char *cert_path; + const char *key_path; +} NBN_WebRTC_Config; -#pragma region NBN_Channel +void NBN_Client_SetWebRTC_Config(NBN_Client *client, NBN_WebRTC_Config config); +void NBN_Server_SetWebRTC_Config(NBN_Server *server, NBN_WebRTC_Config config); -#define NBN_CHANNEL_BUFFER_SIZE 1024 -#define NBN_CHANNEL_CHUNKS_BUFFER_SIZE 255 -#define NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE 2048 -#define NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE 512 +// TODO: ice servers currently hard coded in driver js code +#define NBN_WEBRTC_DEFAULT_CONFIG (NBN_WebRTC_Config){.enable_tls = false, .cert_path = NULL, .key_path = NULL}; -/* IMPORTANT: if you add a library reserved channel below, make sure to update NBN_LIBRARY_RESERVED_CHANNELS */ +#endif // __EMSCRIPTEN__ -/* Library reserved unreliable ordered channel */ -#define NBN_CHANNEL_RESERVED_UNRELIABLE (NBN_MAX_CHANNELS - 1) +#ifdef NBN_WEBRTC_NATIVE -/* Library reserved reliable ordered channel */ -#define NBN_CHANNEL_RESERVED_RELIABLE (NBN_MAX_CHANNELS - 2) +/* NATIVE WEBRTC DRIVER SPECIFIC API */ -/* Library reserved messages channel */ -#define NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES (NBN_MAX_CHANNELS - 3) +#include -typedef NBN_Channel *(*NBN_ChannelBuilder)(void); -typedef void (*NBN_ChannelDestructor)(NBN_Channel *); +typedef struct NBN_WebRTC_Config { + bool enable_tls; + const char *cert_path; + const char *key_path; + const char *passphrase; + const char **ice_servers; + unsigned int ice_servers_count; + rtcLogLevel log_level; +} NBN_WebRTC_Config; -typedef struct NBN_MessageSlot -{ - NBN_Message message; - double last_send_time; - bool free; -} NBN_MessageSlot; +static const char *default_ice_servers[] = {"stun:stun01.sipphone.com"}; -struct NBN_Channel -{ - uint8_t id; - uint8_t *write_chunk_buffer; - uint16_t next_outgoing_message_id; - uint16_t next_recv_message_id; - unsigned int next_outgoing_message_pool_slot; - unsigned int outgoing_message_count; - unsigned int chunk_count; - unsigned int write_chunk_buffer_size; - unsigned int read_chunk_buffer_size; - unsigned int next_outgoing_chunked_message; - int last_received_chunk_id; - uint8_t *read_chunk_buffer; - NBN_ChannelDestructor destructor; - NBN_Connection *connection; - NBN_MessageSlot outgoing_message_slot_buffer[NBN_CHANNEL_BUFFER_SIZE]; - NBN_MessageSlot recved_message_slot_buffer[NBN_CHANNEL_BUFFER_SIZE]; - NBN_MessageChunk *recv_chunk_buffer[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; - NBN_OutgoingMessage outgoing_message_pool[NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE]; +#ifdef NBN_DEBUG - bool (*AddReceivedMessage)(NBN_Channel *, NBN_Message *); - bool (*AddOutgoingMessage)(NBN_Channel *, NBN_Message *); - NBN_Message *(*GetNextRecvedMessage)(NBN_Channel *); - NBN_Message *(*GetNextOutgoingMessage)(NBN_Channel *, double); - int (*OnOutgoingMessageAcked)(NBN_Channel *, uint16_t); - int (*OnOutgoingMessageSent)(NBN_Channel *, NBN_Message *); -}; +#define NBN_DEFAULT_RTC_LOG_LEVEL RTC_LOG_DEBUG -void NBN_Channel_Destroy(NBN_Channel *); -bool NBN_Channel_AddChunk(NBN_Channel *, NBN_Message *); -int NBN_Channel_ReconstructMessageFromChunks(NBN_Channel *, NBN_Connection *, NBN_Message *); -void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *, unsigned int); -void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *, unsigned int); -void NBN_Channel_UpdateMessageLastSendTime(NBN_Channel *, NBN_Message *, double); +#else -/* - Unreliable ordered +#define NBN_DEFAULT_RTC_LOG_LEVEL RTC_LOG_ERROR - Guarantee that messages will be received in order, does not however guarantee that all message will be received when - packets get lost. This is meant to be used for time critical messages when it does not matter that much if they - end up getting lost. A good example would be game snaphosts when any newly received snapshot is more up to date - than the previous one. - */ -typedef struct NBN_UnreliableOrderedChannel -{ - NBN_Channel base; - uint16_t last_received_message_id; - unsigned int next_outgoing_message_slot; -} NBN_UnreliableOrderedChannel; +#endif // NBN_DEBUG -NBN_UnreliableOrderedChannel *NBN_UnreliableOrderedChannel_Create(void); +#define NBN_WEBRTC_DEFAULT_CONFIG \ + (NBN_WebRTC_Config) { \ + .enable_tls = false, .cert_path = NULL, .key_path = NULL, .passphrase = NULL, \ + .ice_servers = default_ice_servers, \ + .ice_servers_count = sizeof(default_ice_servers) / sizeof(default_ice_servers[0]), \ + .log_level = NBN_DEFAULT_RTC_LOG_LEVEL \ + } -/* - Reliable ordered +void NBN_Client_SetWebRTC_Config(NBN_Client *client, NBN_WebRTC_Config config); +void NBN_Server_SetWebRTC_Config(NBN_Server *server, NBN_WebRTC_Config config); - Will guarantee that all messages will be received in order. Use this when you want to make sure a message - will be received, for example for chat messages or initial game world state. - */ -typedef struct NBN_ReliableOrderedChannel -{ - NBN_Channel base; - uint16_t oldest_unacked_message_id; - uint16_t most_recent_message_id; - bool ack_buffer[NBN_CHANNEL_BUFFER_SIZE]; -} NBN_ReliableOrderedChannel; +#endif -NBN_ReliableOrderedChannel *NBN_ReliableOrderedChannel_Create(void); +#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) -#pragma endregion /* NBN_Channel */ +void NBN_Client_SetPing(NBN_Client *client, float v); +void NBN_Client_SetJitter(NBN_Client *client, float v); +void NBN_Client_SetPacketLoss(NBN_Client *client, float v); +void NBN_Client_SetPacketDuplication(NBN_Client *client, float v); -#pragma region NBN_Connection +void NBN_Server_SetPing(NBN_Server *server, float v); +void NBN_Server_SetJitter(NBN_Server *server, float v); +void NBN_Server_SetPacketLoss(NBN_Server *server, float v); +void NBN_Server_SetPacketDuplication(NBN_Server *server, float v); -#define NBN_MAX_PACKET_ENTRIES 1024 +#else -/* Maximum number of packets that can be sent in a single flush - * - * IMPORTANT: do not increase this, it will break packet acks -*/ -#define NBN_CONNECTION_MAX_SENT_PACKET_COUNT 16 +#define NBN_PacketSimulator_Disabled \ + do { \ + } while (0); -/* Number of seconds before the connection is considered stale and get closed */ -#define NBN_CONNECTION_STALE_TIME_THRESHOLD 3 +#define NBN_Client_SetPing(client, v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetJitter(client, v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetPacketLoss(client, v) NBN_PacketSimulator_Disabled +#define NBN_Client_SetPacketDuplication(client, v) NBN_PacketSimulator_Disabled -typedef struct NBN_MessageEntry -{ - uint16_t id; - uint8_t channel_id; -} NBN_MessageEntry; +#define NBN_Server_SetPing(server, v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetJitter(server, v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetPacketLoss(server, v) NBN_PacketSimulator_Disabled +#define NBN_Server_SetPacketDuplication(server, v) NBN_PacketSimulator_Disabled -typedef struct NBN_PacketEntry -{ - bool acked; - bool flagged_as_lost; - unsigned int messages_count; - double send_time; - NBN_MessageEntry messages[NBN_MAX_MESSAGES_PER_PACKET]; -} NBN_PacketEntry; +#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ -typedef struct NBN_ConnectionStats -{ - double ping; - unsigned int total_lost_packets; - float packet_loss; - float upload_bandwidth; - float download_bandwidth; -} NBN_ConnectionStats; +#ifdef __cplusplus +} +#endif -#ifdef NBN_DEBUG - -typedef struct NBN_ConnectionDebugCallback { - void (*OnMessageAddedToRecvQueue)(NBN_Connection *, NBN_Message *); -} NBN_ConnectionDebugCallback; - -#endif /* NBN_DEBUG */ - -struct NBN_Connection -{ - uint32_t id; - uint32_t protocol_id; - double last_recv_packet_time; /* Used to detect stale connections */ - double last_flush_time; /* Last time the send queue was flushed */ - double last_read_packets_time; /* Last time packets were read from the network driver */ - unsigned int downloaded_bytes; /* Keep track of bytes read from the socket (used for download bandwith calculation) */ - int vector_pos; /* Position of the connection in the connections vector */ - uint8_t is_accepted : 1; - uint8_t is_stale : 1; - uint8_t is_closed : 1; - struct NBN_Endpoint *endpoint; - NBN_Driver *driver; /* Network driver used for that connection */ - NBN_Channel *channels[NBN_MAX_CHANNELS]; /* Messages channeling (sending & receiving) */ - NBN_ConnectionStats stats; - void *driver_data; /* Data attached to the connection by the underlying driver */ - -#ifdef NBN_DEBUG - NBN_ConnectionDebugCallback debug_callbacks; -#endif /* NBN_DEBUG */ - - /* - * Packet sequencing & acking - */ - uint16_t next_packet_seq_number; - uint16_t last_received_packet_seq_number; - uint32_t packet_send_seq_buffer[NBN_MAX_PACKET_ENTRIES]; - NBN_PacketEntry packet_send_buffer[NBN_MAX_PACKET_ENTRIES]; - uint32_t packet_recv_seq_buffer[NBN_MAX_PACKET_ENTRIES]; -}; - -typedef struct NBN_ConnectionListNode NBN_ConnectionListNode; - -/* Linked list of connections */ -struct NBN_ConnectionListNode -{ - NBN_Connection *conn; - NBN_ConnectionListNode *next; - NBN_ConnectionListNode *prev; -}; - -NBN_Connection *NBN_Connection_Create(uint32_t, uint32_t, NBN_Endpoint *, NBN_Driver *, void *); -void NBN_Connection_Destroy(NBN_Connection *); -int NBN_Connection_ProcessReceivedPacket(NBN_Connection *, NBN_Packet *, double); -int NBN_Connection_EnqueueOutgoingMessage(NBN_Connection *, NBN_Channel *, NBN_Message *); -int NBN_Connection_FlushSendQueue(NBN_Connection *, double); -int NBN_Connection_InitChannel(NBN_Connection *, NBN_Channel *); -bool NBN_Connection_CheckIfStale(NBN_Connection *, double); - -#pragma endregion /* NBN_Connection */ - -#pragma region NBN_EventQueue - -#define NBN_NO_EVENT 0 /* No event left in the events queue */ -#define NBN_SKIP_EVENT 1 /* Indicates that the event should be skipped */ -#define NBN_EVENT_QUEUE_CAPACITY 1024 - -typedef union NBN_EventData -{ - NBN_MessageInfo message_info; - NBN_Connection *connection; - NBN_ConnectionHandle connection_handle; -} NBN_EventData; - -typedef struct NBN_Event -{ - int type; - NBN_EventData data; -} NBN_Event; - -typedef struct NBN_EventQueue -{ - NBN_Event events[NBN_EVENT_QUEUE_CAPACITY]; - unsigned int head; - unsigned int tail; - unsigned int count; -} NBN_EventQueue; - -NBN_EventQueue *NBN_EventQueue_Create(void); -void NBN_EventQueue_Destroy(NBN_EventQueue *); -bool NBN_EventQueue_Enqueue(NBN_EventQueue *, NBN_Event); -bool NBN_EventQueue_Dequeue(NBN_EventQueue *, NBN_Event *); -bool NBN_EventQueue_IsEmpty(NBN_EventQueue *); - -#pragma endregion /* NBN_EventQueue */ - -#pragma region Packet simulator - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - -/** - * Threading. - * - * Windows headers need to be included by the user of the library before - * the nbnet header because of some winsock2.h / windows.h dependencies. - */ -#ifndef NBNET_WINDOWS -#include // Threading -#endif /* NBNET_WINDOWS */ - -#define NBN_GameClient_SetPing(v) { nbn_game_client.endpoint.packet_simulator.ping = v; } -#define NBN_GameClient_SetJitter(v) { nbn_game_client.endpoint.packet_simulator.jitter = v; } -#define NBN_GameClient_SetPacketLoss(v) { nbn_game_client.endpoint.packet_simulator.packet_loss_ratio = v; } -#define NBN_GameClient_SetPacketDuplication(v) { nbn_game_client.endpoint.packet_simulator.packet_duplication_ratio = v; } - -#define NBN_GameServer_SetPing(v) { nbn_game_server.endpoint.packet_simulator.ping = v; } -#define NBN_GameServer_SetJitter(v) { nbn_game_server.endpoint.packet_simulator.jitter = v; } -#define NBN_GameServer_SetPacketLoss(v) { nbn_game_server.endpoint.packet_simulator.packet_loss_ratio = v; } -#define NBN_GameServer_SetPacketDuplication(v) { nbn_game_server.endpoint.packet_simulator.packet_duplication_ratio = v; } - -typedef struct NBN_PacketSimulatorEntry NBN_PacketSimulatorEntry; - -struct NBN_PacketSimulatorEntry -{ - NBN_Packet packet; - NBN_Connection *receiver; - double delay; - double enqueued_at; - struct NBN_PacketSimulatorEntry *next; - struct NBN_PacketSimulatorEntry *prev; -}; - -typedef struct NBN_PacketSimulator -{ - NBN_Endpoint *endpoint; - NBN_PacketSimulatorEntry *head_packet; - NBN_PacketSimulatorEntry *tail_packet; - unsigned int packet_count; - -#ifdef NBNET_WINDOWS - HANDLE queue_mutex; - HANDLE thread; -#else - pthread_mutex_t queue_mutex; - pthread_t thread; -#endif - - bool running; - unsigned int total_dropped_packets; - - /* Settings */ - float packet_loss_ratio; - float current_packet_loss_ratio; - float packet_duplication_ratio; - double ping; - double jitter; -} NBN_PacketSimulator; - -void NBN_PacketSimulator_Init(NBN_PacketSimulator *, NBN_Endpoint *); -int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *); -void NBN_PacketSimulator_Start(NBN_PacketSimulator *); -void NBN_PacketSimulator_Stop(NBN_PacketSimulator *); - -#else - -#define NBN_GameClient_SetPing(v) NBN_LogInfo("NBN_Debug_SetPing: packet simulator is not enabled, ignore") -#define NBN_GameClient_SetJitter(v) NBN_LogInfo("NBN_Debug_SetJitter: packet simulator is not enabled, ignore") -#define NBN_GameClient_SetPacketLoss(v) NBN_LogInfo("NBN_Debug_SetPacketLoss: packet simulator is not enabled, ignore") -#define NBN_GameClient_SetPacketDuplication(v) NBN_LogInfo("NBN_Debug_SetPacketDuplication: packet simulator is not enabled, ignore") - -#define NBN_GameServer_SetPing(v) NBN_LogInfo("NBN_Debug_SetPing: packet simulator is not enabled, ignore") -#define NBN_GameServer_SetJitter(v) NBN_LogInfo("NBN_Debug_SetJitter: packet simulator is not enabled, ignore") -#define NBN_GameServer_SetPacketLoss(v) NBN_LogInfo("NBN_Debug_SetPacketLoss: packet simulator is not enabled, ignore") -#define NBN_GameServer_SetPacketDuplication(v) NBN_LogInfo("NBN_Debug_SetPacketDuplication: packet simulator is not enabled, ignore") - -#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ - -#pragma endregion /* Packet simulator */ - -#pragma region NBN_Endpoint - -#define NBN_IsReservedMessage(type) (type == NBN_MESSAGE_CHUNK_TYPE || type == NBN_CLIENT_CLOSED_MESSAGE_TYPE \ -|| type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || type == NBN_BYTE_ARRAY_MESSAGE_TYPE \ -|| type == NBN_DISCONNECTION_MESSAGE_TYPE || type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE \ -|| type == NBN_RPC_MESSAGE_TYPE) - -struct NBN_Endpoint -{ - NBN_ChannelBuilder channel_builders[NBN_MAX_CHANNELS]; - NBN_ChannelDestructor channel_destructors[NBN_MAX_CHANNELS]; - NBN_MessageBuilder message_builders[NBN_MAX_MESSAGE_TYPES]; - NBN_MessageDestructor message_destructors[NBN_MAX_MESSAGE_TYPES]; - NBN_MessageSerializer message_serializers[NBN_MAX_MESSAGE_TYPES]; - NBN_EventQueue event_queue; - NBN_RPC rpcs[NBN_RPC_MAX]; - bool is_server; - double time; /* Current time */ - -#ifdef NBN_DEBUG - NBN_ConnectionDebugCallback debug_callbacks; -#endif - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - NBN_PacketSimulator packet_simulator; -#endif -}; - -#pragma endregion /* NBN_Endpoint */ - -#pragma region NBN_GameClient - -enum -{ - /* Client is connected to server */ - NBN_CONNECTED = 2, - - /* Client is disconnected from the server */ - NBN_DISCONNECTED, - - /* Client has received a message from the server */ - NBN_MESSAGE_RECEIVED -}; - -typedef struct NBN_GameClient -{ - NBN_Endpoint endpoint; - NBN_Connection *server_connection; - bool is_connected; - uint8_t server_data[NBN_SERVER_DATA_MAX_SIZE]; /* Data sent by the server when accepting the client's connection */ - unsigned int server_data_len; /* Length of the received server data in bytes */ - NBN_Event last_event; - int closed_code; -} NBN_GameClient; - -extern NBN_GameClient nbn_game_client; - -/** - * Start the game client and send a connection request to the server. - * - * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate - * @param host Host to connect to - * @param port Port to connect to - * - * @return 0 when successully started, -1 otherwise - */ -int NBN_GameClient_Start(const char *protocol_name, const char *host, uint16_t port); - -/** - * Same as NBN_GameClient_Start but with additional parameters. - * - * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate - * @param host Host to connect to - * @param port Port to connect to - * @param data Array of bytes to send to the server upon connection (cannot exceed NBN_CONNECTION_DATA_MAX_SIZE) - * @param length length of the array of bytes - * - * @return 0 when successully started, -1 otherwise - */ -int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t port, uint8_t *data, unsigned int length); - -/** - * Disconnect from the server. The client can be restarted by calling NBN_GameClient_Start or NBN_GameClient_StartWithData again. - */ -void NBN_GameClient_Stop(void); - -/** - * Read the server data that was received upon connection into a preallocated buffer (the buffer must have a size of at least NBN_SERVER_DATA_MAX_SIZE bytes). - * - * @param data The target buffer to copy the server data to - * - * @return the length of the server data in bytes - */ -unsigned int NBN_GameClient_ReadServerData(uint8_t *data); - -/** - * Register a type of message on the game client, has to be called after NBN_GameClient_Start. - * - * - * @param msg_type A user defined message type, can be any value from 0 to 245 (245 to 255 are reserved by nbnet). - * @param msg_builder The function responsible for building the message - * @param msg_destructor The function responsible for destroying the message (and releasing memory) - * @param msg_serializer The function responsible for serializing the message - */ -void NBN_GameClient_RegisterMessage( - uint8_t msg_type, - NBN_MessageBuilder msg_builder, - NBN_MessageDestructor msg_destructor, - NBN_MessageSerializer msg_serializer); - -/** - * Poll game client events. - * - * This function should be called in a loop until it returns NBN_NO_EVENT. - * - * @return The code of the polled event or NBN_NO_EVENT when there is no more events. - */ -int NBN_GameClient_Poll(void); - -/** - * Pack all enqueued messages into packets and send them. - * - * This should be called at a relatively high frequency, probably at the end of - * every game tick. - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendPackets(void); - -/** - * Send a byte array message on a given channel. - * - * It's recommended to use NBN_GameClient_SendUnreliableByteArray or NBN_GameClient_SendReliableByteArray - * unless you really want to use a specific channel. - * - * @param bytes The byte array to send - * @param length The length of the byte array to send - * @param channel_id The ID of the channel to send the message on - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendByteArray(uint8_t *bytes, unsigned int length, uint8_t channel_id); - -/** - * Send a message to the server on a given channel. - * - * It's recommended to use NBN_GameClient_SendUnreliableMessage or NBN_GameClient_SendReliableMessage - * unless you really want to use a specific channel. - * - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameClient_RegisterMessage) - * @param channel_id The ID of the channel to send the message on - * @param msg_data A pointer to the message to send (managed by user code) - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendMessage(uint8_t msg_type, uint8_t channel_id, void *msg_data); - -/** - * Send a message to the server, unreliably. - * - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameClient_RegisterMessage) - * @param msg_data A pointer to the message to send (managed by user code) - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendUnreliableMessage(uint8_t msg_type, void *msg_data); - -/** - * Send a message to the server, reliably. - * - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameClient_RegisterMessage) - * @param msg_data A pointer to the message to send (pointing to user code memory) - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendReliableMessage(uint8_t msg_type, void *msg_data); - -/* - * Send a byte array to the server, unreliably. - * - * @param bytes The byte array to send - * @param length The length of the byte array to send - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendUnreliableByteArray(uint8_t *bytes, unsigned int length); - -/* - * Send a byte array to the server, reliably. - * - * @param bytes The byte array to send - * @param length The length of the byte array to send - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameClient_SendReliableByteArray(uint8_t *bytes, unsigned int length); - -/** - * For drivers only! NOT MEANT TO BE USED BY USER CODE. - */ -NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data, uint32_t protocol_id); - -/** - * Retrieve the info about the last received message. - * - * Call this function when receiveing a NBN_MESSAGE_RECEIVED event to access - * information about the message. - * - * @return A structure containing information about the received message - */ -NBN_MessageInfo NBN_GameClient_GetMessageInfo(void); - -/** - * Retrieve network stats about the game client. - * - * @return A structure containing network related stats about the game client - */ -NBN_ConnectionStats NBN_GameClient_GetStats(void); - -/** - * Retrieve the code sent by the server when closing the connection. - * - * Call this function when receiving a NBN_DISCONNECTED event. - * - * @return The code used by the server when closing the connection or -1 (the default code) - */ -int NBN_GameClient_GetServerCloseCode(void); - -/** - * @return true if connected, false otherwise - */ -bool NBN_GameClient_IsConnected(void); - -/** - * Register a new RPC on the game client. - * - * The same RPC must be register on both the game server and the game clients. - * - * @param id User defined RPC ID, must be an integer between 0 and NBN_RPC_MAX - * @param signature The RPC signature - * @param func The function to call on the callee end (NULL on the caller end) - * - * @return true if packet encryption is enabled, false otherwise - */ -int NBN_GameClient_RegisterRPC(unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func); - -/** - * Call a previously registered RPC on the game server. - * - * @param id The ID of the RPC to execute on the game server (must be a registered ID) - */ -int NBN_GameClient_CallRPC(unsigned int id, ...); - -#ifdef NBN_DEBUG - -void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback); - -#endif /* NBN_DEBUG */ - -#pragma endregion /* NBN_GameClient */ - -#pragma region NBN_GameServer - -#define NBN_MAX_CLIENTS 1024 - -enum -{ - /* A new client has connected */ - NBN_NEW_CONNECTION = 2, - - /* A client has disconnected */ - NBN_CLIENT_DISCONNECTED, - - /* A message has been received from a client */ - NBN_CLIENT_MESSAGE_RECEIVED -}; - -typedef struct NBN_GameServerStats -{ - float upload_bandwidth; /* Total upload bandwith of the game server */ - float download_bandwidth; /* Total download bandwith of the game server */ -} NBN_GameServerStats; - -typedef struct NBN_GameServer -{ - NBN_Endpoint endpoint; - NBN_ConnectionVector *clients; /* Vector of clients connections */ - NBN_ConnectionTable *clients_table; /* Hash table of clients connections */ - NBN_ConnectionListNode *closed_clients_head; - NBN_GameServerStats stats; - NBN_Event last_event; - uint8_t last_connection_data[NBN_CONNECTION_DATA_MAX_SIZE]; - unsigned int last_connection_data_len; -} NBN_GameServer; - -extern NBN_GameServer nbn_game_server; - -/** - * Start the game server. - * - * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate - * @param port The server's port - * - * @return 0 when successfully started, -1 otherwise - */ -int NBN_GameServer_Start(const char *protocol_name, uint16_t port); - -/** - * Same as NBN_GameServer_Start but with additional parameters. - * - * @param protocol_name A unique protocol name, the clients and the server must use the same one or they won't be able to communicate - * @param port The server's port - * - * @return 0 when successfully started, -1 otherwise - */ -int NBN_GameServer_StartEx(const char *protocol_name, uint16_t port); - -/** - * Stop the game server and clean everything up. - */ -void NBN_GameServer_Stop(void); - -/** - * Register a type of message on the game server, has to be called after NBN_GameServer_Start. - * - * - * @param msg_type A user defined message type, can be any value from 0 to 245 (245 to 255 are reserved by nbnet). - * @param msg_builder The function responsible for building the message - * @param msg_destructor The function responsible for destroying the message (and releasing memory) - * @param msg_serializer The function responsible for serializing the message - */ -void NBN_GameServer_RegisterMessage( - uint8_t msg_type, NBN_MessageBuilder msg_builder, NBN_MessageDestructor msg_destructor, NBN_MessageSerializer msg_serializer); - -/** - * Poll game server events. - * - * This function should be called in a loop until it returns NBN_NO_EVENT. - * - * @return The code of the polled event or NBN_NO_EVENT when there is no more events. - */ -int NBN_GameServer_Poll(void); - -/** - * Pack all enqueued messages into packets and send them. - * - * This should be called at a relatively high frequency, probably at the end of - * every game tick. - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendPackets(void); - -/** - * For drivers only! NOT MEANT TO BE USED BY USER CODE. - */ -NBN_Connection *NBN_GameServer_CreateClientConnection(int, void *, uint32_t, uint32_t); - -/** - * Close a client's connection without a specific code (default code is -1) - * - * @param connection_handle The connection to close - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle); - -/** - * Close a client's connection with a specific code. - * - * The code is an arbitrary integer to let the client knows - * why his connection was closed. - * - * @param connection_handle The connection to close - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle connection_handle, int code); - -/** - * Send a byte array to a client on a given channel. - * - * @param connection_handle The connection to send the message to - * @param bytes The byte array to send - * @param length The length of the byte array to send - * @param channel_id The ID of the channel to send the message on - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length, uint8_t channel_id); - -/** - * Send a message to a client on a given channel. - * - * It's recommended to use NBN_GameServer_SendUnreliableMessageTo or NBN_GameServer_SendReliableMessageTo - * unless you really want to use a specific channel. - * - * @param connection_handle The connection to send the message to - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameServer_RegisterMessage) - * @param channel_id The ID of the channel to send the message on - * @param msg_data A pointer to the message to send - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t channel_id, void *msg_data); - -/** - * Send a message to a client, unreliably. - * - * @param connection_handle The connection to send the message to - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameServer_RegisterMessage) - * @param msg_data A pointer to the message to send (managed by user code) - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendUnreliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, void *msg_data); - -/** - * Send a message to a client, reliably. - * - * @param connection_handle The connection to send the message to - * @param msg_type The type of message to send; it needs to be registered (see NBN_GameServer_RegisterMessage) - * @param msg_data A pointer to the message to send (managed by user code) - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendReliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, void *msg_data); - -/** - * Send a byte array to a client, unreliably. - * - * @param connection_handle The connection to send the message to - * @param bytes The byte array to send - * @param length The length of the byte array to send - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendUnreliableByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length); - -/** - * Send a byte array to a client, reliably. - * - * @param connection_handle The connection to send the message to - * @param bytes The byte array to send - * @param length The length of the byte array to send - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_SendReliableByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length); - -/** - * Accept the last client connection request and send a blob of data to the client. - * The client can read that data using the NBN_GameClient_ReadServerData function. - * If you do not wish to send any data to the client upon accepting his connection, use the NBN_GameServer_AcceptIncomingConnection function instead. - * - * Call this function after receiving a NBN_NEW_CONNECTION event. - * - * @param data Data to send - * @param length Data length in bytes - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_AcceptIncomingConnectionWithData(uint8_t *data, unsigned int length); - -/** - * Accept the last client connection request. - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_AcceptIncomingConnection(void); - -/** - * Reject the last client connection request with a specific code. - * - * The code is an arbitrary integer to let the client knows why his connection - * was rejected. - * - * Call this function after receiving a NBN_NEW_CONNECTION event. - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_RejectIncomingConnectionWithCode(int code); - -/** - * Reject the last client connection request without any specific code (default code is -1) - * - * Call this function after receiving a NBN_NEW_CONNECTION event. - * - * @return 0 when successful, -1 otherwise - */ -int NBN_GameServer_RejectIncomingConnection(void); - -/** - * Retrieve the last connection to the game server. - * - * Call this function after receiving a NBN_NEW_CONNECTION event. - * - * @return A NBN_ConnectionHandle for the new connection - */ -NBN_ConnectionHandle NBN_GameServer_GetIncomingConnection(void); - -/** - * Read the last connection data into a preallocated buffer. The target buffer must have a length of at least NBN_CONNECTION_DATA_MAX_SIZE bytes. - * - * @param data the buffer to copy the connection data to - * - * @return the length in bytes of the connection data - */ -unsigned int NBN_GameServer_ReadIncomingConnectionData(uint8_t *data); - -/** - * Return the last disconnected client. - * - * Call this function after receiving a NBN_CLIENT_DISCONNECTED event. - * - * @return The last disconnected connection - */ -NBN_ConnectionHandle NBN_GameServer_GetDisconnectedClient(void); - -/** - * Retrieve the info about the last received message. - * - * Call this function when receiving a NBN_CLIENT_MESSAGE_RECEIVED event to access - * information about the message. - * - * @return A structure containing information about the received message - */ -NBN_MessageInfo NBN_GameServer_GetMessageInfo(void); - -/** - * Retrieve network stats about the game server. - * - * @return A structure containing network related stats about the game server - */ -NBN_GameServerStats NBN_GameServer_GetStats(void); - -/** - * Register a new RPC on the game server. - * - * The same RPC must be register on both the game server and the game clients. - * - * @param id User defined RPC ID, must be an integer between 0 and NBN_RPC_MAX - * @param signature The RPC signature - * @param func The function to call on the callee end (NULL on the caller end) - * - * @return true if packet encryption is enabled, false otherwise - */ -int NBN_GameServer_RegisterRPC(unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func); - -/** - * Call a previously registered RPC on a given client. - * - * @param id The ID of the RPC to execute (must be a registered ID) - * @param connection_handle The connection on which to call the RPC - */ -int NBN_GameServer_CallRPC(unsigned int id, NBN_ConnectionHandle connection_handle, ...); - -#ifdef NBN_DEBUG - -void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback); - -#endif /* NBN_DEBUG */ - -#pragma endregion /* NBN_GameServer */ - -#pragma region Network driver - -#define NBN_MAX_DRIVERS 4 - -typedef enum NBN_DriverEvent -{ - // Client events - NBN_DRIVER_CLI_PACKET_RECEIVED, - - // Server events - NBN_DRIVER_SERV_CLIENT_CONNECTED, - NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, -} NBN_DriverEvent; - -typedef void (*NBN_Driver_StopFunc)(void); -typedef int (*NBN_Driver_RecvPacketsFunc)(void); - -typedef int (*NBN_Driver_ClientStartFunc)(uint32_t, const char *, uint16_t); -typedef int (*NBN_Driver_ClientSendPacketFunc)(NBN_Packet *); - -typedef int (*NBN_Driver_ServerStartFunc)(uint32_t, uint16_t); -typedef int (*NBN_Driver_ServerSendPacketToFunc)(NBN_Packet *, NBN_Connection *); -typedef void (*NBN_Driver_ServerRemoveConnection)(NBN_Connection *); - -typedef struct NBN_DriverImplementation -{ - /* Client functions */ - NBN_Driver_ClientStartFunc cli_start; - NBN_Driver_StopFunc cli_stop; - NBN_Driver_RecvPacketsFunc cli_recv_packets; - NBN_Driver_ClientSendPacketFunc cli_send_packet; - - /* Server functions */ - NBN_Driver_ServerStartFunc serv_start; - NBN_Driver_StopFunc serv_stop; - NBN_Driver_RecvPacketsFunc serv_recv_packets; - NBN_Driver_ServerSendPacketToFunc serv_send_packet_to; - NBN_Driver_ServerRemoveConnection serv_remove_connection; -} NBN_DriverImplementation; - -struct NBN_Driver -{ - int id; - const char *name; - NBN_DriverImplementation impl; -}; - -/** - * Register a new network driver, at least one network driver has to be registered. - * - * @param id ID of the driver, must be unique and within 0 and NBN_MAX_DRIVERS - * @param name Name of the driver - * @param signature Driver implementation (structure containing all driver implementation function pointers) - */ -void NBN_Driver_Register(int id, const char *name, NBN_DriverImplementation implementation); - -/** - * Let nbnet know about specific network events happening within a network driver. - * - * @param ev Event type - * @param data Arbitrary data about the event -*/ -int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data); - -#pragma endregion /* Network driver */ - -#pragma region Utils - -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef ABS -#define ABS(v) (((v) > 0) ? (v) : -(v)) -#endif - -#define SEQUENCE_NUMBER_GT(seq1, seq2) \ - ((seq1 > seq2 && (seq1 - seq2) <= 32767) || (seq1 < seq2 && (seq2 - seq1) >= 32767)) -#define SEQUENCE_NUMBER_GTE(seq1, seq2) \ - ((seq1 >= seq2 && (seq1 - seq2) <= 32767) || (seq1 <= seq2 && (seq2 - seq1) >= 32767)) -#define SEQUENCE_NUMBER_LT(seq1, seq2) \ - ((seq1 < seq2 && (seq2 - seq1) <= 32767) || (seq1 > seq2 && (seq1 - seq2) >= 32767)) - -#pragma endregion /* Utils */ - -#pragma endregion /* Declarations */ - -#endif /* NBNET_H */ - -#pragma region Implementations - -#ifdef NBNET_IMPL - -static NBN_Driver nbn_drivers[NBN_MAX_DRIVERS] = { - {.id = -1}, - {.id = -1}, - {.id = -1}, - {.id = -1} -}; - -static unsigned int nbn_driver_count = 0; - -#pragma region NBN_ConnectionVector - -#define NBN_CONNECTION_VECTOR_INITIAL_CAPACITY 32 - -static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int new_capacity); - -static NBN_ConnectionVector *NBN_ConnectionVector_Create(void) -{ - NBN_ConnectionVector *vector = (NBN_ConnectionVector *)NBN_Allocator(sizeof(NBN_ConnectionVector)); - - vector->connections = NULL; - vector->capacity = 0; - vector->count = 0; - - NBN_ConnectionVector_Grow(vector, NBN_CONNECTION_VECTOR_INITIAL_CAPACITY); - return vector; -} - -static void NBN_ConnectionVector_Destroy(NBN_ConnectionVector *vector) -{ - NBN_Deallocator(vector->connections); - NBN_Deallocator(vector); -} - -static void NBN_ConnectionVector_Add(NBN_ConnectionVector *vector, NBN_Connection *conn) -{ - assert(conn->vector_pos == -1); - - if (vector->count >= vector->capacity) - { - NBN_ConnectionVector_Grow(vector, vector->capacity * 2); - } - - unsigned int position = vector->count; - - if (vector->connections[position]) - { - NBN_LogError("Failed to add connection (id: %d) to vector: position %d is not empty", conn->id, position); - NBN_Abort(); - } - - conn->vector_pos = position; - vector->connections[position] = conn; - vector->count++; -} - -static uint32_t NBN_ConnectionVector_RemoveAt(NBN_ConnectionVector *vector, unsigned int position) -{ - NBN_Connection *conn = vector->connections[position]; - - if (conn == NULL) return 0; - - // Make sure that connections are stored contiguously in memory - - NBN_Connection *last_conn = vector->connections[vector->count - 1]; - - vector->connections[position] = last_conn; - vector->connections[vector->count - 1] = NULL; - last_conn->vector_pos = position; // last connection in the vector is moved to the position of the removed one - vector->count--; - - return conn->id; -} - -static void NBN_ConnectionVector_Grow(NBN_ConnectionVector *vector, unsigned int new_capacity) -{ - vector->connections = (NBN_Connection **)NBN_Reallocator(vector->connections, sizeof(NBN_Connection *) * new_capacity); - - if (vector->connections == NULL) - { - NBN_LogError("Failed to allocate memory to grow the connection vector"); - NBN_Abort(); - } - - for (unsigned int i = 0; i < new_capacity - vector->capacity; i++) - vector->connections[vector->capacity + i] = NULL; - - vector->capacity = new_capacity; -} - -#pragma endregion // NBN_ConnectionVector - -#pragma region NBN_ConnectionTable - -#define NBN_CONNECTION_TABLE_INITIAL_CAPACITY 32 -#define NBN_CONNECTION_TABLE_LOAD_FACTOR 0.75f - -static void NBN_ConnectionTable_InsertEntry(NBN_ConnectionTable *table, unsigned int slot, NBN_Connection *conn); -static void NBN_ConnectionTable_RemoveEntry(NBN_ConnectionTable *table, unsigned int slot); -static unsigned int NBN_ConnectionTable_Hash(int hash); -static void NBN_ConnectionTable_Grow(NBN_ConnectionTable *table, unsigned int new_capacity); - -static NBN_ConnectionTable *NBN_ConnectionTable_Create(void) -{ - NBN_ConnectionTable *table = (NBN_ConnectionTable *)NBN_Allocator(sizeof(NBN_ConnectionTable)); - - table->connections = NULL; - table->capacity = 0; - - NBN_ConnectionTable_Grow(table, NBN_CONNECTION_TABLE_INITIAL_CAPACITY); - - for (unsigned int i = 0; i < table->capacity; i++) - table->connections[i] = NULL; - - return table; -} - -static void NBN_ConnectionTable_Destroy(NBN_ConnectionTable *table) -{ - // Make sure we don't destroy the actual connections as they will be destroyed with the connection vector - NBN_Deallocator(table->connections); - NBN_Deallocator(table); -} - -static void NBN_ConnectionTable_Add(NBN_ConnectionTable *table, NBN_Connection *conn) -{ - unsigned int hash = NBN_ConnectionTable_Hash(conn->id); - unsigned int slot = hash % table->capacity; - - NBN_Connection *entry = table->connections[slot]; - - if (entry == NULL) - { - // no collision, just insert in slot - NBN_ConnectionTable_InsertEntry(table, slot, conn); - return; - } - - // collision, do quadratic probing until we find a free slot - - unsigned int i = 0; - - do - { - slot = (hash + (int)pow(i, 2)) % table->capacity; - entry = table->connections[slot]; - - i++; - } while (entry != NULL); - - NBN_ConnectionTable_InsertEntry(table, slot, conn); -} - -static bool NBN_ConnectionTable_Remove(NBN_ConnectionTable *table, uint32_t id) -{ - unsigned int hash = NBN_ConnectionTable_Hash(id); - unsigned int slot = hash % table->capacity; - NBN_Connection *conn = table->connections[slot]; - - if (conn && conn->id == id) - { - NBN_ConnectionTable_RemoveEntry(table, slot); - return true; - } - - // quadratic probing - - unsigned int i = 0; - - do - { - slot = (hash + (int)pow(i, 2)) % table->capacity; - conn = table->connections[slot]; - - if (conn && conn->id == id) - { - NBN_ConnectionTable_RemoveEntry(table, slot); - return true; - } - - i++; - } while (i < table->capacity); - - return false; -} - -static void NBN_ConnectionTable_InsertEntry(NBN_ConnectionTable *table, unsigned int slot, NBN_Connection *conn) -{ - table->connections[slot] = conn; - table->count++; - table->load_factor = (float)table->count / table->capacity; - - if (table->load_factor > NBN_CONNECTION_TABLE_LOAD_FACTOR) - { - NBN_ConnectionTable_Grow(table, table->capacity * 2); - } -} - -static void NBN_ConnectionTable_RemoveEntry(NBN_ConnectionTable *table, unsigned int slot) -{ - table->connections[slot] = NULL; - table->count--; - table->load_factor = (float)table->count / table->capacity; -} - -static NBN_Connection *NBN_ConnectionTable_Get(NBN_ConnectionTable *table, uint32_t id) -{ - unsigned int hash = NBN_ConnectionTable_Hash(id); - unsigned int slot = hash % table->capacity; - NBN_Connection *conn = table->connections[slot]; - - if (conn && conn->id == id) - return conn; - - // quadratic probing - - unsigned int i = 0; - - do - { - slot = (hash + (int)pow(i, 2)) % table->capacity; - conn = table->connections[slot]; - - if (conn != NULL && conn->id == id) - { - return conn; - } - - i++; - } while (i < table->capacity); - - return NULL; -} - -static unsigned int NBN_ConnectionTable_Hash(int hash) -{ - hash = ((hash >> 16) ^ hash) * 0x45d9f3b; - hash = ((hash >> 16) ^ hash) * 0x45d9f3b; - hash = (hash >> 16) ^ hash; - - return hash; -} - -static void NBN_ConnectionTable_Grow(NBN_ConnectionTable *table, unsigned int new_capacity) -{ - unsigned int old_capacity = table->capacity; - NBN_Connection** old_connections_array = table->connections; - NBN_Connection** new_connections_array = (NBN_Connection **)NBN_Allocator(sizeof(NBN_Connection *) * new_capacity); - - for (unsigned int i = 0; i < new_capacity; i++) - { - new_connections_array[i] = NULL; - } - - table->connections = new_connections_array; - table->capacity = new_capacity; - table->count = 0; - table->load_factor = 0; - - // rehash - - assert(old_connections_array || old_capacity == 0); - - for (unsigned int i = 0; i < old_capacity; i++) - { - if (old_connections_array[i]) - { - NBN_ConnectionTable_Add(table, old_connections_array[i]); - } - } - - if (old_connections_array) NBN_Deallocator(old_connections_array); -} - -#pragma region // NBN_ConnectionTable - -#pragma region Memory management - -NBN_MemoryManager nbn_mem_manager; - -static void MemoryManager_Init(void); -static void MemoryManager_Deinit(void); -static void *MemoryManager_Alloc(unsigned int); -static void MemoryManager_Dealloc(void *, unsigned int); - -#if !defined(NBN_DISABLE_MEMORY_POOLING) - -static void MemPool_Init(NBN_MemPool *, size_t, unsigned int); -static void MemPool_Deinit(NBN_MemPool *); -static void MemPool_Grow(NBN_MemPool *, unsigned int); -static void *MemPool_Alloc(NBN_MemPool *); -static void MemPool_Dealloc(NBN_MemPool *, void *); - -#endif /* NBN_DISABLE_MEMORY_POOLING */ - -static void MemoryManager_Init(void) -{ -#ifdef NBN_DISABLE_MEMORY_POOLING - NBN_LogDebug("MemoryManager_Init without pooling!"); - - nbn_mem_manager.mem_sizes[NBN_MEM_MESSAGE_CHUNK] = sizeof(NBN_MessageChunk); - nbn_mem_manager.mem_sizes[NBN_MEM_BYTE_ARRAY_MESSAGE] = sizeof(NBN_ByteArrayMessage); - nbn_mem_manager.mem_sizes[NBN_MEM_CONNECTION] = sizeof(NBN_Connection); - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - nbn_mem_manager.mem_sizes[NBN_MEM_PACKET_SIMULATOR_ENTRY] = sizeof(NBN_PacketSimulatorEntry); -#endif -#else - NBN_LogDebug("MemoryManager_Init with pooling!"); - - MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_MESSAGE_CHUNK], sizeof(NBN_MessageChunk), 256); - MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_BYTE_ARRAY_MESSAGE], sizeof(NBN_ByteArrayMessage), 256); - MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_CONNECTION], sizeof(NBN_Connection), 16); - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - MemPool_Init(&nbn_mem_manager.mem_pools[NBN_MEM_PACKET_SIMULATOR_ENTRY], sizeof(NBN_PacketSimulatorEntry), 32); -#endif -#endif /* NBN_DISABLE_MEMORY_POOLING */ -} - -static void MemoryManager_Deinit(void) -{ -#if !defined(NBN_DISABLE_MEMORY_POOLING) - MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_MESSAGE_CHUNK]); - MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_BYTE_ARRAY_MESSAGE]); - MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_CONNECTION]); - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - MemPool_Deinit(&nbn_mem_manager.mem_pools[NBN_MEM_PACKET_SIMULATOR_ENTRY]); -#endif -#endif /* NBN_DISABLE_MEMORY_POOLING */ -} - -static void *MemoryManager_Alloc(unsigned int mem_tag) -{ -#ifdef NBN_DISABLE_MEMORY_POOLING - return NBN_Allocator(nbn_mem_manager.mem_sizes[mem_tag]); -#else - return MemPool_Alloc(&nbn_mem_manager.mem_pools[mem_tag]); -#endif /* NBN_DISABLE_MEMORY_POOLING */ -} - -static void MemoryManager_Dealloc(void *ptr, unsigned int mem_tag) -{ -#ifdef NBN_DISABLE_MEMORY_POOLING - (void)mem_tag; - - NBN_Deallocator(ptr); -#else - MemPool_Dealloc(&nbn_mem_manager.mem_pools[mem_tag], ptr); -#endif /* NBN_DISABLE_MEMORY_POOLING */ -} - -#if !defined(NBN_DISABLE_MEMORY_POOLING) - -static void MemPool_Init(NBN_MemPool *pool, size_t block_size, unsigned int initial_block_count) -{ - pool->block_size = MAX(block_size, sizeof(NBN_MemPoolFreeBlock)); - pool->block_idx = 0; - pool->block_count = 0; - pool->free = NULL; - pool->blocks = NULL; - - MemPool_Grow(pool, initial_block_count); -} - -static void MemPool_Deinit(NBN_MemPool *pool) -{ - for (unsigned int i = 0; i < pool->block_count; i++) - NBN_Deallocator(pool->blocks[i]); - - NBN_Deallocator(pool->blocks); -} - -static void *MemPool_Alloc(NBN_MemPool *pool) -{ - if (pool->free) - { - void *block = pool->free; - - pool->free = pool->free->next; - - return block; - } - - if (pool->block_idx == pool->block_count) - MemPool_Grow(pool, pool->block_count * 2); - - void *block = pool->blocks[pool->block_idx]; - - pool->block_idx++; - - return block; -} - -static void MemPool_Dealloc(NBN_MemPool *pool, void *ptr) -{ - NBN_MemPoolFreeBlock *free = pool->free; - - pool->free = (NBN_MemPoolFreeBlock*)ptr; - pool->free->next = free; -} - -static void MemPool_Grow(NBN_MemPool *pool, unsigned int block_count) -{ - pool->blocks = (uint8_t**)NBN_Reallocator(pool->blocks, sizeof(uint8_t *) * block_count); - - for (unsigned int i = 0; i < block_count - pool->block_count; i++) - pool->blocks[pool->block_idx + i] = (uint8_t*)NBN_Allocator(pool->block_size); - - pool->block_count = block_count; -} - -#endif /* NBN_DISABLE_MEMORY_POOLING */ - -#pragma endregion /* Memory management */ - -#pragma region Serialization - -static unsigned int GetRequiredNumberOfBitsFor(unsigned int v) -{ - unsigned int a = v | (v >> 1); - unsigned int b = a | (a >> 2); - unsigned int c = b | (b >> 4); - unsigned int d = c | (c >> 8); - unsigned int e = d | (d >> 16); - unsigned int f = e >> 1; - - unsigned int i = f - ((f >> 1) & 0x55555555); - unsigned int j = (((i >> 2) & 0x33333333) + (i & 0x33333333)); - unsigned int k = (((j >> 4) + j) & 0x0f0f0f0f); - unsigned int l = k + (k >> 8); - unsigned int m = l + (l >> 16); - - return (m & 0x0000003f) + 1; -} - -#pragma region NBN_BitReader - -static void BitReader_ReadFromBuffer(NBN_BitReader *); - -void NBN_BitReader_Init(NBN_BitReader *bit_reader, uint8_t *buffer, unsigned int size) -{ - bit_reader->size = size; - bit_reader->buffer = buffer; - bit_reader->scratch = 0; - bit_reader->scratch_bits_count = 0; - bit_reader->byte_cursor = 0; -} - -int NBN_BitReader_Read(NBN_BitReader *bit_reader, Word *word, unsigned int number_of_bits) -{ - *word = 0; - - if (number_of_bits > bit_reader->scratch_bits_count) - { - unsigned int needed_bytes = (number_of_bits - bit_reader->scratch_bits_count - 1) / 8 + 1; - - if (bit_reader->byte_cursor + needed_bytes > bit_reader->size) - return NBN_ERROR; - - BitReader_ReadFromBuffer(bit_reader); - } - - *word |= (bit_reader->scratch & (((uint64_t)1 << number_of_bits) - 1)); - bit_reader->scratch >>= number_of_bits; - bit_reader->scratch_bits_count -= number_of_bits; - - return 0; -} - -static void BitReader_ReadFromBuffer(NBN_BitReader *bit_reader) -{ - unsigned int bytes_count = MIN(bit_reader->size - bit_reader->byte_cursor, WORD_BYTES); - Word word = 0; - - memcpy(&word, bit_reader->buffer + bit_reader->byte_cursor, bytes_count); - - bit_reader->scratch |= (uint64_t)word << bit_reader->scratch_bits_count; - bit_reader->scratch_bits_count += bytes_count * 8; - bit_reader->byte_cursor += bytes_count; -} - -#pragma endregion /* NBN_BitReader */ - -#pragma region NBN_BitWriter - -static int BitWriter_FlushScratchBits(NBN_BitWriter *, unsigned int); - -void NBN_BitWriter_Init(NBN_BitWriter *bit_writer, uint8_t *buffer, unsigned int size) -{ - bit_writer->size = size; - bit_writer->buffer = buffer; - bit_writer->scratch = 0; - bit_writer->scratch_bits_count = 0; - bit_writer->byte_cursor = 0; -} - -int NBN_BitWriter_Write(NBN_BitWriter *bit_writer, Word value, unsigned int number_of_bits) -{ - bit_writer->scratch |= ((uint64_t)value << bit_writer->scratch_bits_count); - - if ((bit_writer->scratch_bits_count += number_of_bits) >= WORD_BITS) - return BitWriter_FlushScratchBits(bit_writer, WORD_BITS); - - return 0; -} - -int NBN_BitWriter_Flush(NBN_BitWriter *bit_writer) -{ - return BitWriter_FlushScratchBits(bit_writer, bit_writer->scratch_bits_count); -} - -static int BitWriter_FlushScratchBits(NBN_BitWriter *bit_writer, unsigned int number_of_bits) -{ - if (bit_writer->scratch_bits_count < 1) - return 0; - - unsigned int bytes_count = (number_of_bits - 1) / 8 + 1; - - assert(bytes_count <= WORD_BYTES); - - if (bit_writer->byte_cursor + bytes_count > bit_writer->size) - return NBN_ERROR; - - Word word = 0 | (bit_writer->scratch & (((uint64_t)1 << number_of_bits) - 1)); - - memcpy(bit_writer->buffer + bit_writer->byte_cursor, &word, bytes_count); - - bit_writer->scratch >>= number_of_bits; - bit_writer->scratch_bits_count -= number_of_bits; - bit_writer->byte_cursor += bytes_count; - - return 0; -} - -#pragma endregion /* NBN_BitWriter */ - -#pragma region NBN_ReadStream - -void NBN_ReadStream_Init(NBN_ReadStream *read_stream, uint8_t *buffer, unsigned int size) -{ - read_stream->base.type = NBN_STREAM_READ; - read_stream->base.serialize_uint_func = (NBN_Stream_SerializeUInt)NBN_ReadStream_SerializeUint; - read_stream->base.serialize_uint64_func = (NBN_Stream_SerializeUInt64)NBN_ReadStream_SerializeUint64; - read_stream->base.serialize_int_func = (NBN_Stream_SerializeInt)NBN_ReadStream_SerializeInt; - read_stream->base.serialize_float_func = (NBN_Stream_SerializeFloat)NBN_ReadStream_SerializeFloat; - read_stream->base.serialize_bool_func = (NBN_Stream_SerializeBool)NBN_ReadStream_SerializeBool; - read_stream->base.serialize_padding_func = (NBN_Stream_SerializePadding)NBN_ReadStream_SerializePadding; - read_stream->base.serialize_bytes_func = (NBN_Stream_SerializeBytes)NBN_ReadStream_SerializeBytes; - - NBN_BitReader_Init(&read_stream->bit_reader, buffer, size); -} - -int NBN_ReadStream_SerializeUint(NBN_ReadStream *read_stream, unsigned int *value, unsigned int min, unsigned int max) -{ - assert(min <= max); - - if (NBN_BitReader_Read(&read_stream->bit_reader, value, BITS_REQUIRED(min, max)) < 0) - return NBN_ERROR; - - *value += min; - -#ifdef NBN_DEBUG - assert(*value >= min && *value <= max); -#else - if (*value < min || *value > max) - return NBN_ERROR; -#endif - - return 0; -} - -int NBN_ReadStream_SerializeUint64(NBN_ReadStream *read_stream, uint64_t *value) -{ - union uint64_to_bytes - { - uint64_t v; - uint8_t bytes[8]; - } u; - - if (NBN_ReadStream_SerializeBytes(read_stream, u.bytes, 8) < 0) - return NBN_ERROR; - - *value = u.v; - - return 0; -} - -int NBN_ReadStream_SerializeInt(NBN_ReadStream *read_stream, int *value, int min, int max) -{ - assert(min <= max); - - bool isNegative = 0; - unsigned int abs_min = MIN(abs(min), abs(max)); - unsigned int abs_max = MAX(abs(min), abs(max)); - - *value = abs(*value); - - if (NBN_ReadStream_SerializeBool(read_stream, &isNegative) < 0) - return NBN_ERROR; - - if (NBN_ReadStream_SerializeUint(read_stream, (unsigned int *)value, (min < 0 && max > 0) ? 0 : abs_min, abs_max) < 0) - return NBN_ERROR; - - if (isNegative) - *value *= -1; - - return 0; -} - -int NBN_ReadStream_SerializeFloat(NBN_ReadStream *read_stream, float *value, float min, float max, int precision) -{ - assert(min <= max); - - unsigned int mult = pow(10, precision); - int i_min = min * mult; - int i_max = max * mult; - int i_val = 0; - - if (NBN_ReadStream_SerializeInt(read_stream, &i_val, i_min, i_max) < 0) - return NBN_ERROR; - - *value = (float)i_val / mult; - - return 0; -} - -int NBN_ReadStream_SerializeBool(NBN_ReadStream *read_stream, bool *value) -{ - Word v; - - if (NBN_BitReader_Read(&read_stream->bit_reader, &v, 1) < 0) - return NBN_ERROR; - - if (v > 1) - return NBN_ERROR; - - *value = v; - - return 0; -} - -int NBN_ReadStream_SerializePadding(NBN_ReadStream *read_stream) -{ - // if we are at the beginning of a new byte, no need to pad - if (read_stream->bit_reader.scratch_bits_count % 8 == 0) - return 0; - - Word value; - unsigned int padding = read_stream->bit_reader.scratch_bits_count % 8; - int ret = NBN_BitReader_Read(&read_stream->bit_reader, &value, padding); - -#ifdef NBN_DEBUG - assert(value == 0); -#else - if (value != 0) - return NBN_ERROR; -#endif - - return ret; -} - -int NBN_ReadStream_SerializeBytes(NBN_ReadStream *read_stream, uint8_t *bytes, unsigned int length) -{ - if (length == 0) - return NBN_ERROR; - - if (NBN_ReadStream_SerializePadding(read_stream) < 0) - { - return NBN_ERROR; - } - - NBN_BitReader *bit_reader = &read_stream->bit_reader; - - // make sure we are at the start of a new byte after applying padding - assert(bit_reader->scratch_bits_count % 8 == 0); - - if (length * 8 <= bit_reader->scratch_bits_count && length <= sizeof(Word)) - { - // the byte array is fully contained inside the read word - - Word word; - - if (NBN_BitReader_Read(bit_reader, &word, length * 8) < 0) - { - return NBN_ERROR; - } - - memcpy(bytes, &word, length); - } - else - { - // reading is done word by word and in this case, the start of byte array has already been read - // therefore we need to "roll back" the byte cursor so it is at the very begining of the byte array - // inside the buffer - - bit_reader->byte_cursor -= (bit_reader->scratch_bits_count / 8); - bit_reader->scratch_bits_count = 0; - bit_reader->scratch = 0; - - memcpy(bytes, bit_reader->buffer + bit_reader->byte_cursor, length); - - bit_reader->byte_cursor += length; - } - - return 0; -} - -#pragma endregion /* NBN_ReadStream */ - -#pragma region NBN_WriteStream - -void NBN_WriteStream_Init(NBN_WriteStream *write_stream, uint8_t *buffer, unsigned int size) -{ - write_stream->base.type = NBN_STREAM_WRITE; - write_stream->base.serialize_uint_func = (NBN_Stream_SerializeUInt)NBN_WriteStream_SerializeUint; - write_stream->base.serialize_uint64_func = (NBN_Stream_SerializeUInt64)NBN_WriteStream_SerializeUint64; - write_stream->base.serialize_int_func = (NBN_Stream_SerializeInt)NBN_WriteStream_SerializeInt; - write_stream->base.serialize_float_func = (NBN_Stream_SerializeFloat)NBN_WriteStream_SerializeFloat; - write_stream->base.serialize_bool_func = (NBN_Stream_SerializeBool)NBN_WriteStream_SerializeBool; - write_stream->base.serialize_padding_func = (NBN_Stream_SerializePadding)NBN_WriteStream_SerializePadding; - write_stream->base.serialize_bytes_func = (NBN_Stream_SerializeBytes)NBN_WriteStream_SerializeBytes; - - NBN_BitWriter_Init(&write_stream->bit_writer, buffer, size); -} - -int NBN_WriteStream_SerializeUint( - NBN_WriteStream *write_stream, unsigned int *value, unsigned int min, unsigned int max) -{ - assert(min <= max); - assert(*value >= min && *value <= max); - - if (NBN_BitWriter_Write(&write_stream->bit_writer, *value - min, BITS_REQUIRED(min, max)) < 0) - return NBN_ERROR; - - return 0; -} - -int NBN_WriteStream_SerializeUint64(NBN_WriteStream *write_stream, uint64_t *value) -{ - union uint64_to_bytes - { - uint64_t v; - uint8_t bytes[8]; - } u; - - u.v = *value; - - return NBN_WriteStream_SerializeBytes(write_stream, u.bytes, 8); -} - -int NBN_WriteStream_SerializeInt(NBN_WriteStream *write_stream, int *value, int min, int max) -{ - assert(min <= max); - - unsigned int isNegative = 0; - unsigned int abs_min = MIN(abs(min), abs(max)); - unsigned int abs_max = MAX(abs(min), abs(max)); - - isNegative = *value < 0; - *value = abs(*value); - - if (NBN_WriteStream_SerializeUint(write_stream, &isNegative, 0, 1) < 0) - return NBN_ERROR; - - if (NBN_WriteStream_SerializeUint( - write_stream, (unsigned int *)value, (min < 0 && max > 0) ? 0 : abs_min, abs_max) < 0) - return NBN_ERROR; - - if (isNegative) - *value *= -1; - - return 0; -} - -int NBN_WriteStream_SerializeFloat(NBN_WriteStream *write_stream, float *value, float min, float max, int precision) -{ - assert(min <= max); - - unsigned int mult = pow(10, precision); - int i_min = min * mult; - int i_max = max * mult; - int i_val = *value * mult; - - if (NBN_WriteStream_SerializeInt(write_stream, &i_val, i_min, i_max) < 0) - return NBN_ERROR; - - return 0; -} - -int NBN_WriteStream_SerializeBool(NBN_WriteStream *write_stream, bool *value) -{ - int v = *value; - - assert(v >= 0 && v <= 1); - - if (NBN_BitWriter_Write(&write_stream->bit_writer, v, 1) < 0) - return NBN_ERROR; - - return 0; -} - -int NBN_WriteStream_SerializePadding(NBN_WriteStream *write_stream) -{ - // if we are at the beginning of a new byte, no need to pad - if (write_stream->bit_writer.scratch_bits_count % 8 == 0) - return 0; - - unsigned int padding = 8 - (write_stream->bit_writer.scratch_bits_count % 8); - - return NBN_BitWriter_Write(&write_stream->bit_writer, 0, padding); -} - -int NBN_WriteStream_SerializeBytes(NBN_WriteStream *write_stream, uint8_t *bytes, unsigned int length) -{ - if (length == 0) - return NBN_ERROR; - - if (NBN_WriteStream_SerializePadding(write_stream) < 0) - return NBN_ERROR; - - NBN_BitWriter *bit_writer = &write_stream->bit_writer; - - // make sure we are at the start of a new byte after applying padding - assert(bit_writer->scratch_bits_count % 8 == 0); - - if (NBN_WriteStream_Flush(write_stream) < 0) - return NBN_ERROR; - - // make sure everything has been flushed to the buffer before writing the byte array - assert(bit_writer->scratch_bits_count == 0); - - if (bit_writer->byte_cursor + length > bit_writer->size) - return NBN_ERROR; - - memcpy(bit_writer->buffer + bit_writer->byte_cursor, bytes, length); - - bit_writer->byte_cursor += length; - - return 0; -} - -int NBN_WriteStream_Flush(NBN_WriteStream *write_stream) -{ - return NBN_BitWriter_Flush(&write_stream->bit_writer); -} - -#pragma endregion /* NBN_WriteStream */ - -#pragma region NBN_MeasureStream - -void NBN_MeasureStream_Init(NBN_MeasureStream *measure_stream) -{ - measure_stream->base.type = NBN_STREAM_MEASURE; - measure_stream->base.serialize_uint_func = (NBN_Stream_SerializeUInt)NBN_MeasureStream_SerializeUint; - measure_stream->base.serialize_uint64_func = (NBN_Stream_SerializeUInt64)NBN_MeasureStream_SerializeUint64; - measure_stream->base.serialize_int_func = (NBN_Stream_SerializeInt)NBN_MeasureStream_SerializeInt; - measure_stream->base.serialize_float_func = (NBN_Stream_SerializeFloat)NBN_MeasureStream_SerializeFloat; - measure_stream->base.serialize_bool_func = (NBN_Stream_SerializeBool)NBN_MeasureStream_SerializeBool; - measure_stream->base.serialize_padding_func = (NBN_Stream_SerializePadding)NBN_MeasureStream_SerializePadding; - measure_stream->base.serialize_bytes_func = (NBN_Stream_SerializeBytes)NBN_MeasureStream_SerializeBytes; - - measure_stream->number_of_bits = 0; -} - -int NBN_MeasureStream_SerializeUint( - NBN_MeasureStream *measure_stream, unsigned int *value, unsigned int min, unsigned int max) -{ - (void)*value; - - assert(min <= max); - // assert(*value >= min && *value <= max); - - unsigned int number_of_bits = BITS_REQUIRED(min, max); - - measure_stream->number_of_bits += number_of_bits; - - return number_of_bits; -} - -int NBN_MeasureStream_SerializeUint64(NBN_MeasureStream *measure_stream, unsigned int *value) -{ - (void)value; - - return NBN_MeasureStream_SerializeBytes(measure_stream, NULL, 8); -} - -int NBN_MeasureStream_SerializeInt(NBN_MeasureStream *measure_stream, int *value, int min, int max) -{ - assert(min <= max); - assert(*value >= min && *value <= max); - - unsigned int abs_min = MIN(abs(min), abs(max)); - unsigned int abs_max = MAX(abs(min), abs(max)); - unsigned int abs_value = abs(*value); - unsigned number_of_bits = NBN_MeasureStream_SerializeUint( - measure_stream, &abs_value, (min < 0 && max > 0) ? 0 : abs_min, abs_max); - - measure_stream->number_of_bits++; // +1 for int sign - - return number_of_bits + 1; -} - -int NBN_MeasureStream_SerializeFloat( - NBN_MeasureStream *measure_stream, float *value, float min, float max, int precision) -{ - assert(min <= max); - assert(*value >= min && *value <= max); - - unsigned int mult = pow(10, precision); - int i_min = min * mult; - int i_max = max * mult; - int i_val = *value * mult; - - return NBN_MeasureStream_SerializeInt(measure_stream, &i_val, i_min, i_max); -} - -int NBN_MeasureStream_SerializeBool(NBN_MeasureStream *measure_stream, bool *value) -{ - (void)value; - - measure_stream->number_of_bits++; - - return 1; -} - -int NBN_MeasureStream_SerializePadding(NBN_MeasureStream *measure_stream) -{ - if (measure_stream->number_of_bits % 8 == 0) - return 0; - - unsigned int padding = 8 - (measure_stream->number_of_bits % 8); - - measure_stream->number_of_bits += padding; - - return padding; -} - -int NBN_MeasureStream_SerializeBytes(NBN_MeasureStream *measure_stream, uint8_t *bytes, unsigned int length) -{ - (void)bytes; - - NBN_MeasureStream_SerializePadding(measure_stream); - - unsigned int bits = length * 8; - - measure_stream->number_of_bits += bits; - - return bits; -} - -void NBN_MeasureStream_Reset(NBN_MeasureStream *measure_stream) -{ - measure_stream->number_of_bits = 0; -} - -#pragma endregion /* NBN_MeasureStream */ - -#pragma endregion /* Serialization */ - -#pragma region NBN_Packet - -static int Packet_SerializeHeader(NBN_PacketHeader *, NBN_Stream *); - -void NBN_Packet_InitWrite( - NBN_Packet *packet, uint32_t protocol_id, uint16_t seq_number, uint16_t ack, uint32_t ack_bits) -{ - packet->header.protocol_id = protocol_id; - packet->header.messages_count = 0; - packet->header.seq_number = seq_number; - packet->header.ack = ack; - packet->header.ack_bits = ack_bits; - - packet->mode = NBN_PACKET_MODE_WRITE; - packet->sender = NULL; - packet->size = 0; - packet->sealed = false; - packet->m_stream.number_of_bits = 0; - - NBN_WriteStream_Init(&packet->w_stream, packet->buffer + NBN_PACKET_HEADER_SIZE, NBN_PACKET_MAX_DATA_SIZE); - NBN_MeasureStream_Init(&packet->m_stream); -} - -int NBN_Packet_InitRead( - NBN_Packet *packet, NBN_Connection *sender, uint8_t buffer[NBN_PACKET_MAX_SIZE], unsigned int size) -{ - packet->mode = NBN_PACKET_MODE_READ; - packet->sender = sender; - packet->size = size; - packet->sealed = false; - - memcpy(packet->buffer, buffer, size); - - NBN_ReadStream header_r_stream; - - NBN_ReadStream_Init(&header_r_stream, packet->buffer, NBN_PACKET_HEADER_SIZE); - - if (Packet_SerializeHeader(&packet->header, (NBN_Stream *)&header_r_stream) < 0) - return NBN_ERROR; - - NBN_ReadStream_Init(&packet->r_stream, packet->buffer + NBN_PACKET_HEADER_SIZE, packet->size); - - return 0; -} - -uint32_t NBN_Packet_ReadProtocolId(uint8_t buffer[NBN_PACKET_MAX_SIZE], unsigned int size) -{ - if (size < NBN_PACKET_HEADER_SIZE) - return 0; - - NBN_ReadStream r_stream; - - NBN_ReadStream_Init(&r_stream, buffer, NBN_PACKET_HEADER_SIZE); - - NBN_PacketHeader header; - - if (Packet_SerializeHeader(&header, (NBN_Stream *)&r_stream) < 0) - return 0; - - return header.protocol_id; -} - -int NBN_Packet_WriteMessage(NBN_Packet *packet, NBN_Message *message, NBN_MessageSerializer msg_serializer) -{ - if (packet->mode != NBN_PACKET_MODE_WRITE || packet->sealed) - return NBN_PACKET_WRITE_ERROR; - - int current_number_of_bits = packet->m_stream.number_of_bits; - - if (NBN_Message_Measure(message, &packet->m_stream, msg_serializer) < 0) - return NBN_PACKET_WRITE_ERROR; - - if ( - packet->header.messages_count >= NBN_MAX_MESSAGES_PER_PACKET || - packet->m_stream.number_of_bits > NBN_PACKET_MAX_DATA_SIZE * 8) - { - packet->m_stream.number_of_bits = current_number_of_bits; - - return NBN_PACKET_WRITE_NO_SPACE; - } - - if (NBN_Message_SerializeHeader(&message->header, (NBN_Stream *)&packet->w_stream) < 0) - return NBN_PACKET_WRITE_ERROR; - - if (NBN_Message_SerializeData(message, (NBN_Stream *)&packet->w_stream, msg_serializer) < 0) - return NBN_PACKET_WRITE_ERROR; - - packet->size = (packet->m_stream.number_of_bits - 1) / 8 + 1; - packet->header.messages_count++; - - return NBN_PACKET_WRITE_OK; -} - -int NBN_Packet_Seal(NBN_Packet *packet, NBN_Connection *connection) -{ - (void) connection; - - if (packet->mode != NBN_PACKET_MODE_WRITE) - return NBN_ERROR; - - if (NBN_WriteStream_Flush(&packet->w_stream) < 0) - return NBN_ERROR; - - packet->size += NBN_PACKET_HEADER_SIZE; - - NBN_WriteStream header_w_stream; - - NBN_WriteStream_Init(&header_w_stream, packet->buffer, NBN_PACKET_HEADER_SIZE); - - if (Packet_SerializeHeader(&packet->header, (NBN_Stream *)&header_w_stream) < 0) - return NBN_ERROR; - - if (NBN_WriteStream_Flush(&header_w_stream) < 0) - return NBN_ERROR; - - packet->sealed = true; - - return 0; -} - -static int Packet_SerializeHeader(NBN_PacketHeader *header, NBN_Stream *stream) -{ - NBN_SerializeBytes(stream, &header->protocol_id, sizeof(header->protocol_id)); - NBN_SerializeBytes(stream, &header->seq_number, sizeof(header->seq_number)); - NBN_SerializeBytes(stream, &header->ack, sizeof(header->ack)); - NBN_SerializeBytes(stream, &header->ack_bits, sizeof(header->ack_bits)); - NBN_SerializeBytes(stream, &header->messages_count, sizeof(header->messages_count)); - - return 0; -} - -#pragma endregion /* NBN_Packet */ - -#pragma region NBN_Message - -int NBN_Message_SerializeHeader(NBN_MessageHeader *message_header, NBN_Stream *stream) -{ - NBN_SerializeBytes(stream, &message_header->id, sizeof(message_header->id)); - NBN_SerializeBytes(stream, &message_header->type, sizeof(message_header->type)); - NBN_SerializeBytes(stream, &message_header->channel_id, sizeof(message_header->channel_id)); - - return 0; -} - -int NBN_Message_Measure(NBN_Message *message, NBN_MeasureStream *m_stream, NBN_MessageSerializer msg_serializer) -{ - if (NBN_Message_SerializeHeader(&message->header, (NBN_Stream *)m_stream) < 0) - return NBN_ERROR; - - if (NBN_Message_SerializeData(message, (NBN_Stream *)m_stream, msg_serializer) < 0) - return NBN_ERROR; - - return m_stream->number_of_bits; -} - -int NBN_Message_SerializeData(NBN_Message *message, NBN_Stream *stream, NBN_MessageSerializer msg_serializer) -{ - return msg_serializer(message->data, stream); -} - -#pragma endregion /* NBN_Message */ - -#pragma region NBN_MessageChunk - -NBN_MessageChunk *NBN_MessageChunk_Create(void) -{ - NBN_MessageChunk *chunk = (NBN_MessageChunk*)MemoryManager_Alloc(NBN_MEM_MESSAGE_CHUNK); - - chunk->outgoing_msg = NULL; - - return chunk; -} - -void NBN_MessageChunk_Destroy(NBN_MessageChunk *chunk) -{ - MemoryManager_Dealloc(chunk, NBN_MEM_MESSAGE_CHUNK); -} - -int NBN_MessageChunk_Serialize(NBN_MessageChunk *msg, NBN_Stream *stream) -{ - NBN_SerializeBytes(stream, &msg->id, 1); - NBN_SerializeBytes(stream, &msg->total, 1); - NBN_SerializeBytes(stream, msg->data, NBN_MESSAGE_CHUNK_SIZE); - - return 0; -} - -#pragma endregion /* NBN_MessageChunk */ - -#pragma region NBN_ClientClosedMessage - -NBN_ClientClosedMessage *NBN_ClientClosedMessage_Create(void) -{ - return (NBN_ClientClosedMessage*)NBN_Allocator(sizeof(NBN_ClientClosedMessage)); -} - -void NBN_ClientClosedMessage_Destroy(NBN_ClientClosedMessage *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_ClientClosedMessage_Serialize(NBN_ClientClosedMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeInt(stream, msg->code, SHRT_MIN, SHRT_MAX); - - return 0; -} - -#pragma endregion /* NBN_ClientClosedMessage */ - -#pragma region NBN_ClientAcceptedMessage - -NBN_ClientAcceptedMessage *NBN_ClientAcceptedMessage_Create(void) -{ - return (NBN_ClientAcceptedMessage*)NBN_Allocator(sizeof(NBN_ClientAcceptedMessage)); -} - -void NBN_ClientAcceptedMessage_Destroy(NBN_ClientAcceptedMessage *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_ClientAcceptedMessage_Serialize(NBN_ClientAcceptedMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->length, 0, NBN_SERVER_DATA_MAX_SIZE); - NBN_SerializeBytes(stream, msg->data, msg->length); - - return 0; -} - -#pragma endregion /* NBN_ClientAcceptedMessage */ - -#pragma region NBN_ByteArrayMessage - -NBN_ByteArrayMessage *NBN_ByteArrayMessage_Create(void) -{ - return (NBN_ByteArrayMessage*)MemoryManager_Alloc(NBN_MEM_BYTE_ARRAY_MESSAGE); -} - -void NBN_ByteArrayMessage_Destroy(NBN_ByteArrayMessage *msg) -{ - MemoryManager_Dealloc(msg, NBN_MEM_BYTE_ARRAY_MESSAGE); -} - -int NBN_ByteArrayMessage_Serialize(NBN_ByteArrayMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->length, 0, NBN_BYTE_ARRAY_MAX_SIZE); - NBN_SerializeBytes(stream, msg->bytes, msg->length); - - return 0; -} - -#pragma endregion /* NBN_ByteArrayMessage */ - -#pragma region NBN_DisconnectionMessage - -void *NBN_DisconnectionMessage_Create(void) -{ - return NULL; -} - -void NBN_DisconnectionMessage_Destroy(void *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_DisconnectionMessage_Serialize(void *msg, NBN_Stream *stream) -{ - (void)msg; - (void)stream; - - return 0; -} - -#pragma endregion /* NBN_DisconnectionMessage */ - -#pragma region NBN_ConnectionRequestMessage - -NBN_ConnectionRequestMessage *NBN_ConnectionRequestMessage_Create(void) -{ - return (NBN_ConnectionRequestMessage *)NBN_Allocator(sizeof(NBN_ConnectionRequestMessage)); -} - -void NBN_ConnectionRequestMessage_Destroy(NBN_ConnectionRequestMessage *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_ConnectionRequestMessage_Serialize(NBN_ConnectionRequestMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->length, 0, NBN_CONNECTION_DATA_MAX_SIZE); - NBN_SerializeBytes(stream, msg->data, msg->length); - - return 0; -} - -#pragma endregion /* NBN_ConnectionRequestMessage */ - -#pragma region NBN_RPC_Message - -void *NBN_RPC_Message_Create(void) -{ - return (NBN_RPC_Message *)NBN_Allocator(sizeof(NBN_RPC_Message)); -} - -void NBN_RPC_Message_Destroy(NBN_RPC_Message *msg) -{ - NBN_Deallocator(msg); -} - -int NBN_RPC_Message_Serialize(NBN_RPC_Message *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->id, 0, NBN_RPC_MAX); - NBN_SerializeUInt(stream, msg->param_count, 0, NBN_RPC_MAX_PARAM_COUNT); - - for (unsigned int i = 0; i < msg->param_count; i++) - { - NBN_RPC_Param *p = &msg->params[i]; - - NBN_SerializeUInt(stream, p->type, 0, 8); - - if (p->type == NBN_RPC_PARAM_INT) - { - NBN_SerializeBytes(stream, &p->value.i, sizeof(int)); - } - else if (p->type == NBN_RPC_PARAM_FLOAT) - { - NBN_SerializeBytes(stream, &p->value.f, sizeof(float)); - } - else if (p->type == NBN_RPC_PARAM_BOOL) - { - NBN_SerializeBytes(stream, &p->value.b, sizeof(bool)); - } - else if (p->type == NBN_RPC_PARAM_STRING) - { - int length = 0; - - if (stream->type == NBN_STREAM_WRITE || stream->type == NBN_STREAM_MEASURE) - { - int l = strlen(p->value.s); - - assert(l + 1 <= NBN_RPC_STRING_MAX_LENGTH); // make sure we have a spot for the terminating byte - assert(l > 0); - - length = l + 1; - } - - NBN_SerializeUInt(stream, length, 0, NBN_RPC_STRING_MAX_LENGTH); - NBN_SerializeString(stream, p->value.s, length); - } - } - - return 0; -} - -#pragma endregion /* NBN_RPC_Message */ - -#pragma region NBN_Connection - -static NBN_OutgoingMessage *Endpoint_CreateOutgoingMessage(NBN_Endpoint *, NBN_Channel*, uint8_t, void *); - -static uint32_t Connection_BuildPacketAckBits(NBN_Connection *); -static int Connection_DecodePacketHeader(NBN_Connection *, NBN_Packet *, double); -static int Connection_AckPacket(NBN_Connection *, uint16_t, double time); -static void Connection_InitOutgoingPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry **); -static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *, uint16_t); -static bool Connection_InsertReceivedPacketEntry(NBN_Connection *, uint16_t); -static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *, uint16_t); -static bool Connection_IsPacketReceived(NBN_Connection *, uint16_t); -static int Connection_SendPacket(NBN_Connection *, NBN_Packet *, NBN_PacketEntry *, double); -static int Connection_ReadNextMessageFromStream(NBN_Connection *, NBN_ReadStream *, NBN_Message *); -static int Connection_ReadNextMessageFromPacket(NBN_Connection *, NBN_Packet *, NBN_Message *); -static void Connection_RecycleMessage(NBN_Channel *, NBN_Message *); -static void Connection_UpdateAveragePing(NBN_Connection *, double); -static void Connection_UpdateAveragePacketLoss(NBN_Connection *, uint16_t); -static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *, float); -static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *, double); -static NBN_RPC_Message *Connection_BuildRPC(NBN_Connection *, NBN_RPC *, va_list); -static void Connection_HandleReceivedRPC(NBN_ConnectionHandle, NBN_Endpoint *, NBN_RPC_Message *); - -NBN_Connection *NBN_Connection_Create(uint32_t id, uint32_t protocol_id, NBN_Endpoint *endpoint, NBN_Driver *driver, void *driver_data) -{ - NBN_Connection *connection = (NBN_Connection*)MemoryManager_Alloc(NBN_MEM_CONNECTION); - - connection->id = id; - connection->protocol_id = protocol_id; - connection->endpoint = endpoint; - connection->last_recv_packet_time = endpoint->time; - connection->next_packet_seq_number = 1; - connection->last_received_packet_seq_number = 0; - connection->last_flush_time = endpoint->time; - connection->last_read_packets_time = endpoint->time; - connection->downloaded_bytes = 0; - connection->is_accepted = false; - connection->is_stale = false; - connection->is_closed = false; - connection->vector_pos = -1; - - for (int i = 0; i < NBN_MAX_CHANNELS; i++) - connection->channels[i] = NULL; - - for (int i = 0; i < NBN_MAX_PACKET_ENTRIES; i++) - { - connection->packet_send_seq_buffer[i] = 0xFFFFFFFF; - connection->packet_recv_seq_buffer[i] = 0xFFFFFFFF; - } - - NBN_ConnectionStats stats = { 0 }; - - connection->stats = stats; - connection->driver = driver; - connection->driver_data = driver_data; - - return connection; -} - -void NBN_Connection_Destroy(NBN_Connection *connection) -{ - for (int i = 0; i < NBN_MAX_CHANNELS; i++) - { - NBN_Channel *channel = connection->channels[i]; - - if (channel) - { - assert(channel->destructor); - channel->destructor(channel); - } - } - - MemoryManager_Dealloc(connection, NBN_MEM_CONNECTION); -} - -int NBN_Connection_ProcessReceivedPacket(NBN_Connection *connection, NBN_Packet *packet, double time) -{ - if (Connection_DecodePacketHeader(connection, packet, time) < 0) - { - NBN_LogError("Failed to decode packet %d header", packet->header.seq_number); - - return NBN_ERROR; - } - - Connection_UpdateAveragePacketLoss(connection, packet->header.ack); - - if (!Connection_InsertReceivedPacketEntry(connection, packet->header.seq_number)) - return 0; - - if (SEQUENCE_NUMBER_GT(packet->header.seq_number, connection->last_received_packet_seq_number)) - connection->last_received_packet_seq_number = packet->header.seq_number; - - for (int i = 0; i < packet->header.messages_count; i++) - { - NBN_Message message; - - message.outgoing_msg = NULL; - - if (Connection_ReadNextMessageFromPacket(connection, packet, &message) < 0) - { - NBN_LogError("Failed to read message from packet"); - - return NBN_ERROR; - } - - NBN_Channel *channel = connection->channels[message.header.channel_id]; - - if (channel->AddReceivedMessage(channel, &message)) - { - NBN_LogTrace("Received message %d on channel %d : added to recv queue", message.header.id, channel->id); - -#ifdef NBN_DEBUG - if (connection->debug_callbacks.OnMessageAddedToRecvQueue) - connection->debug_callbacks.OnMessageAddedToRecvQueue(connection, &message); -#endif - } - else - { - NBN_LogTrace("Received message %d : discarded", message.header.id); - - Connection_RecycleMessage(channel, &message); - } - } - - return 0; -} - -int NBN_Connection_EnqueueOutgoingMessage(NBN_Connection *connection, NBN_Channel *channel, NBN_Message *message) -{ - assert(!connection->is_closed || message->header.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - assert(!connection->is_stale); - (void) connection; - - NBN_LogTrace("Enqueue message of type %d on channel %d", message->header.type, channel->id); - - if (!channel->AddOutgoingMessage(channel, message)) - { - NBN_LogError("Failed to enqueue outgoing message of type %d on channel %d", - message->header.type, message->header.channel_id); - - return NBN_ERROR; - } - - return 0; -} - -int NBN_Connection_FlushSendQueue(NBN_Connection *connection, double time) -{ - NBN_LogTrace("Flushing the send queue"); - - NBN_Packet packet; - NBN_PacketEntry *packet_entry; - unsigned int sent_packet_count = 0; - unsigned int sent_bytes = 0; - - Connection_InitOutgoingPacket(connection, &packet, &packet_entry); - - for (unsigned int i = 0; i < NBN_MAX_CHANNELS; i++) - { - NBN_Channel *channel = connection->channels[i]; - - if (channel == NULL) - continue; - - NBN_LogTrace("Flushing channel %d (message count: %d)", channel->id, channel->outgoing_message_count); - - NBN_Message *message; - unsigned int j = 0; - - while ( - j < channel->outgoing_message_count && - sent_packet_count < NBN_CONNECTION_MAX_SENT_PACKET_COUNT && - (message = channel->GetNextOutgoingMessage(channel, time)) != NULL - ) - { - bool message_sent = false; - NBN_MessageSerializer msg_serializer = connection->endpoint->message_serializers[message->header.type]; - - assert(msg_serializer); - - int ret = NBN_Packet_WriteMessage(&packet, message, msg_serializer); - - if (ret == NBN_PACKET_WRITE_OK) - { - message_sent = true; - } - else if (ret == NBN_PACKET_WRITE_NO_SPACE) - { - if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) - { - NBN_LogError("Failed to send packet %d", packet.header.seq_number); - - return NBN_ERROR; - } - - sent_packet_count++; - sent_bytes += packet.size; - - Connection_InitOutgoingPacket(connection, &packet, &packet_entry); - - int ret = NBN_Packet_WriteMessage(&packet, message, msg_serializer); - - if (ret != NBN_PACKET_WRITE_OK) - { - NBN_LogError("Failed to send packet %d", packet.header.seq_number); - - return NBN_ERROR; - } - - message_sent = true; - } - else if (ret == NBN_PACKET_WRITE_ERROR) - { - NBN_LogError("Failed to write message %d of type %d to packet %d", - message->header.id, message->header.type, packet.header.seq_number); - - return NBN_ERROR; - } - - if (message_sent) - { - NBN_LogTrace("Message %d added to packet %d", message->header.id, packet.header.seq_number); - - NBN_Channel_UpdateMessageLastSendTime(channel, message, time); - - NBN_MessageEntry e = { message->header.id, channel->id }; - - packet_entry->messages[packet_entry->messages_count++] = e; - - if (channel->OnOutgoingMessageSent) - { - channel->OnOutgoingMessageSent(channel, message); - } - } - - j++; - } - } - - if (Connection_SendPacket(connection, &packet, packet_entry, time) < 0) - { - NBN_LogError("Failed to send packet %d to connection %d", packet.header.seq_number, connection->id); - - return NBN_ERROR; - } - - sent_bytes += packet.size; - sent_packet_count++; - - double t = time - connection->last_flush_time; - - if (t > 0) Connection_UpdateAverageUploadBandwidth(connection, sent_bytes / t); - - connection->last_flush_time = time; - - return 0; -} - -int NBN_Connection_InitChannel(NBN_Connection *connection, NBN_Channel *channel) -{ - channel->connection = connection; - channel->read_chunk_buffer = (uint8_t*)NBN_Allocator(NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE); - channel->write_chunk_buffer = (uint8_t*)NBN_Allocator(NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE); - channel->read_chunk_buffer_size = NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE; - channel->write_chunk_buffer_size = NBN_CHANNEL_RW_CHUNK_BUFFER_INITIAL_SIZE; - channel->next_outgoing_chunked_message = 0; - channel->next_outgoing_message_id = 0; - channel->next_recv_message_id = 0; - channel->outgoing_message_count = 0; - channel->chunk_count = 0; - channel->last_received_chunk_id = -1; - - for (unsigned int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) - { - channel->recved_message_slot_buffer[i].free = true; - channel->outgoing_message_slot_buffer[i].free = true; - } - - for (int i = 0; i < NBN_CHANNEL_CHUNKS_BUFFER_SIZE; i++) - channel->recv_chunk_buffer[i] = NULL; - - NBN_LogDebug("Initialized channel %d for connection %d", channel->id, connection->id); - return 0; -} - -bool NBN_Connection_CheckIfStale(NBN_Connection *connection, double time) -{ -#if defined(NBN_DEBUG) && defined(NBN_DISABLE_STALE_CONNECTION_DETECTION) - (void) connection; - (void) time; - /* When testing under bad network conditions (in soak test for instance), we don't want to deal - with stale connections */ - return false; -#else - return time - connection->last_recv_packet_time > NBN_CONNECTION_STALE_TIME_THRESHOLD; -#endif -} - -static int Connection_DecodePacketHeader(NBN_Connection *connection, NBN_Packet *packet, double time) -{ - if (Connection_AckPacket(connection, packet->header.ack, time) < 0) - { - NBN_LogError("Failed to ack packet %d", packet->header.seq_number); - - return NBN_ERROR; - } - - for (unsigned int i = 0; i < 32; i++) - { - if (B_IS_UNSET(packet->header.ack_bits, i)) - continue; - - if (Connection_AckPacket(connection, packet->header.ack - (i + 1), time) < 0) - { - NBN_LogError("Failed to ack packet %d", packet->header.seq_number); - - return NBN_ERROR; - } - } - - return 0; -} - -static uint32_t Connection_BuildPacketAckBits(NBN_Connection *connection) -{ - uint32_t ack_bits = 0; - - for (int i = 0; i < 32; i++) - { - /* - when last_received_packet_seq_number is lower than 32, the value of acked_packet_seq_number will eventually - wrap around, which means the packets from before the wrap around will naturally be acked - */ - - uint16_t acked_packet_seq_number = connection->last_received_packet_seq_number - (i + 1); - - if (Connection_IsPacketReceived(connection, acked_packet_seq_number)) - B_SET(ack_bits, i); - } - - return ack_bits; -} - -static int Connection_AckPacket(NBN_Connection *connection, uint16_t ack_packet_seq_number, double time) -{ - NBN_PacketEntry *packet_entry = Connection_FindSendPacketEntry(connection, ack_packet_seq_number); - - if (packet_entry && !packet_entry->acked) - { - NBN_LogTrace("Packet %d acked (connection: %d)", ack_packet_seq_number, connection->id); - - packet_entry->acked = true; - - Connection_UpdateAveragePing(connection, time - packet_entry->send_time); - - for (unsigned int i = 0; i < packet_entry->messages_count; i++) - { - NBN_MessageEntry *msg_entry = &packet_entry->messages[i]; - NBN_Channel *channel = connection->channels[msg_entry->channel_id]; - - assert(channel != NULL); - - if (channel->OnOutgoingMessageAcked) - { - if (channel->OnOutgoingMessageAcked(channel, msg_entry->id) < 0) - return NBN_ERROR; - } - } - } - - return 0; -} - -static void Connection_InitOutgoingPacket( - NBN_Connection *connection, NBN_Packet *outgoing_packet, NBN_PacketEntry **packet_entry) -{ - NBN_Packet_InitWrite( - outgoing_packet, - connection->protocol_id, - connection->next_packet_seq_number++, - connection->last_received_packet_seq_number, - Connection_BuildPacketAckBits(connection)); - - *packet_entry = Connection_InsertOutgoingPacketEntry(connection, outgoing_packet->header.seq_number); -} - -static NBN_PacketEntry *Connection_InsertOutgoingPacketEntry(NBN_Connection *connection, uint16_t seq_number) -{ - uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - NBN_PacketEntry entry = { - .acked = false, - .flagged_as_lost = false, - .messages_count = 0, - .send_time = 0, - .messages = { {0, 0} } - }; - - connection->packet_send_seq_buffer[index] = seq_number; - connection->packet_send_buffer[index] = entry; - - return &connection->packet_send_buffer[index]; -} - -static bool Connection_InsertReceivedPacketEntry(NBN_Connection *connection, uint16_t seq_number) -{ - uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - - /* Ignore duplicated packets */ - if (connection->packet_recv_seq_buffer[index] != 0xFFFFFFFF && - connection->packet_recv_seq_buffer[index] == seq_number) - return false; - - /* - Clear entries between the previous highest sequence numbers and new highest one - to avoid entries staying inside the sequence buffer from before the sequence wrap around - and break the packet acking logic. - */ - if (SEQUENCE_NUMBER_GT(seq_number, connection->last_received_packet_seq_number)) - { - for (uint16_t seq = connection->last_received_packet_seq_number + 1; SEQUENCE_NUMBER_LT(seq, seq_number); seq++) - connection->packet_recv_seq_buffer[seq % NBN_MAX_PACKET_ENTRIES] = 0xFFFFFFFF; - } - - connection->packet_recv_seq_buffer[index] = seq_number; - - return true; -} - -static NBN_PacketEntry *Connection_FindSendPacketEntry(NBN_Connection *connection, uint16_t seq_number) -{ - uint16_t index = seq_number % NBN_MAX_PACKET_ENTRIES; - - if (connection->packet_send_seq_buffer[index] == seq_number) - return &connection->packet_send_buffer[index]; - - return NULL; -} - -static bool Connection_IsPacketReceived(NBN_Connection *connection, uint16_t packet_seq_number) -{ - uint16_t index = packet_seq_number % NBN_MAX_PACKET_ENTRIES; - - return connection->packet_recv_seq_buffer[index] == packet_seq_number; -} - -static int Connection_SendPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_PacketEntry *packet_entry, double time) -{ - NBN_LogTrace("Send packet %d to connection %d (messages count: %d)", - packet->header.seq_number, connection->id, packet->header.messages_count); - - assert(packet_entry->messages_count == packet->header.messages_count); - - if (NBN_Packet_Seal(packet, connection) < 0) - { - NBN_LogError("Failed to seal packet"); - - return NBN_ERROR; - } - - packet_entry->send_time = time; - - if (connection->endpoint->is_server) - { -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - return NBN_PacketSimulator_EnqueuePacket(&nbn_game_server.endpoint.packet_simulator, packet, connection); -#else - if (connection->is_stale) - return 0; - - return connection->driver->impl.serv_send_packet_to(packet, connection); -#endif - } - else - { -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - return NBN_PacketSimulator_EnqueuePacket(&nbn_game_client.endpoint.packet_simulator, packet, connection); -#else - NBN_Driver *driver = nbn_game_client.server_connection->driver; - - return driver->impl.cli_send_packet(packet); -#endif - } -} - -static int Connection_ReadNextMessageFromStream( - NBN_Connection *connection, NBN_ReadStream *r_stream, NBN_Message *message) -{ - if (NBN_Message_SerializeHeader(&message->header, (NBN_Stream *)r_stream) < 0) - { - NBN_LogError("Failed to read message header"); - - return NBN_ERROR; - } - - uint8_t msg_type = message->header.type; - NBN_MessageBuilder msg_builder = connection->endpoint->message_builders[msg_type]; - - if (msg_builder == NULL) - { - NBN_LogError("No message builder is registered for messages of type %d", msg_type); - - return NBN_ERROR; - } - - NBN_MessageSerializer msg_serializer = connection->endpoint->message_serializers[msg_type]; - - if (msg_serializer == NULL) - { - NBN_LogError("No message serializer attached to message of type %d", msg_type); - - return NBN_ERROR; - } - - NBN_Channel *channel = connection->channels[message->header.channel_id]; - - if (channel == NULL) - { - NBN_LogError("Channel %d does not exist", message->header.channel_id); - - return NBN_ERROR; - } - - message->data = msg_builder(); - - if (msg_serializer(message->data, (NBN_Stream *)r_stream) < 0) - { - NBN_LogError("Failed to read message body"); - - return NBN_ERROR; - } - - return 0; -} - -static int Connection_ReadNextMessageFromPacket(NBN_Connection *connection, NBN_Packet *packet, NBN_Message *message) -{ - return Connection_ReadNextMessageFromStream(connection, &packet->r_stream, message); -} - -static void Connection_RecycleMessage(NBN_Channel *channel, NBN_Message *message) -{ - // for incoming messages : message->outgoing_msg == NULL - assert(message->outgoing_msg == NULL || message->outgoing_msg->ref_count > 0); - - if (message->outgoing_msg == NULL || --message->outgoing_msg->ref_count == 0) - { - if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) - { - NBN_MessageChunk *chunk = (NBN_MessageChunk*)message->data; - - if (chunk->outgoing_msg) - { - assert(chunk->outgoing_msg->ref_count > 0); - - if (--chunk->outgoing_msg->ref_count == 0) - { - NBN_MessageDestructor msg_destructor = - channel->connection->endpoint->message_destructors[chunk->outgoing_msg->type]; - - if (msg_destructor) - msg_destructor(chunk->outgoing_msg->data); - } - } - } - - NBN_MessageDestructor msg_destructor = channel->connection->endpoint->message_destructors[message->header.type]; - - if (msg_destructor) - msg_destructor(message->data); - } -} - -static void Connection_UpdateAveragePing(NBN_Connection *connection, double ping) -{ - /* exponential smoothing with a factor of 0.05 */ - connection->stats.ping = connection->stats.ping + .05f * (ping - connection->stats.ping); -} - -static void Connection_UpdateAveragePacketLoss(NBN_Connection *connection, uint16_t seq) -{ - unsigned int lost_packet_count = 0; - uint16_t start_seq = seq - 64; - - for (int i = 0; i < 100; i++) - { - uint16_t s = start_seq - i; - NBN_PacketEntry *entry = Connection_FindSendPacketEntry(connection, s); - - if (entry && !entry->acked) - { - lost_packet_count++; - - if (!entry->flagged_as_lost) - { - entry->flagged_as_lost = true; - connection->stats.total_lost_packets++; - } - } - } - - float packet_loss = lost_packet_count / 100.f; - - /* exponential smoothing with a factor of 0.1 */ - connection->stats.packet_loss = connection->stats.packet_loss + .1f * (packet_loss - connection->stats.packet_loss); -} - -static void Connection_UpdateAverageUploadBandwidth(NBN_Connection *connection, float bytes_per_sec) -{ - /* exponential smoothing with a factor of 0.1 */ - connection->stats.upload_bandwidth = - connection->stats.upload_bandwidth + .1f * (bytes_per_sec - connection->stats.upload_bandwidth); -} - -static void Connection_UpdateAverageDownloadBandwidth(NBN_Connection *connection, double time) -{ - double t = time - connection->last_read_packets_time; - - if (t == 0) - return; - - float bytes_per_sec = connection->downloaded_bytes / t; - - /* exponential smoothing with a factor of 0.1 */ - connection->stats.download_bandwidth = - connection->stats.download_bandwidth + .1f * (bytes_per_sec - connection->stats.download_bandwidth); - - connection->downloaded_bytes = 0; -} - -static NBN_RPC_Message *Connection_BuildRPC(NBN_Connection *connection, NBN_RPC *rpc, va_list args) -{ - NBN_RPC_Message *msg = (NBN_RPC_Message *) NBN_RPC_Message_Create(); - - assert(msg != NULL); - - msg->id = rpc->id; - msg->param_count = rpc->signature.param_count; - - for (unsigned int i = 0; i < rpc->signature.param_count; i++) - { - NBN_RPC_ParamType param_type = rpc->signature.params[i]; - - msg->params[i].type = param_type; - - if (param_type == NBN_RPC_PARAM_INT) - { - msg->params[i].value.i = va_arg(args, int); - } - else if (param_type == NBN_RPC_PARAM_FLOAT) - { - msg->params[i].value.f = va_arg(args, double); - } - else if (param_type == NBN_RPC_PARAM_BOOL) - { - msg->params[i].value.b = va_arg(args, int); - } - else if (param_type == NBN_RPC_PARAM_STRING) - { - char *str = va_arg(args, char *); - - strncpy(msg->params[i].value.s, str, NBN_RPC_STRING_MAX_LENGTH); - } - else - { - NBN_LogError("Calling RPC %d with invalid parameters on connection %d", rpc->id, connection->id); - NBN_Abort(); - - return NULL; - } - } - - return msg; -} - -static void Connection_HandleReceivedRPC(NBN_ConnectionHandle connection, NBN_Endpoint *endpoint, NBN_RPC_Message *msg) -{ - if (msg->id > NBN_RPC_MAX - 1) - { - NBN_LogError("Received an invalid RPC"); - - return; - } - - NBN_RPC *rpc = &endpoint->rpcs[msg->id]; - - if (rpc->id != msg->id) - { - NBN_LogError("Received an invalid RPC"); - - return; - } - - if (!rpc->func) - { - NBN_LogError("Received RPC does not have an attached function"); - - return; - } - - rpc->func(msg->param_count, msg->params, connection); -} - -#pragma endregion /* NBN_Connection */ - -#pragma region NBN_Channel - -void NBN_Channel_Destroy(NBN_Channel *channel) -{ - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) - { - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[i]; - - if (!slot->free) - { - Connection_RecycleMessage(channel, &slot->message); - } - - slot = &channel->outgoing_message_slot_buffer[i]; - - if (!slot->free) - { - Connection_RecycleMessage(channel, &slot->message); - } - } - - NBN_Deallocator(channel->read_chunk_buffer); - NBN_Deallocator(channel->write_chunk_buffer); - - NBN_Deallocator(channel); -} - -bool NBN_Channel_AddChunk(NBN_Channel *channel, NBN_Message *chunk_msg) -{ - assert(chunk_msg->header.type == NBN_MESSAGE_CHUNK_TYPE); - - NBN_MessageChunk *chunk = (NBN_MessageChunk *)chunk_msg->data; - - NBN_LogTrace("Add chunk %d to channel %d (current chunk count: %d, last recved chunk id: %d)", - chunk->id, channel->id, channel->chunk_count, channel->last_received_chunk_id); - - if (chunk->id == channel->last_received_chunk_id + 1) - { - assert(channel->recv_chunk_buffer[chunk->id] == NULL); - - channel->recv_chunk_buffer[chunk->id] = chunk; - channel->last_received_chunk_id++; - channel->chunk_count++; - - NBN_LogTrace("Chunk added (%d/%d)", channel->chunk_count, chunk->total); - - if (channel->chunk_count == chunk->total) - { - channel->last_received_chunk_id = -1; - - return true; - } - - return false; - } - else - { - NBN_LogTrace("Chunk ignored"); - } - - /* Clear the chunks buffer */ - for (unsigned int i = 0; i < channel->chunk_count; i++) - { - assert(channel->recv_chunk_buffer[i] != NULL); - - NBN_MessageChunk_Destroy(channel->recv_chunk_buffer[i]); - - channel->recv_chunk_buffer[i] = NULL; - } - - channel->chunk_count = 0; - channel->last_received_chunk_id = -1; - - if (chunk->id == 0) - return NBN_Channel_AddChunk(channel, chunk_msg); - - return false; -} - -int NBN_Channel_ReconstructMessageFromChunks( - NBN_Channel *channel, NBN_Connection *connection, NBN_Message *message) -{ - unsigned int message_size = channel->chunk_count * NBN_MESSAGE_CHUNK_SIZE; - - if (message_size > channel->read_chunk_buffer_size) - NBN_Channel_ResizeReadChunkBuffer(channel, message_size); - - NBN_LogTrace("Reconstructing message (chunk count: %d, size: %d) from channel %d", - channel->chunk_count, message_size, channel->id); - - for (unsigned int i = 0; i < channel->chunk_count; i++) - { - NBN_MessageChunk *chunk = channel->recv_chunk_buffer[i]; - - memcpy(channel->read_chunk_buffer + i * NBN_MESSAGE_CHUNK_SIZE, chunk->data, NBN_MESSAGE_CHUNK_SIZE); - - NBN_MessageChunk_Destroy(chunk); - - channel->recv_chunk_buffer[i] = NULL; - } - - channel->chunk_count = 0; - - NBN_ReadStream r_stream; - - NBN_ReadStream_Init(&r_stream, channel->read_chunk_buffer, message_size); - - if (Connection_ReadNextMessageFromStream(connection, &r_stream, message) < 0) - return NBN_ERROR; - - NBN_LogTrace("Reconstructed message %d of type %d", message->header.id, message->header.type); - - return 0; -} - -void NBN_Channel_ResizeWriteChunkBuffer(NBN_Channel *channel, unsigned int size) -{ - channel->write_chunk_buffer = (uint8_t *)NBN_Reallocator(channel->write_chunk_buffer, size); - - channel->write_chunk_buffer_size = size; -} - -void NBN_Channel_ResizeReadChunkBuffer(NBN_Channel *channel, unsigned int size) -{ - channel->read_chunk_buffer = (uint8_t *)NBN_Reallocator(channel->read_chunk_buffer, size); - - channel->read_chunk_buffer_size = size; -} - -void NBN_Channel_UpdateMessageLastSendTime(NBN_Channel *channel, NBN_Message *message, double time) -{ - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - - assert(slot->message.header.id == message->header.id); - - slot->last_send_time = time; -} - -/* Unreliable ordered */ - -static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Channel *, NBN_Message *); -static bool UnreliableOrderedChannel_AddOutgoingMessage(NBN_Channel *, NBN_Message *); -static NBN_Message *UnreliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *); -static NBN_Message *UnreliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *, double); -static int UnreliableOrderedChannel_OnMessageSent(NBN_Channel *, NBN_Message *); - -NBN_UnreliableOrderedChannel *NBN_UnreliableOrderedChannel_Create(void) -{ - NBN_UnreliableOrderedChannel *channel = (NBN_UnreliableOrderedChannel *)NBN_Allocator(sizeof(NBN_UnreliableOrderedChannel)); - - channel->base.next_outgoing_message_pool_slot = 0; - channel->base.AddReceivedMessage = UnreliableOrderedChannel_AddReceivedMessage; - channel->base.AddOutgoingMessage = UnreliableOrderedChannel_AddOutgoingMessage; - channel->base.GetNextRecvedMessage = UnreliableOrderedChannel_GetNextRecvedMessage; - channel->base.GetNextOutgoingMessage = UnreliableOrderedChannel_GetNextOutgoingMessage; - channel->base.OnOutgoingMessageAcked = NULL; - channel->base.OnOutgoingMessageSent = UnreliableOrderedChannel_OnMessageSent; - - memset(channel->base.outgoing_message_pool, 0, sizeof(channel->base.outgoing_message_pool)); - - channel->last_received_message_id = 0; - channel->next_outgoing_message_slot = 0; - - return channel; -} - -static bool UnreliableOrderedChannel_AddReceivedMessage(NBN_Channel *channel, NBN_Message *message) -{ - NBN_UnreliableOrderedChannel *unreliable_ordered_channel = (NBN_UnreliableOrderedChannel *)channel; - - if (SEQUENCE_NUMBER_GT(message->header.id, unreliable_ordered_channel->last_received_message_id)) - { - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - - memcpy(&slot->message, message, sizeof(NBN_Message)); - - slot->free = false; - - unreliable_ordered_channel->last_received_message_id = message->header.id; - - return true; - } - - return false; -} - -static bool UnreliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) -{ - uint16_t msg_id = channel->next_outgoing_message_id; - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - - memcpy(&slot->message, message, sizeof(NBN_Message)); - - slot->message.header.id = msg_id; - slot->free = false; - - channel->next_outgoing_message_id++; - channel->outgoing_message_count++; - - return true; -} - -static NBN_Message *UnreliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *channel) -{ - NBN_UnreliableOrderedChannel *unreliable_ordered_channel = (NBN_UnreliableOrderedChannel *)channel; - - while (SEQUENCE_NUMBER_LT(channel->next_recv_message_id, unreliable_ordered_channel->last_received_message_id)) - { - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; - - if (!slot->free && slot->message.header.id == channel->next_recv_message_id) - { - slot->free = true; - - return &slot->message; - } - - channel->next_recv_message_id++; - } - - return NULL; -} - -static NBN_Message *UnreliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *channel, double time) -{ - (void)time; - - NBN_UnreliableOrderedChannel *unreliable_ordered_channel = (NBN_UnreliableOrderedChannel *)channel; - - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[unreliable_ordered_channel->next_outgoing_message_slot]; - - if (slot->free) - return NULL; - - slot->free = true; - - unreliable_ordered_channel->next_outgoing_message_slot = - (unreliable_ordered_channel->next_outgoing_message_slot + 1) % NBN_CHANNEL_BUFFER_SIZE; - - return &slot->message; -} - -static int UnreliableOrderedChannel_OnMessageSent(NBN_Channel *channel, NBN_Message *message) -{ - Connection_RecycleMessage(channel, message); - - return 0; -} - -/* Reliable ordered */ - -static bool ReliableOrderedChannel_AddReceivedMessage(NBN_Channel *, NBN_Message *); -static bool ReliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *); -static NBN_Message *ReliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *); -static NBN_Message *ReliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *, double); -static int ReliableOrderedChannel_OnOutgoingMessageAcked(NBN_Channel *, uint16_t); - -NBN_ReliableOrderedChannel *NBN_ReliableOrderedChannel_Create(void) -{ - NBN_ReliableOrderedChannel *channel = (NBN_ReliableOrderedChannel *)NBN_Allocator(sizeof(NBN_ReliableOrderedChannel)); - - channel->base.next_outgoing_message_pool_slot = 0; - channel->base.AddReceivedMessage = ReliableOrderedChannel_AddReceivedMessage; - channel->base.AddOutgoingMessage = ReliableOrderedChannel_AddOutgoingMessage; - channel->base.GetNextRecvedMessage = ReliableOrderedChannel_GetNextRecvedMessage; - channel->base.GetNextOutgoingMessage = ReliableOrderedChannel_GetNextOutgoingMessage; - channel->base.OnOutgoingMessageAcked = ReliableOrderedChannel_OnOutgoingMessageAcked; - channel->base.OnOutgoingMessageSent = NULL; - - memset(channel->base.recved_message_slot_buffer, 0, sizeof(channel->base.recved_message_slot_buffer)); - memset(channel->base.outgoing_message_pool, 0, sizeof(channel->base.outgoing_message_pool)); - - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) - channel->ack_buffer[i] = false; - - channel->oldest_unacked_message_id = 0; - channel->most_recent_message_id = 0; - - return channel; -} - -static unsigned int ReliableOrderedChannel_ComputeMessageIdDelta(uint16_t id1, uint16_t id2) -{ - if (SEQUENCE_NUMBER_GT(id1, id2)) - return (id1 >= id2) ? id1 - id2 : ((0xFFFF + 1) - id2) + id1; - else - return (id2 >= id1) ? id2 - id1 : (((0xFFFF + 1) - id1) + id2) % 0xFFFF; -} - -static bool ReliableOrderedChannel_AddReceivedMessage(NBN_Channel *channel, NBN_Message *message) -{ - NBN_ReliableOrderedChannel *reliable_ordered_channel = (NBN_ReliableOrderedChannel *)channel; - unsigned int dt = ReliableOrderedChannel_ComputeMessageIdDelta( - message->header.id, reliable_ordered_channel->most_recent_message_id); - - NBN_LogTrace("Add recved message %d of type %d to channel %d (most recent msg id: %d, dt: %d)", - message->header.id, message->header.type, channel->id, reliable_ordered_channel->most_recent_message_id, dt); - - if (SEQUENCE_NUMBER_GT(message->header.id, reliable_ordered_channel->most_recent_message_id)) - { - assert(dt < NBN_CHANNEL_BUFFER_SIZE); - - reliable_ordered_channel->most_recent_message_id = message->header.id; - } - else - { - /* This is an old message that has already been received, probably coming from - an out of order late packet. */ - if (dt >= NBN_CHANNEL_BUFFER_SIZE) - return false; - } - - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[message->header.id % NBN_CHANNEL_BUFFER_SIZE]; - - if (!slot->free) - { - Connection_RecycleMessage(channel, &slot->message); - } - - memcpy(&slot->message, message, sizeof(NBN_Message)); - - slot->free = false; - - return true; -} - -static bool ReliableOrderedChannel_AddOutgoingMessage(NBN_Channel *channel, NBN_Message *message) -{ - uint16_t msg_id = channel->next_outgoing_message_id; - int index = msg_id % NBN_CHANNEL_BUFFER_SIZE; - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[index]; - - if (!slot->free) - { - NBN_LogTrace("Slot %d is not free on channel %d (slot msg id: %d, outgoing message count: %d)", - index, channel->id, slot->message.header.id, channel->outgoing_message_count); - - return false; - } - - memcpy(&slot->message, message, sizeof(NBN_Message)); - - slot->message.header.id = msg_id; - slot->last_send_time = -1; - slot->free = false; - - channel->next_outgoing_message_id++; - channel->outgoing_message_count++; - - NBN_LogTrace("Added outgoing message %d of type %d to channel %d (index: %d)", - slot->message.header.id, slot->message.header.type, channel->id, index); - - return true; -} - -static NBN_Message *ReliableOrderedChannel_GetNextRecvedMessage(NBN_Channel *channel) -{ - NBN_MessageSlot *slot = &channel->recved_message_slot_buffer[channel->next_recv_message_id % NBN_CHANNEL_BUFFER_SIZE]; - - if (!slot->free && slot->message.header.id == channel->next_recv_message_id) - { - slot->free = true; - channel->next_recv_message_id++; - - return &slot->message; - } - - return NULL; -} - -static NBN_Message *ReliableOrderedChannel_GetNextOutgoingMessage(NBN_Channel *channel, double time) -{ - NBN_ReliableOrderedChannel *reliable_ordered_channel = (NBN_ReliableOrderedChannel *)channel; - - int max_message_id = (reliable_ordered_channel->oldest_unacked_message_id + (NBN_CHANNEL_BUFFER_SIZE - 1)) % (0xFFFF + 1); - - if (SEQUENCE_NUMBER_LT(channel->next_outgoing_message_id, max_message_id)) - max_message_id = channel->next_outgoing_message_id; - - uint16_t msg_id = reliable_ordered_channel->oldest_unacked_message_id; - - while (SEQUENCE_NUMBER_LT(msg_id, max_message_id)) - { - NBN_MessageSlot *slot = &channel->outgoing_message_slot_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - - if ( - !slot->free && - (slot->last_send_time < 0 || time - slot->last_send_time >= NBN_MESSAGE_RESEND_DELAY)) - { - return &slot->message; - } - - msg_id++; - } - - return NULL; -} - -static int ReliableOrderedChannel_OnOutgoingMessageAcked(NBN_Channel *channel, uint16_t msg_id) -{ - NBN_ReliableOrderedChannel *reliable_ordered_channel = (NBN_ReliableOrderedChannel *)channel; - NBN_MessageSlot *slot = &reliable_ordered_channel->base.outgoing_message_slot_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE]; - - if (slot->free || slot->message.header.id != msg_id) - return 0; - - slot->free = true; - - NBN_LogTrace("Message %d acked on channel %d (buffer index: %d, oldest unacked: %d)", - msg_id, channel->id, msg_id % NBN_CHANNEL_BUFFER_SIZE, reliable_ordered_channel->oldest_unacked_message_id); - - reliable_ordered_channel->ack_buffer[msg_id % NBN_CHANNEL_BUFFER_SIZE] = true; - channel->outgoing_message_count--; - - if (msg_id == reliable_ordered_channel->oldest_unacked_message_id) - { - for (int i = 0; i < NBN_CHANNEL_BUFFER_SIZE; i++) - { - uint16_t ack_msg_id = msg_id + i; - int index = ack_msg_id % NBN_CHANNEL_BUFFER_SIZE; - - if (reliable_ordered_channel->ack_buffer[index]) - { - reliable_ordered_channel->ack_buffer[index] = false; - reliable_ordered_channel->oldest_unacked_message_id++; - } - else - { - break; - } - } - - NBN_LogTrace("Updated oldest unacked message id on channel %d: %d", - channel->id, reliable_ordered_channel->oldest_unacked_message_id); - } - - Connection_RecycleMessage(channel, &slot->message); - - return 0; -} - -#pragma endregion /* NBN_MessageChannel */ - -#pragma region NBN_EventQueue - -void NBN_EventQueue_Init(NBN_EventQueue *event_queue) -{ - event_queue->head = 0; - event_queue->tail = 0; - event_queue->count = 0; -} - -bool NBN_EventQueue_Enqueue(NBN_EventQueue *event_queue, NBN_Event ev) -{ - if (event_queue->count >= NBN_EVENT_QUEUE_CAPACITY) - return false; - - event_queue->events[event_queue->tail] = ev; - - event_queue->tail = (event_queue->tail + 1) % NBN_EVENT_QUEUE_CAPACITY; - event_queue->count++; - - return true; -} - -bool NBN_EventQueue_Dequeue(NBN_EventQueue *event_queue, NBN_Event *ev) -{ - if (NBN_EventQueue_IsEmpty(event_queue)) - return false; - - memcpy(ev, &event_queue->events[event_queue->head], sizeof(NBN_Event)); - event_queue->head = (event_queue->head + 1) % NBN_EVENT_QUEUE_CAPACITY; - event_queue->count--; - - return true; -} - -bool NBN_EventQueue_IsEmpty(NBN_EventQueue *event_queue) -{ - return event_queue->count == 0; -} - -#pragma endregion /* NBN_EventQueue */ - -#pragma region NBN_Endpoint - -static void Endpoint_Init(NBN_Endpoint *, bool); -static void Endpoint_Deinit(NBN_Endpoint *); -static void Endpoint_RegisterMessageBuilder(NBN_Endpoint *, NBN_MessageBuilder, uint8_t); -static void Endpoint_RegisterMessageDestructor(NBN_Endpoint *, NBN_MessageDestructor, uint8_t); -static void Endpoint_RegisterMessageSerializer(NBN_Endpoint *, NBN_MessageSerializer, uint8_t); -static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *, uint32_t, uint32_t, int, void *); -static int Endpoint_RegisterRPC(NBN_Endpoint *, unsigned int id, NBN_RPC_Signature, NBN_RPC_Func); -static uint32_t Endpoint_BuildProtocolId(const char *); -static void Endpoint_RegisterChannel(NBN_Endpoint *, uint8_t, NBN_ChannelBuilder, NBN_ChannelDestructor); -static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *, NBN_Packet *, NBN_Connection *); -static int Endpoint_EnqueueOutgoingMessage(NBN_Endpoint *, NBN_Connection *, NBN_OutgoingMessage *, uint8_t); -static int Endpoint_SplitMessageIntoChunks( - NBN_Message *, NBN_OutgoingMessage *, NBN_Channel *, NBN_MessageSerializer, unsigned int, NBN_MessageChunk **); -static void Endpoint_UpdateTime(NBN_Endpoint *); - -static void Endpoint_Init(NBN_Endpoint *endpoint, bool is_server) -{ - MemoryManager_Init(); - - endpoint->is_server = is_server; - - for (int i = 0; i < NBN_MAX_CHANNELS; i++) - { - endpoint->channel_builders[i] = NULL; - endpoint->channel_destructors[i] = NULL; - } - - for (int i = 0; i < NBN_MAX_MESSAGE_TYPES; i++) - { - endpoint->message_builders[i] = NULL; - endpoint->message_destructors[i] = NULL; - endpoint->message_serializers[i] = NULL; - } - - NBN_RPC rpc = {.id = UINT_MAX}; - for (int i = 0; i < NBN_RPC_MAX; i++) - { - endpoint->rpcs[i] = rpc; - } - - NBN_EventQueue_Init(&endpoint->event_queue); - - /* Register library reserved reliable channel */ - Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_UNRELIABLE, (NBN_ChannelBuilder)NBN_UnreliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); - Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_RELIABLE, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); - Endpoint_RegisterChannel(endpoint, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); - - /* Register general purposes reliable channels */ - for (int i = 0; i < NBN_MAX_CHANNELS - NBN_LIBRARY_RESERVED_CHANNELS; i++) - { - Endpoint_RegisterChannel(endpoint, i, (NBN_ChannelBuilder)NBN_ReliableOrderedChannel_Create, (NBN_ChannelDestructor)NBN_Channel_Destroy); - } - - /* Register NBN_MessageChunk library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_MessageChunk_Create, NBN_MESSAGE_CHUNK_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_MessageChunk_Serialize, NBN_MESSAGE_CHUNK_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_MessageChunk_Destroy, NBN_MESSAGE_CHUNK_TYPE); - - /* Register NBN_ClientClosedMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_ClientClosedMessage_Create, NBN_CLIENT_CLOSED_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_ClientClosedMessage_Serialize, NBN_CLIENT_CLOSED_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_ClientClosedMessage_Destroy, NBN_CLIENT_CLOSED_MESSAGE_TYPE); - - /* Register NBN_ClientAcceptedMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_ClientAcceptedMessage_Create, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_ClientAcceptedMessage_Serialize, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_ClientAcceptedMessage_Destroy, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE); - - /* Register NBN_ByteArrayMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_ByteArrayMessage_Create, NBN_BYTE_ARRAY_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_ByteArrayMessage_Serialize, NBN_BYTE_ARRAY_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_ByteArrayMessage_Destroy, NBN_BYTE_ARRAY_MESSAGE_TYPE); - - /* Register NBN_DisconnectionMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_DisconnectionMessage_Create, NBN_DISCONNECTION_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_DisconnectionMessage_Serialize, NBN_DISCONNECTION_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_DisconnectionMessage_Destroy, NBN_DISCONNECTION_MESSAGE_TYPE); - - /* Register NBN_ConnectionRequestMessage library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_ConnectionRequestMessage_Create, NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_ConnectionRequestMessage_Serialize, NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_ConnectionRequestMessage_Destroy, NBN_CONNECTION_REQUEST_MESSAGE_TYPE); - - /* Register NBN_RPC_Message library message */ - Endpoint_RegisterMessageBuilder( - endpoint, (NBN_MessageBuilder)NBN_RPC_Message_Create, NBN_RPC_MESSAGE_TYPE); - Endpoint_RegisterMessageSerializer( - endpoint, (NBN_MessageSerializer)NBN_RPC_Message_Serialize, NBN_RPC_MESSAGE_TYPE); - Endpoint_RegisterMessageDestructor( - endpoint, (NBN_MessageDestructor)NBN_RPC_Message_Destroy, NBN_RPC_MESSAGE_TYPE); - -#ifdef NBN_DEBUG - memset(&endpoint->debug_callbacks, 0, sizeof(endpoint->debug_callbacks)); -#endif - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - NBN_PacketSimulator_Init(&endpoint->packet_simulator, endpoint); - NBN_PacketSimulator_Start(&endpoint->packet_simulator); -#endif - - Endpoint_UpdateTime(endpoint); -} - -static void Endpoint_Deinit(NBN_Endpoint *endpoint) -{ - (void)endpoint; - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - NBN_PacketSimulator_Stop(&endpoint->packet_simulator); -#endif - - MemoryManager_Deinit(); -} - -static void Endpoint_RegisterMessageBuilder(NBN_Endpoint *endpoint, NBN_MessageBuilder msg_builder, uint8_t msg_type) -{ - endpoint->message_builders[msg_type] = msg_builder; -} - -static void Endpoint_RegisterMessageDestructor(NBN_Endpoint *endpoint, NBN_MessageDestructor msg_destructor, uint8_t msg_type) -{ - endpoint->message_destructors[msg_type] = msg_destructor; -} - -static void Endpoint_RegisterMessageSerializer( - NBN_Endpoint *endpoint, NBN_MessageSerializer msg_serializer, uint8_t msg_type) -{ - endpoint->message_serializers[msg_type] = msg_serializer; -} - -static NBN_Connection *Endpoint_CreateConnection(NBN_Endpoint *endpoint, uint32_t id, uint32_t protocol_id, int driver_id, void *driver_data) -{ - NBN_Driver *driver = &nbn_drivers[driver_id]; - - assert(driver->id >= 0); - - NBN_Connection *connection = NBN_Connection_Create(id, protocol_id, endpoint, driver, driver_data); - - for (unsigned int chan_id = 0; chan_id < NBN_MAX_CHANNELS; chan_id++) - { - NBN_ChannelBuilder builder = endpoint->channel_builders[chan_id]; - - if (!builder) continue; - - NBN_ChannelDestructor destructor = endpoint->channel_destructors[chan_id]; - - assert(destructor); - - NBN_Channel *channel = builder(); - - assert(channel); - - channel->id = chan_id; - channel->destructor = destructor; - - connection->channels[chan_id] = channel; - - NBN_Connection_InitChannel(connection, channel); - } - - return connection; -} - -static int Endpoint_RegisterRPC(NBN_Endpoint *endpoint, unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func) -{ - if (id >= NBN_RPC_MAX) - { - NBN_LogError("Failed to register RPC, invalid ID"); - - return NBN_ERROR; - } - - if (signature.param_count > NBN_RPC_MAX_PARAM_COUNT) - { - NBN_LogError("Failed to register RPC %d, too many parameters", id); - - return NBN_ERROR; - } - - NBN_RPC rpc = { .id = id, .signature = signature, .func = func }; - - endpoint->rpcs[id] = rpc; - - NBN_LogDebug("Registered RPC (id: %d, parameter count: %d)", id, signature.param_count); - - return 0; -} - -static uint32_t Endpoint_BuildProtocolId(const char *protocol_name) -{ - uint32_t protocol_id = 2166136261; - - for (unsigned int i = 0; i < strlen(protocol_name); i++) - { - protocol_id *= 16777619; - protocol_id ^= protocol_name[i]; - } - - return protocol_id; -} - -static void Endpoint_RegisterChannel(NBN_Endpoint *endpoint, uint8_t id, NBN_ChannelBuilder builder, NBN_ChannelDestructor destructor) -{ - if (endpoint->channel_builders[id] || endpoint->channel_destructors[id]) - { - NBN_LogError("A channel with id %d already exists", id); - NBN_Abort(); - } - - endpoint->channel_builders[id] = builder; - endpoint->channel_destructors[id] = destructor; -} - -static int Endpoint_ProcessReceivedPacket(NBN_Endpoint *endpoint, NBN_Packet *packet, NBN_Connection *connection) -{ - (void)endpoint; - - NBN_LogTrace("Received packet %d (conn id: %d, ack: %d, messages count: %d)", packet->header.seq_number, - connection->id, packet->header.ack, packet->header.messages_count); - - if (NBN_Connection_ProcessReceivedPacket(connection, packet, endpoint->time) < 0) - return NBN_ERROR; - - connection->last_recv_packet_time = endpoint->time; - connection->downloaded_bytes += packet->size; - - return 0; -} - -static NBN_OutgoingMessage *Endpoint_CreateOutgoingMessage(NBN_Endpoint *endpoint, NBN_Channel *channel, uint8_t msg_type, void *data) -{ - assert(channel); - - if (endpoint->message_serializers[msg_type] == NULL) - { - NBN_LogError("No message serializer is registered for messages of type %d", msg_type); - NBN_Abort(); - } - - NBN_OutgoingMessage *outgoing_message = &channel->outgoing_message_pool[channel->next_outgoing_message_pool_slot]; - if (outgoing_message->ref_count != 0) - { - NBN_LogError("Outgoing message pool has run out of space"); - - return NULL; - } - - outgoing_message->type = msg_type; - outgoing_message->data = data; - outgoing_message->ref_count = 0; - - channel->next_outgoing_message_pool_slot = (channel->next_outgoing_message_pool_slot + 1) % NBN_CHANNEL_OUTGOING_MESSAGE_POOL_SIZE; - - return outgoing_message; -} - -static int Endpoint_EnqueueOutgoingMessage( - NBN_Endpoint *endpoint, NBN_Connection *connection, NBN_OutgoingMessage *outgoing_msg, uint8_t channel_id) -{ - NBN_Channel *channel = connection->channels[channel_id]; - - if (channel == NULL) - { - NBN_LogError("Channel %d does not exist", channel_id); - - return NBN_ERROR; - } - - NBN_MessageSerializer msg_serializer = endpoint->message_serializers[outgoing_msg->type]; - - assert(msg_serializer); - - NBN_Message message = { - {0, outgoing_msg->type, channel_id}, - NULL, - outgoing_msg, - outgoing_msg->data}; - - NBN_MeasureStream m_stream; - - NBN_MeasureStream_Init(&m_stream); - - unsigned int message_size = (NBN_Message_Measure(&message, &m_stream, msg_serializer) - 1) / 8 + 1; - - if (message_size > NBN_PACKET_MAX_DATA_SIZE) - { - NBN_MessageChunk *chunks[NBN_CHANNEL_CHUNKS_BUFFER_SIZE]; - int chunk_count = Endpoint_SplitMessageIntoChunks( - &message, outgoing_msg, channel, msg_serializer, message_size, chunks); - - assert(chunk_count <= NBN_CHANNEL_CHUNKS_BUFFER_SIZE); - - if (chunk_count < 0) - { - NBN_LogError("Failed to split message into chunks"); - - return NBN_ERROR; - } - - assert(outgoing_msg->ref_count == 0); - outgoing_msg->ref_count = chunk_count; - - NBN_Channel *channel = connection->channels[channel_id]; - - for (int i = 0; i < chunk_count; i++) - { - NBN_OutgoingMessage *chunk_outgoing_msg = Endpoint_CreateOutgoingMessage( - endpoint, channel, NBN_MESSAGE_CHUNK_TYPE, chunks[i]); - - if (chunk_outgoing_msg == NULL) - return NBN_ERROR; - - if (Endpoint_EnqueueOutgoingMessage(endpoint, connection, chunk_outgoing_msg, channel_id) < 0) - { - NBN_LogError("Failed to enqueue message chunk"); - - return NBN_ERROR; - } - } - - return 0; - } - else - { - assert(outgoing_msg->ref_count == 0); - outgoing_msg->ref_count = 1; - - return NBN_Connection_EnqueueOutgoingMessage(connection, channel, &message); - } -} - -static int Endpoint_SplitMessageIntoChunks( - NBN_Message *message, - NBN_OutgoingMessage *outgoing_msg, - NBN_Channel *channel, - NBN_MessageSerializer msg_serializer, - unsigned int message_size, - NBN_MessageChunk **chunks) -{ - unsigned int chunk_count = ((message_size - 1) / NBN_MESSAGE_CHUNK_SIZE) + 1; - - NBN_LogTrace("Split message into %d chunks (message size: %d)", chunk_count, message_size); - - if (chunk_count > NBN_CHANNEL_CHUNKS_BUFFER_SIZE) - { - NBN_LogError("The maximum number of chunks is 255"); - - return NBN_ERROR; - } - - if (message_size > channel->write_chunk_buffer_size) - NBN_Channel_ResizeWriteChunkBuffer(channel, message_size); - - NBN_WriteStream w_stream; - - NBN_WriteStream_Init(&w_stream, channel->write_chunk_buffer, message_size); - - if (NBN_Message_SerializeHeader(&message->header, (NBN_Stream *)&w_stream) < 0) - return NBN_ERROR; - - if (NBN_Message_SerializeData(message, (NBN_Stream *)&w_stream, msg_serializer) < 0) - return NBN_ERROR; - - for (unsigned int i = 0; i < chunk_count; i++) - { - NBN_MessageChunk *chunk = NBN_MessageChunk_Create(); - - chunk->id = i; - chunk->total = chunk_count; - chunk->outgoing_msg = outgoing_msg; - - unsigned int offset = i * NBN_MESSAGE_CHUNK_SIZE; - unsigned int chunk_size = MIN(NBN_MESSAGE_CHUNK_SIZE, message_size - offset); - - assert(chunk_size <= NBN_MESSAGE_CHUNK_SIZE); - - memcpy(chunk->data, channel->write_chunk_buffer + offset, chunk_size); - - NBN_LogTrace("Enqueue chunk %d (size: %d, total: %d) for message %d of type %d", - chunk->id, chunk_size, chunk->total, message->header.id, message->header.type); - - chunks[i] = chunk; - } - - return chunk_count; -} - -static void Endpoint_UpdateTime(NBN_Endpoint *endpoint) -{ -#ifdef NBNET_WINDOWS - endpoint->time = GetTickCount64() / 1000.0; -#else - static struct timespec tp; - - if (clock_gettime(CLOCK_MONOTONIC_RAW, &tp) < 0) - { - NBN_LogError("gettimeofday() failed"); - NBN_Abort(); - } - - endpoint->time = tp.tv_sec + (tp.tv_nsec / (double)1e9); -#endif // NBNET_WINDOWS -} - -#pragma endregion /* NBN_Endpoint */ - -#pragma region Network driver - -static void ClientDriver_OnPacketReceived(NBN_Packet *packet); -static int ServerDriver_OnClientConnected(NBN_Connection *); -static int ServerDriver_OnClientPacketReceived(NBN_Packet *); - -void NBN_Driver_Register(int id, const char *name, NBN_DriverImplementation implementation) -{ - // driver id must be valid - assert(id >= 0 && id < NBN_MAX_DRIVERS); - - NBN_Driver *driver = &nbn_drivers[id]; - - // driver id must be unique - assert(driver->id == -1); - - driver->id = id; - driver->name = name; - driver->impl = implementation; - - NBN_LogInfo("Registered driver (ID: %d, Name: %s)", id, name); - - nbn_driver_count++; -} - -int NBN_Driver_RaiseEvent(NBN_DriverEvent ev, void *data) -{ - switch (ev) - { - case NBN_DRIVER_CLI_PACKET_RECEIVED: - ClientDriver_OnPacketReceived((NBN_Packet *)data); - break; - - case NBN_DRIVER_SERV_CLIENT_CONNECTED: - return ServerDriver_OnClientConnected((NBN_Connection *)data); - - case NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED: - return ServerDriver_OnClientPacketReceived((NBN_Packet *)data); - } - - return 0; -} - -#pragma endregion /* Network driver */ - -#pragma region NBN_GameClient - -NBN_GameClient nbn_game_client; - -static int GameClient_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static int GameClient_HandleEvent(void); -static int GameClient_HandleMessageReceivedEvent(void); - -int NBN_GameClient_Start(const char *protocol_name, const char *host, uint16_t port) -{ - return NBN_GameClient_StartEx(protocol_name, host, port, NULL, 0); -} - -int NBN_GameClient_StartEx(const char *protocol_name, const char *host, uint16_t port, uint8_t *data, unsigned int length) -{ - if (nbn_driver_count < 1) - { - NBN_LogError("At least one network driver has to be registered"); - NBN_Abort(); - } - - if (data && length > NBN_CONNECTION_DATA_MAX_SIZE) - { - NBN_LogError("Connection data cannot exceed %d bytes", NBN_CONNECTION_DATA_MAX_SIZE); - NBN_Abort(); - } - - Endpoint_Init(&nbn_game_client.endpoint, false); - - nbn_game_client.server_connection = NULL; - nbn_game_client.is_connected = false; - nbn_game_client.closed_code = -1; - - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) continue; - - if (driver->impl.cli_start(Endpoint_BuildProtocolId(protocol_name), host, port) < 0) - { - NBN_LogError("Failed to start driver %s", driver->name); - return NBN_ERROR; - } - } - - NBN_ConnectionRequestMessage *msg = NBN_ConnectionRequestMessage_Create(); - - msg->length = length; - - if (data) - { - memcpy(msg->data, data, NBN_CONNECTION_DATA_MAX_SIZE); - } - - if (NBN_GameClient_SendMessage(NBN_CONNECTION_REQUEST_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg) < 0) - return NBN_ERROR; - - NBN_LogInfo("Started"); - - return 0; -} - -void NBN_GameClient_Stop(void) -{ - // Poll remaining events to clear the event queue - while (NBN_GameClient_Poll() != NBN_NO_EVENT) {} - - if (nbn_game_client.server_connection) - { - if (!nbn_game_client.server_connection->is_closed && !nbn_game_client.server_connection->is_stale) - { - NBN_LogInfo("Disconnecting..."); - - if (NBN_GameClient_SendMessage(NBN_DISCONNECTION_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, NULL) < 0) - { - NBN_LogError("Failed to send disconnection message"); - } - - if (NBN_GameClient_SendPackets() < 0) - { - NBN_LogError("Failed to send packets"); - } - - nbn_game_client.server_connection->is_closed = true; - - NBN_LogInfo("Disconnected"); - } - - NBN_Connection_Destroy(nbn_game_client.server_connection); - nbn_game_client.server_connection = NULL; - } - - NBN_LogInfo("Stopping all drivers..."); - - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) continue; - - driver->impl.cli_stop(); - } - - nbn_game_client.is_connected = false; - nbn_game_client.closed_code = -1; - nbn_game_client.server_data_len = 0; - - memset(nbn_game_client.server_data, 0, sizeof(nbn_game_client.server_data)); - Endpoint_Deinit(&nbn_game_client.endpoint); - - NBN_LogInfo("Stopped"); -} - -unsigned int NBN_GameClient_ReadServerData(uint8_t *data) -{ - memcpy(data, nbn_game_client.server_data, nbn_game_client.server_data_len); - - return nbn_game_client.server_data_len; -} - -void NBN_GameClient_RegisterMessage( - uint8_t msg_type, - NBN_MessageBuilder msg_builder, - NBN_MessageDestructor msg_destructor, - NBN_MessageSerializer msg_serializer) -{ - if (NBN_IsReservedMessage(msg_type)) - { - NBN_LogError("Message type %d is reserved by the library", msg_type); - NBN_Abort(); - } - - Endpoint_RegisterMessageBuilder(&nbn_game_client.endpoint, msg_builder, msg_type); - Endpoint_RegisterMessageDestructor(&nbn_game_client.endpoint, msg_destructor, msg_type); - Endpoint_RegisterMessageSerializer(&nbn_game_client.endpoint, msg_serializer, msg_type); -} - -int NBN_GameClient_Poll(void) -{ - Endpoint_UpdateTime(&nbn_game_client.endpoint); - - if (nbn_game_client.server_connection->is_stale) - return NBN_NO_EVENT; - - if (NBN_EventQueue_IsEmpty(&nbn_game_client.endpoint.event_queue)) - { - if (NBN_Connection_CheckIfStale(nbn_game_client.server_connection, nbn_game_client.endpoint.time)) - { - nbn_game_client.server_connection->is_stale = true; - nbn_game_client.is_connected = false; - - NBN_LogInfo("Server connection is stale. Disconnected."); - - NBN_Event e; - - e.type = NBN_DISCONNECTED; - e.data.connection = (NBN_Connection *)NULL; - - if (!NBN_EventQueue_Enqueue(&nbn_game_client.endpoint.event_queue, e)) - return NBN_ERROR; - } - else - { - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) continue; - - if (driver->impl.cli_recv_packets() < 0) - { - NBN_LogError("Failed to read packets from driver %s", driver->name); - return NBN_ERROR; - } - } - - for (unsigned int i = 0; i < NBN_MAX_CHANNELS; i++) - { - NBN_Channel *channel = nbn_game_client.server_connection->channels[i]; - - if (channel) - { - NBN_Message *msg; - - while ((msg = channel->GetNextRecvedMessage(channel)) != NULL) - { - NBN_LogTrace("Got message %d of type %d from the recv queue", msg->header.id, msg->header.type); - - if (GameClient_ProcessReceivedMessage(msg, nbn_game_client.server_connection) < 0) - { - NBN_LogError("Failed to process received message"); - - return NBN_ERROR; - } - } - } - } - - Connection_UpdateAverageDownloadBandwidth(nbn_game_client.server_connection, nbn_game_client.endpoint.time); - - nbn_game_client.server_connection->last_read_packets_time = nbn_game_client.endpoint.time; - } - } - - bool ret = NBN_EventQueue_Dequeue(&nbn_game_client.endpoint.event_queue, &nbn_game_client.last_event); - - return ret ? GameClient_HandleEvent() : NBN_NO_EVENT; -} - -int NBN_GameClient_SendPackets(void) -{ - return NBN_Connection_FlushSendQueue(nbn_game_client.server_connection, nbn_game_client.endpoint.time); -} - -int NBN_GameClient_SendMessage(uint8_t msg_type, uint8_t channel_id, void *msg_data) -{ - NBN_OutgoingMessage *outgoing_msg = Endpoint_CreateOutgoingMessage( - &nbn_game_client.endpoint, - nbn_game_client.server_connection->channels[channel_id], - msg_type, - msg_data); - - - if (outgoing_msg == NULL) - { - NBN_LogError("Failed to create outgoing message"); - - return NBN_ERROR; - } - - if (Endpoint_EnqueueOutgoingMessage( - &nbn_game_client.endpoint, nbn_game_client.server_connection, outgoing_msg, channel_id) < 0) - { - NBN_LogError("Failed to create outgoing message"); - - return NBN_ERROR; - } - - return 0; -} - -int NBN_GameClient_SendByteArray(uint8_t *bytes, unsigned int length, uint8_t channel_id) -{ - if (length > NBN_BYTE_ARRAY_MAX_SIZE) - { - NBN_LogError("Byte array cannot exceed %d bytes", NBN_BYTE_ARRAY_MAX_SIZE); - - return NBN_ERROR; - } - - NBN_ByteArrayMessage *msg = NBN_ByteArrayMessage_Create(); - - memcpy(msg->bytes, bytes, length); - - msg->length = length; - - return NBN_GameClient_SendMessage(NBN_BYTE_ARRAY_MESSAGE_TYPE, channel_id, msg); -} - -int NBN_GameClient_SendUnreliableMessage(uint8_t msg_type, void *msg_data) -{ - return NBN_GameClient_SendMessage(msg_type, NBN_CHANNEL_RESERVED_UNRELIABLE, msg_data); -} - -int NBN_GameClient_SendReliableMessage(uint8_t msg_type, void *msg_data) -{ - return NBN_GameClient_SendMessage(msg_type, NBN_CHANNEL_RESERVED_RELIABLE, msg_data); -} - -int NBN_GameClient_SendUnreliableByteArray(uint8_t *bytes, unsigned int length) -{ - return NBN_GameClient_SendByteArray(bytes, length, NBN_CHANNEL_RESERVED_UNRELIABLE); -} - -int NBN_GameClient_SendReliableByteArray(uint8_t *bytes, unsigned int length) -{ - return NBN_GameClient_SendByteArray(bytes, length, NBN_CHANNEL_RESERVED_RELIABLE); -} - -NBN_Connection *NBN_GameClient_CreateServerConnection(int driver_id, void *driver_data, uint32_t protocol_id) -{ - NBN_Connection *server_connection = Endpoint_CreateConnection(&nbn_game_client.endpoint, 0, protocol_id, driver_id, driver_data); - -#ifdef NBN_DEBUG - server_connection->debug_callbacks = nbn_game_client.endpoint.debug_callbacks; -#endif - - nbn_game_client.server_connection = server_connection; - - return server_connection; -} - -NBN_MessageInfo NBN_GameClient_GetMessageInfo(void) -{ - assert(nbn_game_client.last_event.type == NBN_MESSAGE_RECEIVED); - - return nbn_game_client.last_event.data.message_info; -} - -NBN_ConnectionStats NBN_GameClient_GetStats(void) -{ - return nbn_game_client.server_connection->stats; -} - -int NBN_GameClient_GetServerCloseCode(void) -{ - return nbn_game_client.closed_code; -} - -bool NBN_GameClient_IsConnected(void) -{ - return nbn_game_client.is_connected; -} - -int NBN_GameClient_RegisterRPC(unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func) -{ - return Endpoint_RegisterRPC(&nbn_game_client.endpoint, id, signature, func); -} - -int NBN_GameClient_CallRPC(unsigned int id, ...) -{ - NBN_RPC rpc = nbn_game_client.endpoint.rpcs[id]; - - if (rpc.id != id) - { - NBN_LogError("Cannot call invalid RPC (ID: %d)", id); - - return NBN_ERROR; - } - - va_list args; - - va_start(args, id); - - NBN_RPC_Message *rpc_msg = Connection_BuildRPC(nbn_game_client.server_connection, &rpc, args); - - assert(rpc_msg); - - if (NBN_GameClient_SendMessage(NBN_RPC_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, rpc_msg) < 0) - { - NBN_LogError("Failed to send RPC message"); - - goto rpc_error; - } - - va_end(args); - - return 0; - -rpc_error: - va_end(args); - - return NBN_ERROR; -} - -#ifdef NBN_DEBUG - -void NBN_GameClient_Debug_RegisterCallback(NBN_ConnectionDebugCallback cbs) -{ - nbn_game_client.endpoint.debug_callbacks = cbs; -} - -#endif /* NBN_DEBUG */ - -static int GameClient_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *server_connection) -{ - assert(nbn_game_client.server_connection == server_connection); - - NBN_Event ev; - - ev.type = NBN_MESSAGE_RECEIVED; - - if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) - { - NBN_Channel *channel = server_connection->channels[message->header.channel_id]; - - if (!NBN_Channel_AddChunk(channel, message)) - return 0; - - NBN_Message complete_message; - - if (NBN_Channel_ReconstructMessageFromChunks(channel, server_connection, &complete_message) < 0) - { - NBN_LogError("Failed to reconstruct message from chunks"); - - return NBN_ERROR; - } - - NBN_MessageInfo msg_info = {complete_message.header.type, complete_message.header.channel_id, complete_message.data, 0}; - - ev.data.message_info = msg_info; - } - else - { - NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, 0}; - - ev.data.message_info = msg_info; - } - - if (!NBN_EventQueue_Enqueue(&nbn_game_client.endpoint.event_queue, ev)) - return NBN_ERROR; - - return 0; -} - -static int GameClient_HandleEvent(void) -{ - switch (nbn_game_client.last_event.type) - { - case NBN_MESSAGE_RECEIVED: - return GameClient_HandleMessageReceivedEvent(); - - default: - return nbn_game_client.last_event.type; - } -} - -static int GameClient_HandleMessageReceivedEvent(void) -{ - NBN_MessageInfo message_info = nbn_game_client.last_event.data.message_info; - - int ret = NBN_NO_EVENT; - - if (message_info.type == NBN_CLIENT_CLOSED_MESSAGE_TYPE) - { - nbn_game_client.is_connected = false; - nbn_game_client.closed_code = ((NBN_ClientClosedMessage *)message_info.data)->code; - ret = NBN_DISCONNECTED; - } - else if (message_info.type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE) - { - nbn_game_client.is_connected = true; - ret = NBN_CONNECTED; - - NBN_ClientAcceptedMessage *accepted_msg = (NBN_ClientAcceptedMessage *)message_info.data; - - memcpy(nbn_game_client.server_data, accepted_msg->data, accepted_msg->length); - nbn_game_client.server_data_len = accepted_msg->length; - } - else if (message_info.type == NBN_RPC_MESSAGE_TYPE) - { - ret = NBN_NO_EVENT; - - Connection_HandleReceivedRPC(nbn_game_client.server_connection->id, &nbn_game_client.endpoint, (NBN_RPC_Message *)message_info.data); - } - else - { - ret = NBN_MESSAGE_RECEIVED; - } - - return ret; -} - -#pragma endregion /* NBN_GameClient */ - -#pragma region Game client driver - -static void ClientDriver_OnPacketReceived(NBN_Packet *packet) -{ - // packets from server should always be valid - if (Endpoint_ProcessReceivedPacket(&nbn_game_client.endpoint, packet, nbn_game_client.server_connection) < 0) - { - NBN_LogError("Received invalid packet from server"); - NBN_Abort(); - } -} - -#pragma endregion /* Game Client driver */ - -#pragma region NBN_GameServer - -NBN_GameServer nbn_game_server; - -static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t msg_type, uint8_t channel_id, void *msg_data); -static int GameServer_AddClient(NBN_Connection *); -static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection); -static void GameServer_AddClientToClosedList(NBN_Connection *client); -static unsigned int GameServer_GetClientCount(void); -static int GameServer_ProcessReceivedMessage(NBN_Message *, NBN_Connection *); -static int GameServer_CloseStaleClientConnections(void); -static void GameServer_RemoveClosedClientConnections(void); -static int GameServer_HandleEvent(void); -static int GameServer_HandleMessageReceivedEvent(void); - -int NBN_GameServer_Start(const char *protocol_name, uint16_t port) -{ - return NBN_GameServer_StartEx(protocol_name, port); -} - -int NBN_GameServer_StartEx(const char *protocol_name, uint16_t port) -{ - if (nbn_driver_count < 1) - { - NBN_LogError("At least one network driver has to be registered"); - NBN_Abort(); - } - - Endpoint_Init(&nbn_game_server.endpoint, true); - - if ((nbn_game_server.clients = NBN_ConnectionVector_Create()) == NULL) - { - NBN_LogError("Failed to create connections vector"); - NBN_Abort(); - } - - if ((nbn_game_server.clients_table = NBN_ConnectionTable_Create()) == NULL) - { - NBN_LogError("Failed to create connections table"); - NBN_Abort(); - } - - nbn_game_server.closed_clients_head = NULL; - - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) continue; - - if (driver->impl.serv_start(Endpoint_BuildProtocolId(protocol_name), port) < 0) - { - NBN_LogError("Failed to start driver %s", driver->name); - return NBN_ERROR; - } - } - - NBN_LogInfo("Started"); - - return 0; -} - -void NBN_GameServer_Stop(void) -{ - // Poll remaning events to clear the event queue - while (NBN_GameServer_Poll() != NBN_NO_EVENT) {} - - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) - { - NBN_Connection_Destroy(nbn_game_server.clients->connections[i]); - } - - NBN_ConnectionVector_Destroy(nbn_game_server.clients); - NBN_ConnectionTable_Destroy(nbn_game_server.clients_table); - - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) continue; - - driver->impl.serv_stop(); - } - - // Free closed clients list - NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; - - while (current) - { - NBN_ConnectionListNode *next = current->next; - - NBN_Deallocator(current); - - current = next; - } - - nbn_game_server.closed_clients_head = NULL; - Endpoint_Deinit(&nbn_game_server.endpoint); - - NBN_LogInfo("Stopped"); -} - -void NBN_GameServer_RegisterMessage( - uint8_t msg_type, - NBN_MessageBuilder msg_builder, - NBN_MessageDestructor msg_destructor, - NBN_MessageSerializer msg_serializer) -{ - if (NBN_IsReservedMessage(msg_type)) - { - NBN_LogError("Message type %d is reserved by the library", msg_type); - NBN_Abort(); - } - - Endpoint_RegisterMessageBuilder(&nbn_game_server.endpoint, msg_builder, msg_type); - Endpoint_RegisterMessageDestructor(&nbn_game_server.endpoint, msg_destructor, msg_type); - Endpoint_RegisterMessageSerializer(&nbn_game_server.endpoint, msg_serializer, msg_type); -} - -int NBN_GameServer_Poll(void) -{ - Endpoint_UpdateTime(&nbn_game_server.endpoint); - - if (NBN_EventQueue_IsEmpty(&nbn_game_server.endpoint.event_queue)) - { - if (GameServer_CloseStaleClientConnections() < 0) - return NBN_ERROR; - - for (unsigned int i = 0; i < NBN_MAX_DRIVERS; i++) - { - NBN_Driver *driver = &nbn_drivers[i]; - - if (driver->id < 0) continue; - - if (driver->impl.serv_recv_packets() < 0) - { - NBN_LogError("Failed to read packets from driver %s", driver->name); - return NBN_ERROR; - } - } - - nbn_game_server.stats.download_bandwidth = 0; - - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) - { - NBN_Connection *client = nbn_game_server.clients->connections[i]; - - for (unsigned int i = 0; i < NBN_MAX_CHANNELS; i++) - { - NBN_Channel *channel = client->channels[i]; - - if (channel) - { - NBN_Message *msg; - - while ((msg = channel->GetNextRecvedMessage(channel)) != NULL) - { - if (GameServer_ProcessReceivedMessage(msg, client) < 0) - { - NBN_LogError("Failed to process received message"); - - return NBN_ERROR; - } - } - } - } - - if (!client->is_closed) - Connection_UpdateAverageDownloadBandwidth(client, nbn_game_server.endpoint.time); - - nbn_game_server.stats.download_bandwidth += client->stats.download_bandwidth; - client->last_read_packets_time = nbn_game_server.endpoint.time; - } - - GameServer_RemoveClosedClientConnections(); - } - - while (NBN_EventQueue_Dequeue(&nbn_game_server.endpoint.event_queue, &nbn_game_server.last_event)) - { - int ev = GameServer_HandleEvent(); - - if (ev != NBN_SKIP_EVENT) return ev; - } - - return NBN_NO_EVENT; -} - -int NBN_GameServer_SendPackets(void) -{ - nbn_game_server.stats.upload_bandwidth = 0; - - GameServer_RemoveClosedClientConnections(); - - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) - { - NBN_Connection *client = nbn_game_server.clients->connections[i]; - - assert(!(client->is_closed && client->is_stale)); - - if (!client->is_stale && NBN_Connection_FlushSendQueue(client, nbn_game_server.endpoint.time) < 0) - return NBN_ERROR; - - nbn_game_server.stats.upload_bandwidth += client->stats.upload_bandwidth; - } - - return 0; -} - -NBN_Connection *NBN_GameServer_CreateClientConnection(int driver_id, void *driver_data, uint32_t protocol_id, uint32_t conn_id) -{ - assert(conn_id > 0); // Connection IDs start at 1 - - NBN_Connection *client = Endpoint_CreateConnection(&nbn_game_server.endpoint, conn_id, protocol_id, driver_id, driver_data); - -#ifdef NBN_DEBUG - client->debug_callbacks = nbn_game_server.endpoint.debug_callbacks; -#endif - - return client; -} - -int NBN_GameServer_CloseClientWithCode(NBN_ConnectionHandle connection_handle, int code) -{ - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogError("Client %d does not exist", connection_handle); - return NBN_ERROR; - } - - return GameServer_CloseClientWithCode(client, code, false); -} - -int NBN_GameServer_CloseClient(NBN_ConnectionHandle connection_handle) -{ - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogError("Client %d does not exist", connection_handle); - return NBN_ERROR; - } - - return GameServer_CloseClientWithCode(client, -1, false); -} - -int NBN_GameServer_SendByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length, uint8_t channel_id) -{ - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogError("Client %d does not exist", connection_handle); - return NBN_ERROR; - } - - if (length > NBN_BYTE_ARRAY_MAX_SIZE) - { - NBN_LogError("Byte array cannot exceed %d bytes", NBN_BYTE_ARRAY_MAX_SIZE); - - return NBN_ERROR; - } - - NBN_ByteArrayMessage *msg = NBN_ByteArrayMessage_Create(); - - memcpy(msg->bytes, bytes, length); - - msg->length = length; - - return NBN_GameServer_SendMessageTo(connection_handle, NBN_BYTE_ARRAY_MESSAGE_TYPE, channel_id, msg); -} - -int NBN_GameServer_SendMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, uint8_t channel_id, void *msg_data) -{ - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogWarning("Cannot send message to client %d (does not exist)", connection_handle); - return 0; - } - - return GameServer_SendMessageTo(client, msg_type, channel_id, msg_data); -} - -int NBN_GameServer_SendUnreliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, void *msg_data) -{ - return NBN_GameServer_SendMessageTo(connection_handle, msg_type, NBN_CHANNEL_RESERVED_UNRELIABLE, msg_data); -} - -int NBN_GameServer_SendReliableMessageTo(NBN_ConnectionHandle connection_handle, uint8_t msg_type, void *msg_data) -{ - return NBN_GameServer_SendMessageTo(connection_handle, msg_type, NBN_CHANNEL_RESERVED_RELIABLE, msg_data); -} - -int NBN_GameServer_SendUnreliableByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length) -{ - return NBN_GameServer_SendByteArrayTo(connection_handle, bytes, length, NBN_CHANNEL_RESERVED_UNRELIABLE); -} - -int NBN_GameServer_SendReliableByteArrayTo(NBN_ConnectionHandle connection_handle, uint8_t *bytes, unsigned int length) -{ - return NBN_GameServer_SendByteArrayTo(connection_handle, bytes, length, NBN_CHANNEL_RESERVED_RELIABLE); -} - -int NBN_GameServer_AcceptIncomingConnection(void) -{ - return NBN_GameServer_AcceptIncomingConnectionWithData(NULL, 0); -} - -int NBN_GameServer_AcceptIncomingConnectionWithData(uint8_t *data, unsigned int length) -{ - assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - assert(nbn_game_server.last_event.data.connection != NULL); - - if (length > NBN_SERVER_DATA_MAX_SIZE) - { - NBN_LogError("Data length cannot exceed %d bytes", NBN_SERVER_DATA_MAX_SIZE); - return NBN_ERROR; - } - - NBN_Connection *client = nbn_game_server.last_event.data.connection; - NBN_ClientAcceptedMessage *msg = NBN_ClientAcceptedMessage_Create(); - - assert(msg != NULL); - - msg->length = length; - - if (data) - memcpy(msg->data, data, length); - - if (GameServer_SendMessageTo(client, NBN_CLIENT_ACCEPTED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg) < 0) - return NBN_ERROR; - - client->is_accepted = true; - - NBN_LogTrace("Client %d has been accepted", client->id); - - return 0; -} - -int NBN_GameServer_RejectIncomingConnectionWithCode(int code) -{ - assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - assert(nbn_game_server.last_event.data.connection != NULL); - - return GameServer_CloseClientWithCode(nbn_game_server.last_event.data.connection, code, false); -} - -int NBN_GameServer_RejectIncomingConnection(void) -{ - return NBN_GameServer_RejectIncomingConnectionWithCode(-1); -} - -NBN_ConnectionHandle NBN_GameServer_GetIncomingConnection(void) -{ - assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - assert(nbn_game_server.last_event.data.connection != NULL); - - return nbn_game_server.last_event.data.connection->id; -} - -unsigned int NBN_GameServer_ReadIncomingConnectionData(uint8_t *data) -{ - assert(nbn_game_server.last_event.type == NBN_NEW_CONNECTION); - - memcpy(data, nbn_game_server.last_connection_data, nbn_game_server.last_connection_data_len); - - return nbn_game_server.last_connection_data_len; -} - -NBN_ConnectionHandle NBN_GameServer_GetDisconnectedClient(void) -{ - assert(nbn_game_server.last_event.type == NBN_CLIENT_DISCONNECTED); - - return nbn_game_server.last_event.data.connection_handle; -} - -NBN_MessageInfo NBN_GameServer_GetMessageInfo(void) -{ - assert(nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED); - - return nbn_game_server.last_event.data.message_info; -} - -NBN_GameServerStats NBN_GameServer_GetStats(void) -{ - return nbn_game_server.stats; -} - -int NBN_GameServer_RegisterRPC(unsigned int id, NBN_RPC_Signature signature, NBN_RPC_Func func) -{ - return Endpoint_RegisterRPC(&nbn_game_server.endpoint, id, signature, func); -} - -int NBN_GameServer_CallRPC(unsigned int id, NBN_ConnectionHandle connection_handle, ...) -{ - NBN_Connection *client = NBN_ConnectionTable_Get(nbn_game_server.clients_table, connection_handle); - - if (client == NULL) - { - NBN_LogError("Client %d does not exist", connection_handle); - - return NBN_ERROR; - } - - NBN_RPC rpc = nbn_game_server.endpoint.rpcs[id]; - - if (rpc.id != id) - { - NBN_LogError("Cannot call invalid RPC (ID: %d)", id); - - return NBN_ERROR; - } - - va_list args; - - va_start(args, connection_handle); - - NBN_RPC_Message *rpc_msg = Connection_BuildRPC(client, &rpc, args); - - assert(rpc_msg); - - if (GameServer_SendMessageTo(client, NBN_RPC_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, rpc_msg) < 0) - { - NBN_LogError("Failed to send RPC message"); - - goto rpc_error; - } - - va_end(args); - - return 0; - -rpc_error: - va_end(args); - - return NBN_ERROR; -} - -#ifdef NBN_DEBUG - -void NBN_GameServer_Debug_RegisterCallback(NBN_ConnectionDebugCallback cbs) -{ - nbn_game_server.endpoint.debug_callbacks = cbs; -} - -#endif /* NBN_DEBUG */ - -static int GameServer_SendMessageTo(NBN_Connection *client, uint8_t msg_type, uint8_t channel_id, void *msg_data) -{ - NBN_OutgoingMessage *outgoing_msg = Endpoint_CreateOutgoingMessage(&nbn_game_server.endpoint, client->channels[channel_id], msg_type, msg_data); - - if (outgoing_msg == NULL) - { - NBN_LogError("Failed to create outgoing message"); - - return NBN_ERROR; - } - - /* The only message type we can send to an unaccepted client is a NBN_ClientAcceptedMessage message */ - assert(client->is_accepted || outgoing_msg->type == NBN_CLIENT_ACCEPTED_MESSAGE_TYPE || outgoing_msg->type == NBN_CLIENT_CLOSED_MESSAGE_TYPE); - - if (Endpoint_EnqueueOutgoingMessage(&nbn_game_server.endpoint, client, outgoing_msg, channel_id) < 0) - { - NBN_LogError("Failed to create outgoing message for client %d", client->id); - - /* Do not close the client if we failed to send the close client message to avoid infinite loops */ - if (outgoing_msg->type != NBN_CLIENT_CLOSED_MESSAGE_TYPE) - { - GameServer_CloseClientWithCode(client, -1, false); - - return NBN_ERROR; - } - } - - return 0; -} - -static int GameServer_AddClient(NBN_Connection *client) -{ - if (nbn_game_server.clients->count >= NBN_MAX_CLIENTS) - { - NBN_LogError("Cannot accept new client: too many clients"); - - return NBN_ERROR; - } - - NBN_ConnectionVector_Add(nbn_game_server.clients, client); - NBN_ConnectionTable_Add(nbn_game_server.clients_table, client); - - return 0; -} - -static int GameServer_CloseClientWithCode(NBN_Connection *client, int code, bool disconnection) -{ - if (!client->is_closed && client->is_accepted) - { - if (!disconnection) - { - NBN_Event e; - - e.type = NBN_CLIENT_DISCONNECTED; - e.data.connection_handle = client->id; - - if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) - return NBN_ERROR; - } - } - - if (client->is_stale) - { - NBN_LogDebug("Closing stale connection %d", client->id); - - GameServer_AddClientToClosedList(client); - client->is_closed = true; - - return 0; - } - - NBN_LogDebug("Closing active connection %d (will send a disconnection message)", client->id); - - GameServer_AddClientToClosedList(client); - client->is_closed = true; - - if (!disconnection) - { - NBN_LogDebug("Send close message for client %d (code: %d)", client->id, code); - - NBN_ClientClosedMessage *msg = NBN_ClientClosedMessage_Create(); - - msg->code = code; - - GameServer_SendMessageTo(client, NBN_CLIENT_CLOSED_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_LIBRARY_MESSAGES, msg); - } - - return 0; -} - -static void GameServer_AddClientToClosedList(NBN_Connection *client) -{ - if (client->is_closed) return; - - NBN_ConnectionListNode *node = (NBN_ConnectionListNode *)NBN_Allocator(sizeof(NBN_ConnectionListNode)); - - node->conn = client; - node->next = NULL; - - if (nbn_game_server.closed_clients_head == NULL) - { - // list is empty - nbn_game_server.closed_clients_head = node; - node->prev = NULL; - } - else - { - // list is not empty, add node at the end - NBN_ConnectionListNode *tail = nbn_game_server.closed_clients_head; - - while (tail->next != NULL) tail = tail->next; - - node->prev = tail; - tail->next = node; - } -} - -static inline unsigned int GameServer_GetClientCount(void) -{ - return nbn_game_server.clients->count; -} - -static int GameServer_ProcessReceivedMessage(NBN_Message *message, NBN_Connection *client) -{ - NBN_Event ev; - - ev.type = NBN_CLIENT_MESSAGE_RECEIVED; - - if (message->header.type == NBN_MESSAGE_CHUNK_TYPE) - { - NBN_Channel *channel = client->channels[message->header.channel_id]; - - if (!NBN_Channel_AddChunk(channel, message)) - return 0; - - NBN_Message complete_message; - - if (NBN_Channel_ReconstructMessageFromChunks(channel, client, &complete_message) < 0) - { - NBN_LogError("Failed to reconstruct message from chunks"); - - return NBN_ERROR; - } - - NBN_MessageInfo msg_info = {complete_message.header.type, complete_message.header.channel_id, complete_message.data, client->id}; - - ev.data.message_info = msg_info; - } - else - { - NBN_MessageInfo msg_info = {message->header.type, message->header.channel_id, message->data, client->id}; - - ev.data.message_info = msg_info; - } - - if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, ev)) - return NBN_ERROR; - - return 0; -} - -static int GameServer_CloseStaleClientConnections(void) -{ - for (unsigned int i = 0; i < nbn_game_server.clients->count; i++) - { - NBN_Connection *client = nbn_game_server.clients->connections[i]; - - if (!client->is_stale && NBN_Connection_CheckIfStale(client, nbn_game_server.endpoint.time)) - { - NBN_LogInfo("Client %d connection is stale, closing it.", client->id); - - client->is_stale = true; - - if (GameServer_CloseClientWithCode(client, -1, false) < 0) - return NBN_ERROR; - } - } - - return 0; -} - -static void GameServer_RemoveClosedClientConnections(void) -{ - NBN_ConnectionListNode *current = nbn_game_server.closed_clients_head; - - while (current) - { - NBN_ConnectionListNode *prev = current->prev; - NBN_ConnectionListNode *next = current->next; - NBN_Connection *client = current->conn; - - assert(client->id > 0); - - if (client->is_stale) - { - NBN_LogDebug("Remove closed client connection (ID: %d)", client->id); - - client->driver->impl.serv_remove_connection(client); // Notify the driver to remove the connection - - // Remove the connection from the connections vector and table - - uint32_t rm_conn_id = NBN_ConnectionVector_RemoveAt(nbn_game_server.clients, client->vector_pos); - - if (rm_conn_id != client->id) - { - NBN_LogError("Failed to remove client connection from connections vector"); - NBN_Abort(); - } - - bool table_rm = NBN_ConnectionTable_Remove(nbn_game_server.clients_table, client->id); - - if (!table_rm) - { - NBN_LogError("Failed to remove client connection from connections table"); - NBN_Abort(); - } - - // Destroy the connection - - NBN_Connection_Destroy(client); - - // Remove the connection from the closed clients list - - NBN_Deallocator(current); - - if (current == nbn_game_server.closed_clients_head) - { - // delete the head of the list - NBN_ConnectionListNode *new_head = next; - - if (new_head) - { - new_head->prev = NULL; - } - - nbn_game_server.closed_clients_head = new_head; - } - else - { - // delete a node in the middle of the list - prev->next = next; - - if (next) next->prev = prev; - } - } - - current = next; - } -} - -static int GameServer_HandleEvent(void) -{ - return nbn_game_server.last_event.type == NBN_CLIENT_MESSAGE_RECEIVED ? - GameServer_HandleMessageReceivedEvent() : - nbn_game_server.last_event.type; -} - -static int GameServer_HandleMessageReceivedEvent(void) -{ - NBN_MessageInfo message_info = nbn_game_server.last_event.data.message_info; - NBN_Connection *sender = NBN_ConnectionTable_Get(nbn_game_server.clients_table, message_info.sender); - - if (sender == NULL) - { - // skip events related to unknown clients (clients that might have been removed) - NBN_LogTrace("Received message from unknown client (ID: %d)", message_info.sender); - return NBN_SKIP_EVENT; - } - - // skip all events related to a closed or stale connection - if (sender->is_closed || sender->is_stale) - return NBN_SKIP_EVENT; - - if (message_info.type == NBN_DISCONNECTION_MESSAGE_TYPE) - { - NBN_LogInfo("Received disconnection message from client %d", sender->id); - - if (GameServer_CloseClientWithCode(sender, -1, true) < 0) - return NBN_ERROR; - - sender->is_stale = true; - - nbn_game_server.last_event.type = NBN_CLIENT_DISCONNECTED; - nbn_game_server.last_event.data.connection_handle = sender->id; - - GameServer_RemoveClosedClientConnections(); - - return NBN_CLIENT_DISCONNECTED; - } - - int ret = NBN_CLIENT_MESSAGE_RECEIVED; - - if (message_info.type == NBN_RPC_MESSAGE_TYPE) - { - ret = NBN_NO_EVENT; - - Connection_HandleReceivedRPC(message_info.sender, &nbn_game_server.endpoint, (NBN_RPC_Message *)message_info.data); - } - else if (message_info.type == NBN_CONNECTION_REQUEST_MESSAGE_TYPE) - { - ret = NBN_NO_EVENT; - - NBN_ConnectionRequestMessage *msg = (NBN_ConnectionRequestMessage *)message_info.data; - - nbn_game_server.last_connection_data_len = msg->length; - memcpy(nbn_game_server.last_connection_data, msg->data, msg->length); - - NBN_Event e; - - e.type = NBN_NEW_CONNECTION; - e.data.connection = sender; - - if (!NBN_EventQueue_Enqueue(&nbn_game_server.endpoint.event_queue, e)) - return NBN_ERROR; - } - - return ret; -} - -#pragma endregion /* NBN_GameServer */ - -#pragma region Game server driver - -static int ServerDriver_OnClientConnected(NBN_Connection *client) -{ - if (GameServer_AddClient(client) < 0) - { - NBN_LogError("Failed to add client"); - - return NBN_ERROR; - } - - return 0; -} - -static int ServerDriver_OnClientPacketReceived(NBN_Packet *packet) -{ - if (Endpoint_ProcessReceivedPacket(&nbn_game_server.endpoint, packet, packet->sender) < 0) - { - NBN_LogError("An error occured while processing packet from client %d, closing the client", packet->sender->id); - - return GameServer_CloseClientWithCode(packet->sender, -1, false); - } - - return 0; -} - -#pragma endregion /* Game server driver */ - -#pragma region Packet simulator - -#if defined(NBN_DEBUG) && defined(NBN_USE_PACKET_SIMULATOR) - -#define RAND_RATIO_BETWEEN(min, max) (((rand() % (int)((max * 100.f) - (min * 100.f) + 1)) + (min * 100.f)) / 100.f) -#define RAND_RATIO RAND_RATIO_BETWEEN(0, 1) - -#ifdef NBNET_WINDOWS -DWORD WINAPI PacketSimulator_Routine(LPVOID); -#else -static void *PacketSimulator_Routine(void *); -#endif - -static int PacketSimulator_SendPacket(NBN_PacketSimulator *, NBN_Packet *, NBN_Connection *receiver); -static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *); - -void NBN_PacketSimulator_Init(NBN_PacketSimulator *packet_simulator, NBN_Endpoint *endpoint) -{ - packet_simulator->endpoint = endpoint; - packet_simulator->running = false; - packet_simulator->ping = 0; - packet_simulator->jitter = 0; - packet_simulator->packet_loss_ratio = 0; - packet_simulator->total_dropped_packets = 0; - packet_simulator->packet_duplication_ratio = 0; - packet_simulator->head_packet = NULL; - packet_simulator->tail_packet = NULL; - packet_simulator->packet_count = 0; - -#ifdef NBNET_WINDOWS - packet_simulator->queue_mutex = CreateMutex(NULL, FALSE, NULL); -#else - packet_simulator->queue_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; -#endif -} - -int NBN_PacketSimulator_EnqueuePacket(NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, NBN_Connection *receiver) -{ -#ifdef NBNET_WINDOWS - WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); -#else - pthread_mutex_lock(&packet_simulator->queue_mutex); -#endif - - /* Compute jitter in range [ -jitter, +jitter ]. - * Jitter is converted from seconds to milliseconds for the random operation below. - */ - - int jitter = packet_simulator->jitter * 1000; - - jitter = (jitter > 0) ? (rand() % (jitter * 2)) - jitter : 0; - - NBN_PacketSimulatorEntry *entry = (NBN_PacketSimulatorEntry *)MemoryManager_Alloc(NBN_MEM_PACKET_SIMULATOR_ENTRY); - - entry->delay = packet_simulator->ping + (double)jitter / 1000; /* and converted back to seconds */ - entry->receiver = receiver; - entry->enqueued_at = packet_simulator->endpoint->time; - - memcpy(&entry->packet, packet, sizeof(NBN_Packet)); - - if (packet_simulator->packet_count > 0) - { - entry->prev = packet_simulator->tail_packet; - entry->next = NULL; - - packet_simulator->tail_packet->next = entry; - packet_simulator->tail_packet = entry; - } - else // the list is empty - { - entry->prev = NULL; - entry->next = NULL; - - packet_simulator->head_packet = entry; - packet_simulator->tail_packet = entry; - } - - packet_simulator->packet_count++; - -#ifdef NBNET_WINDOWS - ReleaseMutex(packet_simulator->queue_mutex); -#else - pthread_mutex_unlock(&packet_simulator->queue_mutex); -#endif - - return 0; -} - -void NBN_PacketSimulator_Start(NBN_PacketSimulator *packet_simulator) -{ -#ifdef NBNET_WINDOWS - packet_simulator->thread = CreateThread(NULL, 0, PacketSimulator_Routine, packet_simulator, 0, NULL); -#else - pthread_create(&packet_simulator->thread, NULL, PacketSimulator_Routine, packet_simulator); -#endif - - packet_simulator->running = true; -} - -void NBN_PacketSimulator_Stop(NBN_PacketSimulator *packet_simulator) -{ - packet_simulator->running = false; - -#ifdef NBNET_WINDOWS - WaitForSingleObject(packet_simulator->thread, INFINITE); -#else - pthread_join(packet_simulator->thread, NULL); -#endif -} - -#ifdef NBNET_WINDOWS -DWORD WINAPI PacketSimulator_Routine(LPVOID arg) -#else -static void *PacketSimulator_Routine(void *arg) -#endif -{ - NBN_PacketSimulator *packet_simulator = (NBN_PacketSimulator *)arg; - - while (packet_simulator->running) - { -#ifdef NBNET_WINDOWS - WaitForSingleObject(packet_simulator->queue_mutex, INFINITE); -#else - pthread_mutex_lock(&packet_simulator->queue_mutex); -#endif - - NBN_PacketSimulatorEntry *entry = packet_simulator->head_packet; - - while (entry) - { - NBN_PacketSimulatorEntry *next = entry->next; - - if (packet_simulator->endpoint->time - entry->enqueued_at < entry->delay) - { - entry = next; - - continue; - } - - PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); - - for (unsigned int i = 0; i < PacketSimulator_GetRandomDuplicatePacketCount(packet_simulator); i++) - { - NBN_LogDebug("Duplicate packet %d (count: %d)", entry->packet.header.seq_number, i + 1); - - PacketSimulator_SendPacket(packet_simulator, &entry->packet, entry->receiver); - } - - // remove the entry from the packet list - if (entry == packet_simulator->head_packet) // it's the head of the list - { - NBN_PacketSimulatorEntry *new_head = entry->next; - - if (new_head) - new_head->prev = NULL; - else - packet_simulator->tail_packet = NULL; - - packet_simulator->head_packet = new_head; - } - else if (entry == packet_simulator->tail_packet) // it's the tail of the list - { - NBN_PacketSimulatorEntry *new_tail = entry->prev; - - new_tail->next = NULL; - packet_simulator->tail_packet = new_tail; - } - else // it's in the middle of the list - { - entry->prev->next = entry->next; - entry->next->prev = entry->prev; - } - - packet_simulator->packet_count--; - - // release the memory allocated for the entry - MemoryManager_Dealloc(entry, NBN_MEM_PACKET_SIMULATOR_ENTRY); - - entry = next; - } - -#ifdef NBNET_WINDOWS - ReleaseMutex(packet_simulator->queue_mutex); -#else - pthread_mutex_unlock(&packet_simulator->queue_mutex); -#endif - } - -#ifdef NBNET_WINDOWS - return 0; -#else - return NULL; -#endif -} - -static int PacketSimulator_SendPacket( - NBN_PacketSimulator *packet_simulator, NBN_Packet *packet, NBN_Connection *receiver) -{ - if (RAND_RATIO < packet_simulator->packet_loss_ratio) - { - packet_simulator->total_dropped_packets++; - NBN_LogDebug("Drop packet %d (Total dropped packets: %d)", packet->header.seq_number, packet_simulator->total_dropped_packets); - - return 0; - } - - NBN_Driver *driver = receiver->driver; - - if (receiver->endpoint->is_server) - { - if (receiver->is_stale) - return 0; - - return driver->impl.serv_send_packet_to(packet, receiver); - } - else - { - return driver->impl.cli_send_packet(packet); - } -} - -static unsigned int PacketSimulator_GetRandomDuplicatePacketCount(NBN_PacketSimulator *packet_simulator) -{ - if (RAND_RATIO < packet_simulator->packet_duplication_ratio) - return rand() % 10 + 1; - - return 0; -} - -#endif /* NBN_DEBUG && NBN_USE_PACKET_SIMULATOR */ - -#pragma endregion /* Packet simulator */ - -#endif /* NBNET_IMPL */ - -#pragma endregion /* Implementations */ +#endif /* NBNET_H */ diff --git a/net_drivers/udp.h b/net_drivers/udp.h deleted file mode 100644 index 95059ec..0000000 --- a/net_drivers/udp.h +++ /dev/null @@ -1,719 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -/* - --- NBNET UDP DRIVER --- - - Portable single UDP socket network driver for the nbnet library. - - How to use: - - 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_UDP_Register in both your client and server code before calling NBN_GameClient_Start or NBN_GameServer_Start -*/ - -void NBN_UDP_Register(void); - -#ifdef NBNET_IMPL - -#include -#include -#include - -#define NBN_UDP_DRIVER_ID 0 -#define NBN_UDP_DRIVER_NAME "UDP" - -#pragma region Platform detection - -#if defined(_WIN32) || defined(_WIN64) - #ifndef PLATFORM_WINDOWS - #define PLATFORM_WINDOWS - #endif -#elif (defined(__APPLE__) && defined(__MACH__)) - #define PLATFORM_MAC -#else - #define PLATFORM_UNIX -#endif - -#pragma endregion /* Platform detection */ - -#if defined(PLATFORM_WINDOWS) - -#include - -typedef int socklen_t; - -#elif defined(PLATFORM_UNIX) || defined(PLATFORM_MAC) - -#include -#include -#include -#include -#include -#include - -#define INVALID_SOCKET -1 -#define SOCKET_ERROR -1 -#define closesocket(s) close(s) - -typedef int SOCKET; -typedef struct sockaddr_in SOCKADDR_IN; -typedef struct sockaddr SOCKADDR; -typedef struct in_addr IN_ADDR; - -#endif - -typedef struct -{ - uint32_t host; - uint16_t port; -} NBN_IPAddress; - -typedef struct -{ - uint32_t id; - NBN_IPAddress address; - NBN_Connection *conn; // nbnet connection associated to this UDP connection -} NBN_UDP_Connection; - -static SOCKET nbn_udp_sock; - -static bool CompareIPAddresses(NBN_IPAddress ip_addr1, NBN_IPAddress ip_addr2); - -#pragma region Hashtable - -#define HTABLE_DEFAULT_INITIAL_CAPACITY 32 -#define HTABLE_LOAD_FACTOR_THRESHOLD 0.75 - -typedef struct -{ - NBN_IPAddress ip_addr; - NBN_UDP_Connection *conn; - unsigned int slot; -} NBN_UDP_HTableEntry; - -typedef struct -{ - NBN_UDP_HTableEntry **internal_array; - unsigned int capacity; - unsigned int count; - float load_factor; -} NBN_UDP_HTable; - -static NBN_UDP_HTable *NBN_UDP_HTable_Create(void); -static NBN_UDP_HTable *NBN_UDP_HTable_CreateWithCapacity(unsigned int); -static void NBN_UDP_HTable_Destroy(NBN_UDP_HTable *); -static void NBN_UDP_HTable_Add(NBN_UDP_HTable *, NBN_IPAddress, NBN_UDP_Connection *); -static NBN_UDP_Connection *NBN_UDP_HTable_Get(NBN_UDP_HTable *, NBN_IPAddress); -static NBN_UDP_Connection *NBN_UDP_HTable_Remove(NBN_UDP_HTable *, NBN_IPAddress); -static void NBN_UDP_HTable_InsertEntry(NBN_UDP_HTable *, NBN_UDP_HTableEntry *); -static void NBN_UDP_HTable_RemoveEntry(NBN_UDP_HTable *, NBN_UDP_HTableEntry *); -static unsigned int NBN_UDP_HTable_FindFreeSlot(NBN_UDP_HTable *, NBN_UDP_HTableEntry *, bool *); -static NBN_UDP_HTableEntry *NBN_UDP_HTable_FindEntry(NBN_UDP_HTable *, NBN_IPAddress); -static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *); -static unsigned long NBN_UDP_HTable_HashSDBM(NBN_IPAddress); - -static NBN_UDP_HTable *NBN_UDP_HTable_Create(void) -{ - return NBN_UDP_HTable_CreateWithCapacity(HTABLE_DEFAULT_INITIAL_CAPACITY); -} - -static NBN_UDP_HTable *NBN_UDP_HTable_CreateWithCapacity(unsigned int capacity) -{ - NBN_UDP_HTable *htable = (NBN_UDP_HTable *) NBN_Allocator(sizeof(NBN_UDP_HTable)); - - htable->internal_array = (NBN_UDP_HTableEntry **) NBN_Allocator(sizeof(NBN_UDP_HTableEntry *) * capacity); - htable->capacity = capacity; - htable->count = 0; - htable->load_factor = 0; - - for (unsigned int i = 0; i < htable->capacity; i++) - htable->internal_array[i] = NULL; - - return htable; -} - -static void NBN_UDP_HTable_Destroy(NBN_UDP_HTable *htable) -{ - for (unsigned int i = 0; i < htable->capacity; i++) - { - NBN_UDP_HTableEntry *entry = htable->internal_array[i]; - - if (entry) - NBN_Deallocator(entry); - } - - NBN_Deallocator(htable->internal_array); - NBN_Deallocator(htable); -} - -static void NBN_UDP_HTable_Add(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr, NBN_UDP_Connection *conn) -{ - NBN_UDP_HTableEntry *entry = (NBN_UDP_HTableEntry*) NBN_Allocator(sizeof(NBN_UDP_HTableEntry)); - - entry->ip_addr = ip_addr; - entry->conn = conn; - - NBN_UDP_HTable_InsertEntry(htable, entry); - - if (htable->load_factor >= HTABLE_LOAD_FACTOR_THRESHOLD) - NBN_UDP_HTable_Grow(htable); -} - -static NBN_UDP_Connection *NBN_UDP_HTable_Get(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) -{ - NBN_UDP_HTableEntry *entry = NBN_UDP_HTable_FindEntry(htable, ip_addr); - - return entry ? entry->conn : NULL; -} - -static NBN_UDP_Connection *NBN_UDP_HTable_Remove(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) -{ - NBN_UDP_HTableEntry *entry = NBN_UDP_HTable_FindEntry(htable, ip_addr); - - if (entry) - { - NBN_UDP_Connection *conn = entry->conn; - NBN_UDP_HTable_RemoveEntry(htable, entry); - - return conn; - } - - return NULL; -} - -static void NBN_UDP_HTable_InsertEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry) -{ - bool use_existing_slot = false; - unsigned int slot = NBN_UDP_HTable_FindFreeSlot(htable, entry, &use_existing_slot); - - entry->slot = slot; - htable->internal_array[slot] = entry; - - if (!use_existing_slot) - { - htable->count++; - htable->load_factor = (float)htable->count / htable->capacity; - } -} - -static void NBN_UDP_HTable_RemoveEntry(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry) -{ - htable->internal_array[entry->slot] = NULL; - - NBN_Deallocator(entry); - - htable->count--; - htable->load_factor = (float)htable->count / htable->capacity; -} - -static unsigned int NBN_UDP_HTable_FindFreeSlot(NBN_UDP_HTable *htable, NBN_UDP_HTableEntry *entry, bool *use_existing_slot) -{ - unsigned long hash = NBN_UDP_HTable_HashSDBM(entry->ip_addr); - unsigned int slot; - - // quadratic probing - - NBN_UDP_HTableEntry *current_entry; - unsigned int i = 0; - - do - { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - i++; - } while (current_entry != NULL && !CompareIPAddresses(current_entry->ip_addr, entry->ip_addr)); - - if (current_entry != NULL) // it means the current entry as the same key as the inserted entry - { - *use_existing_slot = true; - - NBN_Deallocator(current_entry); - } - - return slot; -} - -static NBN_UDP_HTableEntry *NBN_UDP_HTable_FindEntry(NBN_UDP_HTable *htable, NBN_IPAddress ip_addr) -{ - unsigned long hash = NBN_UDP_HTable_HashSDBM(ip_addr); - unsigned int slot; - - //quadratic probing - - NBN_UDP_HTableEntry *current_entry; - unsigned int i = 0; - - do - { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - if (current_entry != NULL && CompareIPAddresses(current_entry->ip_addr, ip_addr)) - { - return current_entry; - } - - i++; - } while (i < htable->capacity); - - return NULL; -} - -static void NBN_UDP_HTable_Grow(NBN_UDP_HTable *htable) -{ - unsigned int old_capacity = htable->capacity; - unsigned int new_capacity = old_capacity * 2; - NBN_UDP_HTableEntry** old_internal_array = htable->internal_array; - NBN_UDP_HTableEntry** new_internal_array = (NBN_UDP_HTableEntry**) NBN_Allocator(sizeof(NBN_UDP_HTableEntry*) * new_capacity); - - for (unsigned int i = 0; i < new_capacity; i++) - { - new_internal_array[i] = NULL; - } - - htable->internal_array = new_internal_array; - htable->capacity = new_capacity; - htable->count = 0; - htable->load_factor = 0; - - // rehash - - for (unsigned int i = 0; i < old_capacity; i++) - { - if (old_internal_array[i]) - NBN_UDP_HTable_InsertEntry(htable, old_internal_array[i]); - } - - NBN_Deallocator(old_internal_array); -} - -static unsigned long NBN_UDP_HTable_HashSDBM(NBN_IPAddress ip_addr) -{ - return ip_addr.host ^ ip_addr.port; -} - -#pragma endregion // Hashtable - -#pragma region Socket functions - -#ifdef PLATFORM_WINDOWS - -static char err_msg[32]; - -#endif - -static int InitSocket(void); -static void DeinitSocket(void); -static int BindSocket(uint16_t); -static char *GetLastErrorMessage(void); - -static int InitSocket(void) -{ -#ifdef PLATFORM_WINDOWS - WSADATA wsa; - int err = WSAStartup(MAKEWORD(2, 2), &wsa); - if (err < 0) - { - NBN_LogError("WSAStartup() failed"); - - return NBN_ERROR; - } -#endif - - if ((nbn_udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) - return NBN_ERROR; - -#if defined(PLATFORM_WINDOWS) - DWORD non_blocking = 1; - - if (ioctlsocket(nbn_udp_sock, FIONBIO, &non_blocking) != 0) - { - NBN_LogError("ioctlsocket() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } -#elif defined(PLATFORM_MAC) || defined(PLATFORM_UNIX) - int non_blocking = 1; - - if (fcntl(nbn_udp_sock, F_SETFL, O_NONBLOCK, non_blocking) < 0) - { - NBN_LogError("fcntl() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } -#endif - - return 0; -} - -static void DeinitSocket(void) -{ - closesocket(nbn_udp_sock); - -#ifdef PLATFORM_WINDOWS - WSACleanup(); -#endif -} - -static int BindSocket(uint16_t port) -{ - SOCKADDR_IN sin; - - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - - if (bind(nbn_udp_sock, (SOCKADDR *)&sin, sizeof(sin)) < 0) - { - NBN_LogError("bind() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } - - return 0; -} - -static int ResolveIpAddress(const char *host, uint16_t port, NBN_IPAddress *address) -{ - size_t host_len = strlen(host); - char *dup_host = (char*)NBN_Allocator(host_len + 1); - memcpy(dup_host, host, host_len + 1); - uint8_t arr[4]; - - for (int i = 0; i < 4; i++) - { - char *s; - - // TODO: replace strtok with strsep - if ((s = strtok(i == 0 ? dup_host : NULL, ".")) == NULL) - return NBN_ERROR; - - char *end = NULL; - int v = strtol(s, &end, 10); - - if (end == s || v < 0 || v > 255) - return NBN_ERROR; - - arr[i] = (uint8_t)v; - } - - address->host = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3]; - address->port = port; - - NBN_Deallocator(dup_host); - - return 0; -} - -static char *GetLastErrorMessage(void) -{ -#ifdef PLATFORM_WINDOWS - snprintf(err_msg, sizeof(err_msg), "%d", WSAGetLastError()); - - return err_msg; -#else - return strerror(errno); -#endif -} - -#pragma endregion /* Socket functions */ - -#pragma region Game server - -typedef struct NBN_UDP_Server -{ - NBN_UDP_HTable *connections; - uint32_t next_conn_id; // nbnet connection ids start at 1 - uint32_t protocol_id; -} NBN_UDP_Server; - -static NBN_UDP_Server nbn_udp_serv = {NULL, 1, 0}; - -static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress); - -static int NBN_UDP_ServStart(uint32_t protocol_id, uint16_t port) -{ - nbn_udp_serv.protocol_id = protocol_id; - nbn_udp_serv.connections = NBN_UDP_HTable_Create(); - - if (InitSocket() < 0) - return NBN_ERROR; - - if (BindSocket(port) < 0) - return NBN_ERROR; - - return 0; -} - -static void NBN_UDP_ServStop(void) -{ - NBN_UDP_HTable_Destroy(nbn_udp_serv.connections); - DeinitSocket(); -} - -static int NBN_UDP_ServRecvPackets(void) -{ - uint8_t buffer[NBN_PACKET_MAX_SIZE] = {0}; - SOCKADDR_IN src_addr; - socklen_t src_addr_len = sizeof(src_addr); - NBN_IPAddress ip_address; - - while (true) - { - int bytes = recvfrom(nbn_udp_sock, (char *)buffer, sizeof(buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); - - if (bytes <= 0) - break; - - ip_address.host = ntohl(src_addr.sin_addr.s_addr); - ip_address.port = ntohs(src_addr.sin_port); - - if (NBN_Packet_ReadProtocolId(buffer, bytes) != nbn_udp_serv.protocol_id) - continue; /* not matching the protocol of the receiver */ - - NBN_Connection *conn = FindOrCreateClientConnectionByAddress(ip_address); - - if (conn == NULL) - continue; // skip the connection - - NBN_Packet packet; - - if (NBN_Packet_InitRead(&packet, conn, buffer, bytes) < 0) - continue; /* not a valid packet */ - - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet) < 0) - { - NBN_LogError("Failed to raise game server event"); - - return NBN_ERROR; - } - } - - return 0; -} - -static void NBN_UDP_ServRemoveClientConnection(NBN_Connection *connection) -{ - assert(connection != NULL); - - NBN_UDP_Connection *udp_conn = NBN_UDP_HTable_Remove(nbn_udp_serv.connections, ((NBN_UDP_Connection *)connection->driver_data)->address); - - if (udp_conn) - { - NBN_LogDebug("Destroyed UDP connection %d", connection->id); - - NBN_Deallocator(udp_conn); - } -} - -static int NBN_UDP_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *connection) -{ - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)connection->driver_data; - - SOCKADDR_IN dest_addr; - - dest_addr.sin_addr.s_addr = htonl(udp_conn->address.host); - dest_addr.sin_family = AF_INET; - dest_addr.sin_port = htons(udp_conn->address.port); - - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, sizeof(dest_addr)) == SOCKET_ERROR) - { - NBN_LogError("sendto() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } - - return 0; -} - -static NBN_Connection *FindOrCreateClientConnectionByAddress(NBN_IPAddress address) -{ - NBN_UDP_Connection *udp_conn = NBN_UDP_HTable_Get(nbn_udp_serv.connections, address); - - if (udp_conn == NULL) - { - /* this is a new connection */ - - if (GameServer_GetClientCount() >= NBN_MAX_CLIENTS) - return NULL; - - udp_conn = (NBN_UDP_Connection *)NBN_Allocator(sizeof(NBN_UDP_Connection)); - - udp_conn->id = nbn_udp_serv.next_conn_id++; - udp_conn->address = address; - udp_conn->conn = NBN_GameServer_CreateClientConnection(NBN_UDP_DRIVER_ID, udp_conn, nbn_udp_serv.protocol_id, udp_conn->id); - - NBN_UDP_HTable_Add(nbn_udp_serv.connections, address, udp_conn); - - NBN_LogDebug("New UDP connection (id: %d)", udp_conn->id); - - if (NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, udp_conn->conn) < 0) - { - NBN_LogError("Failed to raise game server event"); - - return NULL; - } - } - - return udp_conn->conn; -} - -static bool CompareIPAddresses(NBN_IPAddress ip_addr1, NBN_IPAddress ip_addr2) -{ - return ip_addr1.host == ip_addr2.host && ip_addr1.port == ip_addr2.port; -} - -#pragma endregion /* Game server */ - -#pragma region Game client - -typedef struct NBN_UDP_Client -{ - NBN_Connection *server_conn; - uint32_t protocol_id; -} NBN_UDP_Client; - -static NBN_UDP_Client nbn_udp_cli = {NULL, 0}; - -static int ResolveIpAddress(const char *, uint16_t, NBN_IPAddress *); - -static int NBN_UDP_CliStart(uint32_t protocol_id, const char *host, uint16_t port) -{ - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)NBN_Allocator(sizeof(NBN_Connection)); - - nbn_udp_cli.protocol_id = protocol_id; - - if (ResolveIpAddress(host, port, &udp_conn->address) < 0) - { - NBN_LogError("Failed to resolve IP address from %s", host); - - return NBN_ERROR; - } - - if (InitSocket() < 0) - return NBN_ERROR; - - if (BindSocket(0) < 0) - return NBN_ERROR; - - nbn_udp_cli.server_conn = NBN_GameClient_CreateServerConnection(NBN_UDP_DRIVER_ID, udp_conn, protocol_id); - - return 0; -} - -static void NBN_UDP_CliStop(void) -{ - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; - - NBN_Deallocator(udp_conn); - DeinitSocket(); -} - -static int NBN_UDP_CliRecvPackets(void) -{ - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; - uint8_t buffer[NBN_PACKET_MAX_SIZE] = {0}; - SOCKADDR_IN src_addr; - socklen_t src_addr_len = sizeof(src_addr); - - while (true) - { - int bytes = recvfrom(nbn_udp_sock, (char *)buffer, sizeof(buffer), 0, (SOCKADDR *)&src_addr, &src_addr_len); - - if (bytes <= 0) - break; - - NBN_IPAddress ip_address; - - ip_address.host = ntohl(src_addr.sin_addr.s_addr); - ip_address.port = ntohs(src_addr.sin_port); - - /* make sure the received packet is from the server */ - if (ip_address.host != udp_conn->address.host || ip_address.port != udp_conn->address.port) - continue; - - if (NBN_Packet_ReadProtocolId(buffer, bytes) != nbn_udp_cli.protocol_id) - continue; /* not matching the protocol of the receiver */ - - NBN_Packet packet; - - if (NBN_Packet_InitRead(&packet, nbn_udp_cli.server_conn, buffer, bytes) < 0) - continue; /* not a valid packet */ - - NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); - } - - return 0; -} - -static int NBN_UDP_CliSendPacket(NBN_Packet *packet) -{ - NBN_UDP_Connection *udp_conn = (NBN_UDP_Connection *)nbn_udp_cli.server_conn->driver_data; - SOCKADDR_IN dest_addr; - - dest_addr.sin_addr.s_addr = htonl(udp_conn->address.host); - dest_addr.sin_family = AF_INET; - dest_addr.sin_port = htons(udp_conn->address.port); - - if (sendto(nbn_udp_sock, (const char *)packet->buffer, packet->size, 0, (SOCKADDR *)&dest_addr, sizeof(dest_addr)) == SOCKET_ERROR) - { - NBN_LogError("sendto() failed: %s", GetLastErrorMessage()); - - return NBN_ERROR; - } - - return 0; -} - -#pragma endregion /* Game client */ - -#pragma region Driver registering - -void NBN_UDP_Register(void) -{ - NBN_DriverImplementation driver_impl = { - // Client implementation - NBN_UDP_CliStart, - NBN_UDP_CliStop, - NBN_UDP_CliRecvPackets, - NBN_UDP_CliSendPacket, - - // Server implementation - NBN_UDP_ServStart, - NBN_UDP_ServStop, - NBN_UDP_ServRecvPackets, - NBN_UDP_ServSendPacketTo, - NBN_UDP_ServRemoveClientConnection - }; - - NBN_Driver_Register( - NBN_UDP_DRIVER_ID, - NBN_UDP_DRIVER_NAME, - driver_impl - ); -} - -#pragma endregion /* Driver registering */ - -#endif /* NBNET_IMPL */ diff --git a/net_drivers/webrtc.h b/net_drivers/webrtc.h deleted file mode 100644 index 88dab7d..0000000 --- a/net_drivers/webrtc.h +++ /dev/null @@ -1,475 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -/* - --- NBNET WEBRTC DRIVER --- - - WebRTC driver using a single unreliable data channel for the nbnet library. - - How to use: - - 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_WebRTC_Register in both your client and server code before calling NBN_GameClient_Start or NBN_GameServer_Start -*/ - -typedef struct NBN_WebRTC_Config -{ - bool enable_tls; - const char *cert_path; - const char *key_path; -} NBN_WebRTC_Config; - -void NBN_WebRTC_Register(NBN_WebRTC_Config config); - -#ifdef NBNET_IMPL - -#if !defined(EXTERN_C) - #if defined(__cplusplus) - #define NBN_EXTERN extern "C" - #else - #define NBN_EXTERN extern - #endif -#endif - -#include - -#define NBN_WEBRTC_DRIVER_ID 1 -#define NBN_WEBRTC_DRIVER_NAME "WebRTC" - -typedef struct -{ - uint32_t id; - NBN_Connection *conn; -} NBN_WebRTC_Peer; - -#pragma region Hashtable - -#define HTABLE_DEFAULT_INITIAL_CAPACITY 32 -#define HTABLE_LOAD_FACTOR_THRESHOLD 0.75 - -typedef struct -{ - uint32_t peer_id; - NBN_WebRTC_Peer *peer; - unsigned int slot; -} NBN_WebRTC_HTableEntry; - -typedef struct -{ - NBN_WebRTC_HTableEntry **internal_array; - unsigned int capacity; - unsigned int count; - float load_factor; -} NBN_WebRTC_HTable; - -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_Create(void); -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_CreateWithCapacity(unsigned int); -static void NBN_WebRTC_HTable_Destroy(NBN_WebRTC_HTable *); -static void NBN_WebRTC_HTable_Add(NBN_WebRTC_HTable *, uint32_t, NBN_WebRTC_Peer *); -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Get(NBN_WebRTC_HTable *, uint32_t); -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Remove(NBN_WebRTC_HTable *, uint32_t); -static void NBN_WebRTC_HTable_InsertEntry(NBN_WebRTC_HTable *, NBN_WebRTC_HTableEntry *); -static void NBN_WebRTC_HTable_RemoveEntry(NBN_WebRTC_HTable *, NBN_WebRTC_HTableEntry *); -static unsigned int NBN_WebRTC_HTable_FindFreeSlot(NBN_WebRTC_HTable *, NBN_WebRTC_HTableEntry *, bool *); -static NBN_WebRTC_HTableEntry *NBN_WebRTC_HTable_FindEntry(NBN_WebRTC_HTable *, uint32_t); -static void NBN_WebRTC_HTable_Grow(NBN_WebRTC_HTable *); - -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_Create(void) -{ - return NBN_WebRTC_HTable_CreateWithCapacity(HTABLE_DEFAULT_INITIAL_CAPACITY); -} - -static NBN_WebRTC_HTable *NBN_WebRTC_HTable_CreateWithCapacity(unsigned int capacity) -{ - NBN_WebRTC_HTable *htable = (NBN_WebRTC_HTable*)NBN_Allocator(sizeof(NBN_WebRTC_HTable)); - - htable->internal_array = (NBN_WebRTC_HTableEntry**)NBN_Allocator(sizeof(NBN_WebRTC_HTableEntry *) * capacity); - htable->capacity = capacity; - htable->count = 0; - htable->load_factor = 0; - - for (unsigned int i = 0; i < htable->capacity; i++) - htable->internal_array[i] = NULL; - - return htable; -} - -static void NBN_WebRTC_HTable_Destroy(NBN_WebRTC_HTable *htable) -{ - for (unsigned int i = 0; i < htable->capacity; i++) - { - NBN_WebRTC_HTableEntry *entry = htable->internal_array[i]; - - if (entry) - NBN_Deallocator(entry); - } - - NBN_Deallocator(htable->internal_array); - NBN_Deallocator(htable); -} - -static void NBN_WebRTC_HTable_Add(NBN_WebRTC_HTable *htable, uint32_t peer_id, NBN_WebRTC_Peer *peer) -{ - NBN_WebRTC_HTableEntry *entry = (NBN_WebRTC_HTableEntry*)NBN_Allocator(sizeof(NBN_WebRTC_HTableEntry)); - - entry->peer_id = peer_id; - entry->peer = peer; - - NBN_WebRTC_HTable_InsertEntry(htable, entry); - - if (htable->load_factor >= HTABLE_LOAD_FACTOR_THRESHOLD) - NBN_WebRTC_HTable_Grow(htable); -} - -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Get(NBN_WebRTC_HTable *htable, uint32_t peer_id) -{ - NBN_WebRTC_HTableEntry *entry = NBN_WebRTC_HTable_FindEntry(htable, peer_id); - - return entry ? entry->peer : NULL; -} - -static NBN_WebRTC_Peer *NBN_WebRTC_HTable_Remove(NBN_WebRTC_HTable *htable, uint32_t peer_id) -{ - NBN_WebRTC_HTableEntry *entry = NBN_WebRTC_HTable_FindEntry(htable, peer_id); - - if (entry) - { - NBN_WebRTC_Peer *peer = entry->peer; - - NBN_WebRTC_HTable_RemoveEntry(htable, entry); - - return peer; - } - - return NULL; -} - -static void NBN_WebRTC_HTable_InsertEntry(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry) -{ - bool use_existing_slot = false; - unsigned int slot = NBN_WebRTC_HTable_FindFreeSlot(htable, entry, &use_existing_slot); - - entry->slot = slot; - htable->internal_array[slot] = entry; - - if (!use_existing_slot) - { - htable->count++; - htable->load_factor = (float)htable->count / htable->capacity; - } -} - -static void NBN_WebRTC_HTable_RemoveEntry(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry) -{ - htable->internal_array[entry->slot] = NULL; - - NBN_Deallocator(entry); - - htable->count--; - htable->load_factor = htable->count / htable->capacity; -} - -static unsigned int NBN_WebRTC_HTable_FindFreeSlot(NBN_WebRTC_HTable *htable, NBN_WebRTC_HTableEntry *entry, bool *use_existing_slot) -{ - unsigned long hash = entry->peer_id; - unsigned int slot; - - // quadratic probing - - NBN_WebRTC_HTableEntry *current_entry; - unsigned int i = 0; - - do - { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - i++; - } while (current_entry != NULL && current_entry->peer_id != entry->peer_id); - - if (current_entry != NULL) // it means the current entry as the same key as the inserted entry - { - *use_existing_slot = true; - - NBN_Deallocator(current_entry); - } - - return slot; -} - -static NBN_WebRTC_HTableEntry *NBN_WebRTC_HTable_FindEntry(NBN_WebRTC_HTable *htable, uint32_t peer_id) -{ - unsigned long hash = peer_id; - unsigned int slot; - - //quadratic probing - - NBN_WebRTC_HTableEntry *current_entry; - unsigned int i = 0; - - do - { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - if (current_entry != NULL && current_entry->peer_id == peer_id) - { - return current_entry; - } - - i++; - } while (i < htable->capacity); - - return NULL; -} - -static void NBN_WebRTC_HTable_Grow(NBN_WebRTC_HTable *htable) -{ - unsigned int old_capacity = htable->capacity; - unsigned int new_capacity = old_capacity * 2; - NBN_WebRTC_HTableEntry** old_internal_array = htable->internal_array; - NBN_WebRTC_HTableEntry** new_internal_array = (NBN_WebRTC_HTableEntry**)NBN_Allocator(sizeof(NBN_WebRTC_HTableEntry*) * new_capacity); - - for (unsigned int i = 0; i < new_capacity; i++) - { - new_internal_array[i] = NULL; - } - - htable->internal_array = new_internal_array; - htable->capacity = new_capacity; - htable->count = 0; - htable->load_factor = 0; - - // rehash - - for (unsigned int i = 0; i < old_capacity; i++) - { - if (old_internal_array[i]) - NBN_WebRTC_HTable_InsertEntry(htable, old_internal_array[i]); - } - - NBN_Deallocator(old_internal_array); -} - -#pragma endregion // Hashtable - -#pragma region Game server - -/* --- JS API --- */ - -NBN_EXTERN void __js_game_server_init(uint32_t, bool, const char *, const char *); -NBN_EXTERN int __js_game_server_start(uint16_t); -NBN_EXTERN int __js_game_server_dequeue_packet(uint32_t *, uint8_t *); -NBN_EXTERN int __js_game_server_send_packet_to(uint8_t *, unsigned int, uint32_t); -NBN_EXTERN void __js_game_server_close_client_peer(unsigned int); -NBN_EXTERN void __js_game_server_stop(void); - -/* --- Driver implementation --- */ - -typedef struct NBN_WebRTC_Server -{ - NBN_WebRTC_HTable *peers; - uint8_t packet_buffer[NBN_PACKET_MAX_SIZE]; - uint32_t protocol_id; -} NBN_WebRTC_Server; - -static NBN_WebRTC_Server nbn_wrtc_serv = {NULL, {0}, 0}; -static NBN_WebRTC_Config nbn_wrtc_cfg; - -static int NBN_WebRTC_ServStart(uint32_t protocol_id, uint16_t port) -{ - __js_game_server_init(protocol_id, nbn_wrtc_cfg.enable_tls, nbn_wrtc_cfg.key_path, nbn_wrtc_cfg.cert_path); - - if (__js_game_server_start(port) < 0) - return -1; - - nbn_wrtc_serv.peers = NBN_WebRTC_HTable_Create(); - nbn_wrtc_serv.protocol_id = protocol_id; - - return 0; -} - -static void NBN_WebRTC_ServStop(void) -{ - __js_game_server_stop(); - NBN_WebRTC_HTable_Destroy(nbn_wrtc_serv.peers); -} - -static int NBN_WebRTC_ServRecvPackets(void) -{ - uint32_t peer_id; - unsigned int len; - - while ((len = __js_game_server_dequeue_packet(&peer_id, (uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) - { - NBN_Packet packet; - - NBN_WebRTC_Peer *peer = NBN_WebRTC_HTable_Get(nbn_wrtc_serv.peers, peer_id); - - if (peer == NULL) - { - if (GameServer_GetClientCount() >= NBN_MAX_CLIENTS) - continue; - - NBN_LogTrace("Peer %d has connected", peer_id); - - peer = (NBN_WebRTC_Peer *)NBN_Allocator(sizeof(NBN_WebRTC_Peer)); - - peer->id = peer_id; - peer->conn = NBN_GameServer_CreateClientConnection( - NBN_WEBRTC_DRIVER_ID, - peer, - nbn_wrtc_serv.protocol_id, - peer_id); - - NBN_WebRTC_HTable_Add(nbn_wrtc_serv.peers, peer_id, peer); - - NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, peer->conn); - } - - if (NBN_Packet_InitRead(&packet, peer->conn, nbn_wrtc_serv.packet_buffer, len) < 0) - continue; - - packet.sender = peer->conn; - - NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet); - } - - return 0; -} - -static void NBN_WebRTC_ServRemoveClientConnection(NBN_Connection *conn) -{ - assert(conn != NULL); - - __js_game_server_close_client_peer(conn->id); - - NBN_WebRTC_Peer *peer = NBN_WebRTC_HTable_Remove(nbn_wrtc_serv.peers, ((NBN_WebRTC_Peer *)conn->driver_data)->id); - - if (peer) - { - NBN_LogDebug("Destroyed peer %d", peer->id); - - NBN_Deallocator(peer); - } -} - -static int NBN_WebRTC_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) -{ - return __js_game_server_send_packet_to(packet->buffer, packet->size, conn->id); -} - -#pragma endregion /* Game server */ - -#pragma region Game client - -/* --- JS API --- */ - -NBN_EXTERN void __js_game_client_init(uint32_t, bool); -NBN_EXTERN int __js_game_client_start(const char *, uint16_t); -NBN_EXTERN int __js_game_client_dequeue_packet(uint8_t *); -NBN_EXTERN int __js_game_client_send_packet(uint8_t *, unsigned int); -NBN_EXTERN void __js_game_client_close(void); - -/* --- Driver implementation --- */ - -typedef struct NBN_WebRTC_Client -{ - NBN_Connection *server_conn; -} NBN_WebRTC_Client; - -static NBN_WebRTC_Client nbn_wrtc_cli = {NULL}; - -static int NBN_WebRTC_CliStart(uint32_t protocol_id, const char *host, uint16_t port) -{ - __js_game_client_init(protocol_id, nbn_wrtc_cfg.enable_tls); - - nbn_wrtc_cli.server_conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_DRIVER_ID, NULL, protocol_id); - - int res; - - if ((res = __js_game_client_start(host, port)) < 0) - return -1; - - return 0; -} - -static void NBN_WebRTC_CliStop(void) -{ - __js_game_client_close(); -} - -static int NBN_WebRTC_CliRecvPackets(void) -{ - unsigned int len; - - while ((len = __js_game_client_dequeue_packet((uint8_t *)nbn_wrtc_serv.packet_buffer)) > 0) - { - NBN_Packet packet; - - if (NBN_Packet_InitRead(&packet, nbn_wrtc_cli.server_conn, nbn_wrtc_serv.packet_buffer, len) < 0) - continue; - - NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); - } - - return 0; -} - -static int NBN_WebRTC_CliSendPacket(NBN_Packet *packet) -{ - return __js_game_client_send_packet(packet->buffer, packet->size); -} - -#pragma endregion /* Game client */ - -#pragma region Driver registering - -void NBN_WebRTC_Register(NBN_WebRTC_Config config) -{ - NBN_DriverImplementation driver_impl = { - // Client implementation - NBN_WebRTC_CliStart, - NBN_WebRTC_CliStop, - NBN_WebRTC_CliRecvPackets, - NBN_WebRTC_CliSendPacket, - - // Server implementation - NBN_WebRTC_ServStart, - NBN_WebRTC_ServStop, - NBN_WebRTC_ServRecvPackets, - NBN_WebRTC_ServSendPacketTo, - NBN_WebRTC_ServRemoveClientConnection - }; - - nbn_wrtc_cfg = config; - - NBN_Driver_Register( - NBN_WEBRTC_DRIVER_ID, - NBN_WEBRTC_DRIVER_NAME, - driver_impl - ); -} - -#pragma endregion /* Driver registering */ - -#endif /* NBNET_IMPL */ diff --git a/net_drivers/webrtc/package-lock.json b/net_drivers/webrtc/package-lock.json deleted file mode 100644 index 5bc5b8e..0000000 --- a/net_drivers/webrtc/package-lock.json +++ /dev/null @@ -1,973 +0,0 @@ -{ - "name": "nbnet", - "version": "1.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "nbnet", - "version": "1.0", - "license": "MIT", - "dependencies": { - "@roamhq/wrtc": "^0.8.0", - "websocket": "^1.0.31", - "winston": "^3.2.1" - } - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@roamhq/wrtc": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc/-/wrtc-0.8.0.tgz", - "integrity": "sha512-C0V/nqc4/2xzORI5qa4mIeN/8UO3ywN1kInrJ9u6GljFx0D18JMUJEqe8yYHa61RrEeoWN3PKdW++k8TocSx/A==", - "optionalDependencies": { - "@roamhq/wrtc-darwin-arm64": "0.8.0", - "@roamhq/wrtc-darwin-x64": "0.8.0", - "@roamhq/wrtc-linux-arm64": "0.8.1", - "@roamhq/wrtc-linux-x64": "0.8.1", - "@roamhq/wrtc-win32-x64": "0.8.0", - "domexception": "^4.0.0" - } - }, - "node_modules/@roamhq/wrtc-darwin-arm64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-arm64/-/wrtc-darwin-arm64-0.8.0.tgz", - "integrity": "sha512-OtV2KWO7zOG3L8TF3KCt9aucynVCD/ww2xeXXgg+FLkya3ca0uzehN8EQJ3BL4tkInksbFJ2ssyu9cehfJ3ZuA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@roamhq/wrtc-darwin-x64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-x64/-/wrtc-darwin-x64-0.8.0.tgz", - "integrity": "sha512-VY7Vzt/SDDDCpW//h8GW9bOZrOr8gWXPZVD9473ypl4jyBIoO57yyLbHzd1G0vBUkS6szsHlQCz1WwpI30YL+g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@roamhq/wrtc-linux-arm64": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-arm64/-/wrtc-linux-arm64-0.8.1.tgz", - "integrity": "sha512-FBJLLazlWkGQUXaokC/rTbrUQbb0CNFYry52fZGstufrGLTWu+g4HcwXdVvxh1tnVtVMvkQGk+mlOL52sCxw0A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@roamhq/wrtc-linux-x64": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-x64/-/wrtc-linux-x64-0.8.1.tgz", - "integrity": "sha512-I9oWG7b4uvWO1IOR/aF34n+ID6TKVuSs0jd19h5KdhfRtw7FFh9xxuwN9rONPxLVa6fS0q+MCZgAf8Scz89L8Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@roamhq/wrtc-win32-x64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-win32-x64/-/wrtc-win32-x64-0.8.0.tgz", - "integrity": "sha512-R2fxl41BLWPiP4eaTHGLzbbVvRjx1mV/OsgINCvawO7Hwz5Zx9I45+Fhrw3hd4n5amIeSG9VIF7Kz8eeTFXTGQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" - }, - "node_modules/bufferutil": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", - "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", - "optional": true, - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esniff/node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dependencies": { - "type": "^2.0.0" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/logform": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", - "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", - "dependencies": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/logform/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "engines": { - "node": "*" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "optional": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/websocket": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", - "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.63", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/winston": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.1.tgz", - "integrity": "sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.6.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", - "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", - "dependencies": { - "logform": "^2.6.1", - "readable-stream": "^3.6.2", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", - "engines": { - "node": ">=0.10.32" - } - } - }, - "dependencies": { - "@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" - }, - "@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "@roamhq/wrtc": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc/-/wrtc-0.8.0.tgz", - "integrity": "sha512-C0V/nqc4/2xzORI5qa4mIeN/8UO3ywN1kInrJ9u6GljFx0D18JMUJEqe8yYHa61RrEeoWN3PKdW++k8TocSx/A==", - "requires": { - "@roamhq/wrtc-darwin-arm64": "0.8.0", - "@roamhq/wrtc-darwin-x64": "0.8.0", - "@roamhq/wrtc-linux-arm64": "0.8.1", - "@roamhq/wrtc-linux-x64": "0.8.1", - "@roamhq/wrtc-win32-x64": "0.8.0", - "domexception": "^4.0.0" - } - }, - "@roamhq/wrtc-darwin-arm64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-arm64/-/wrtc-darwin-arm64-0.8.0.tgz", - "integrity": "sha512-OtV2KWO7zOG3L8TF3KCt9aucynVCD/ww2xeXXgg+FLkya3ca0uzehN8EQJ3BL4tkInksbFJ2ssyu9cehfJ3ZuA==", - "optional": true - }, - "@roamhq/wrtc-darwin-x64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-x64/-/wrtc-darwin-x64-0.8.0.tgz", - "integrity": "sha512-VY7Vzt/SDDDCpW//h8GW9bOZrOr8gWXPZVD9473ypl4jyBIoO57yyLbHzd1G0vBUkS6szsHlQCz1WwpI30YL+g==", - "optional": true - }, - "@roamhq/wrtc-linux-arm64": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-arm64/-/wrtc-linux-arm64-0.8.1.tgz", - "integrity": "sha512-FBJLLazlWkGQUXaokC/rTbrUQbb0CNFYry52fZGstufrGLTWu+g4HcwXdVvxh1tnVtVMvkQGk+mlOL52sCxw0A==", - "optional": true - }, - "@roamhq/wrtc-linux-x64": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-x64/-/wrtc-linux-x64-0.8.1.tgz", - "integrity": "sha512-I9oWG7b4uvWO1IOR/aF34n+ID6TKVuSs0jd19h5KdhfRtw7FFh9xxuwN9rONPxLVa6fS0q+MCZgAf8Scz89L8Q==", - "optional": true - }, - "@roamhq/wrtc-win32-x64": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@roamhq/wrtc-win32-x64/-/wrtc-win32-x64-0.8.0.tgz", - "integrity": "sha512-R2fxl41BLWPiP4eaTHGLzbbVvRjx1mV/OsgINCvawO7Hwz5Zx9I45+Fhrw3hd4n5amIeSG9VIF7Kz8eeTFXTGQ==", - "optional": true - }, - "@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" - }, - "async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" - }, - "bufferutil": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", - "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "requires": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "requires": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "optional": true, - "requires": { - "webidl-conversions": "^7.0.0" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "dependencies": { - "type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" - } - } - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", - "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", - "requires": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==" - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "optional": true - }, - "websocket": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", - "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.63", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "winston": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.1.tgz", - "integrity": "sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==", - "requires": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.6.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" - } - }, - "winston-transport": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", - "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", - "requires": { - "logform": "^2.6.1", - "readable-stream": "^3.6.2", - "triple-beam": "^1.3.0" - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==" - } - } -} diff --git a/net_drivers/webrtc_c.h b/net_drivers/webrtc_c.h deleted file mode 100644 index ede30c4..0000000 --- a/net_drivers/webrtc_c.h +++ /dev/null @@ -1,980 +0,0 @@ -/* - -Copyright (C) 2024 BIAGINI Nathan - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -/* - --- NBNET NATIVE WEBRTC DRIVER --- - - WebRTC driver for the nbnet library, using a single unreliable data channel. As opposed to the other emscripten/JS - based WebRTC driver (webrtc.h), this one is fully written in C99 and can be compiled as a native application. - - Dependencies: - - 1. libdatachannel (https://github.com/paullouisageneau/libdatachannel) - 2. json.h (https://github.com/sheredom/json.h) - - How to use: - - 1. Include this header *once* after the nbnet header in the same file where you defined the NBNET_IMPL macro - 2. Call NBN_WebRTC_C_Register in both your client and server code before calling NBN_GameClient_Start or NBN_GameServer_Start -*/ - -#include -#include -#include -#include "json.h" - -#define NBN_WEBRTC_C_DRIVER_ID 2 -#define NBN_WEBRTC_C_DRIVER_NAME "WebRTC_C" - -typedef struct NBN_WebRTC_C_Config -{ - bool enable_tls; - const char *cert_path; - const char *key_path; - const char *passphrase; - const char **ice_servers; - unsigned int ice_servers_count; - rtcLogLevel log_level; -} NBN_WebRTC_C_Config; - -static NBN_WebRTC_C_Config nbn_wrtc_c_cfg; - -void NBN_WebRTC_C_Register(NBN_WebRTC_C_Config config); -void NBN_WebRTC_C_Unregister(void); - -#ifdef NBNET_IMPL - -typedef struct -{ - int id; - int channel_id; - int ws; - NBN_Connection *conn; -} NBN_WebRTC_C_Peer; - -static void NBN_WebRTC_C_DestroyPeer(NBN_WebRTC_C_Peer *peer); - -#pragma region Hashtable - -#define HTABLE_DEFAULT_INITIAL_CAPACITY 32 -#define HTABLE_LOAD_FACTOR_THRESHOLD 0.75 - -typedef struct -{ - int peer_id; - NBN_WebRTC_C_Peer *peer; - unsigned int slot; -} NBN_WebRTC_C_HTableEntry; - -typedef struct -{ - NBN_WebRTC_C_HTableEntry **internal_array; - unsigned int capacity; - unsigned int count; - float load_factor; -} NBN_WebRTC_C_HTable; - -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_Create(void); -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_CreateWithCapacity(unsigned int); -static void NBN_WebRTC_C_HTable_Destroy(NBN_WebRTC_C_HTable *); -static void NBN_WebRTC_C_HTable_Add(NBN_WebRTC_C_HTable *, int, NBN_WebRTC_C_Peer *); -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Get(NBN_WebRTC_C_HTable *, int); -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Remove(NBN_WebRTC_C_HTable *, int); -static void NBN_WebRTC_C_HTable_InsertEntry(NBN_WebRTC_C_HTable *, NBN_WebRTC_C_HTableEntry *); -static void NBN_WebRTC_C_HTable_RemoveEntry(NBN_WebRTC_C_HTable *, NBN_WebRTC_C_HTableEntry *); -static unsigned int NBN_WebRTC_C_HTable_FindFreeSlot(NBN_WebRTC_C_HTable *, NBN_WebRTC_C_HTableEntry *, bool *); -static NBN_WebRTC_C_HTableEntry *NBN_WebRTC_C_HTable_FindEntry(NBN_WebRTC_C_HTable *, int); -static void NBN_WebRTC_C_HTable_Grow(NBN_WebRTC_C_HTable *); - -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_Create(void) -{ - return NBN_WebRTC_C_HTable_CreateWithCapacity(HTABLE_DEFAULT_INITIAL_CAPACITY); -} - -static NBN_WebRTC_C_HTable *NBN_WebRTC_C_HTable_CreateWithCapacity(unsigned int capacity) -{ - NBN_WebRTC_C_HTable *htable = (NBN_WebRTC_C_HTable*)NBN_Allocator(sizeof(NBN_WebRTC_C_HTable)); - - htable->internal_array = (NBN_WebRTC_C_HTableEntry**)NBN_Allocator(sizeof(NBN_WebRTC_C_HTableEntry *) * capacity); - htable->capacity = capacity; - htable->count = 0; - htable->load_factor = 0; - - for (unsigned int i = 0; i < htable->capacity; i++) - { - htable->internal_array[i] = NULL; - } - - return htable; -} - -static void NBN_WebRTC_C_HTable_Destroy(NBN_WebRTC_C_HTable *htable) -{ - for (unsigned int i = 0; i < htable->capacity; i++) - { - NBN_WebRTC_C_HTableEntry *entry = htable->internal_array[i]; - - if (entry) - { - NBN_WebRTC_C_DestroyPeer(entry->peer); - } - } - - NBN_Deallocator(htable->internal_array); - NBN_Deallocator(htable); -} - -static void NBN_WebRTC_C_HTable_Add(NBN_WebRTC_C_HTable *htable, int peer_id, NBN_WebRTC_C_Peer *peer) -{ - NBN_WebRTC_C_HTableEntry *entry = (NBN_WebRTC_C_HTableEntry*)NBN_Allocator(sizeof(NBN_WebRTC_C_HTableEntry)); - - entry->peer_id = peer_id; - entry->peer = peer; - - NBN_WebRTC_C_HTable_InsertEntry(htable, entry); - - if (htable->load_factor >= HTABLE_LOAD_FACTOR_THRESHOLD) - NBN_WebRTC_C_HTable_Grow(htable); -} - -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Get(NBN_WebRTC_C_HTable *htable, int peer_id) -{ - NBN_WebRTC_C_HTableEntry *entry = NBN_WebRTC_C_HTable_FindEntry(htable, peer_id); - - return entry ? entry->peer : NULL; -} - -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_HTable_Remove(NBN_WebRTC_C_HTable *htable, int peer_id) -{ - NBN_WebRTC_C_HTableEntry *entry = NBN_WebRTC_C_HTable_FindEntry(htable, peer_id); - - if (entry) - { - NBN_WebRTC_C_Peer *peer = entry->peer; - - NBN_WebRTC_C_HTable_RemoveEntry(htable, entry); - - return peer; - } - - return NULL; -} - -static void NBN_WebRTC_C_HTable_InsertEntry(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry) -{ - bool use_existing_slot = false; - unsigned int slot = NBN_WebRTC_C_HTable_FindFreeSlot(htable, entry, &use_existing_slot); - - entry->slot = slot; - htable->internal_array[slot] = entry; - - if (!use_existing_slot) - { - htable->count++; - htable->load_factor = (float)htable->count / htable->capacity; - } -} - -static void NBN_WebRTC_C_HTable_RemoveEntry(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry) -{ - htable->internal_array[entry->slot] = NULL; - - NBN_Deallocator(entry); - - htable->count--; - htable->load_factor = htable->count / htable->capacity; -} - -static unsigned int NBN_WebRTC_C_HTable_FindFreeSlot(NBN_WebRTC_C_HTable *htable, NBN_WebRTC_C_HTableEntry *entry, bool *use_existing_slot) -{ - unsigned long hash = entry->peer_id; - unsigned int slot; - - // quadratic probing - - NBN_WebRTC_C_HTableEntry *current_entry; - unsigned int i = 0; - - do - { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - i++; - } while (current_entry != NULL && current_entry->peer_id != entry->peer_id); - - if (current_entry != NULL) // it means the current entry as the same key as the inserted entry - { - *use_existing_slot = true; - - NBN_Deallocator(current_entry); - } - - return slot; -} - -static NBN_WebRTC_C_HTableEntry *NBN_WebRTC_C_HTable_FindEntry(NBN_WebRTC_C_HTable *htable, int peer_id) -{ - unsigned long hash = peer_id; - unsigned int slot; - - //quadratic probing - - NBN_WebRTC_C_HTableEntry *current_entry; - unsigned int i = 0; - - do - { - slot = (hash + (int)pow(i, 2)) % htable->capacity; - current_entry = htable->internal_array[slot]; - - if (current_entry != NULL && current_entry->peer_id == peer_id) - { - return current_entry; - } - - i++; - } while (i < htable->capacity); - - return NULL; -} - -static void NBN_WebRTC_C_HTable_Grow(NBN_WebRTC_C_HTable *htable) -{ - unsigned int old_capacity = htable->capacity; - unsigned int new_capacity = old_capacity * 2; - NBN_WebRTC_C_HTableEntry** old_internal_array = htable->internal_array; - NBN_WebRTC_C_HTableEntry** new_internal_array = (NBN_WebRTC_C_HTableEntry**)NBN_Allocator(sizeof(NBN_WebRTC_C_HTableEntry*) * new_capacity); - - for (unsigned int i = 0; i < new_capacity; i++) - { - new_internal_array[i] = NULL; - } - - htable->internal_array = new_internal_array; - htable->capacity = new_capacity; - htable->count = 0; - htable->load_factor = 0; - - // rehash - - for (unsigned int i = 0; i < old_capacity; i++) - { - if (old_internal_array[i]) - NBN_WebRTC_C_HTable_InsertEntry(htable, old_internal_array[i]); - } - - NBN_Deallocator(old_internal_array); -} - -#pragma endregion // Hashtable - -#pragma region String utils - -// IMPORTANT: res needs to be pre allocated and big enough to old the resulting string -static void NBN_WebRTC_C_StringReplaceAll(char *res, const char *str, const char *a, const char *b) -{ - char *substr = (char*)strstr(str, a); - size_t len_a = strlen(a); - size_t len_b = strlen(b); - - if (substr) - { - int pos = substr - str; - - strncpy(res, str, pos); - strncpy(res + pos, b, len_b); - - NBN_WebRTC_C_StringReplaceAll(res + pos + len_b, str + pos + len_a, a, b); - } - else - { - strncpy(res, str, strlen(str) + 1); - } -} - -#pragma endregion /* String utils */ - -#pragma region WebRTC common - -static char *NBN_WebRTC_C_ParseSignalingMessage(const char *msg, size_t msg_len, const char *type) -{ - char *sdp = NULL; - struct json_value_s* root = json_parse(msg, msg_len); // this has to be freed - - struct json_object_s* object = (struct json_object_s*)root->payload; - struct json_object_element_s *curr = object->start; - - if (root->type != json_type_object) - { - NBN_LogDebug("Received an invalid signaling message: %s", msg); - goto leave_free_root; - } - - while (curr != NULL) - { - if (strncmp(curr->name->string, "type", 4) == 0) - { - struct json_string_s *str = json_value_as_string(curr->value); - - if (strncmp(str->string, type, strlen(type))) - { - // unexpected type - NBN_LogDebug("Received a signaling message with an unexpected type: %s (expected: %s)", str->string, type); - sdp = NULL; - goto leave_free_root; - } - } - else if (strncmp(curr->name->string, "sdp", 3) == 0) - { - struct json_string_s *str = json_value_as_string(curr->value); - - if (str) - { - // strdup equivalent using NBN_Allocator, make sure this get freed - size_t len = strlen(str->string); - - sdp = (char*)NBN_Allocator(len + 1); - memcpy(sdp, str->string, len + 1); - } - } - - curr = curr->next; - } - -leave_free_root: - free(root); - - return sdp; -} - -static char *NBN_WebRTC_C_EscapeSDP(const char *sdp) -{ - size_t len = strlen(sdp) * 2; // TODO: kinda lame way of making sure it's going to be big enough, find a better way - char *escaped_sdp = (char*)NBN_Allocator(len); - - NBN_WebRTC_C_StringReplaceAll(escaped_sdp, sdp, "\r\n", "\\r\\n"); - - return escaped_sdp; -} - -static void NBN_WebRTC_C_Log(rtcLogLevel level, const char *msg) -{ - switch (level) - { - case RTC_LOG_FATAL: - case RTC_LOG_ERROR: - NBN_LogError("%s",msg); - break; - - case RTC_LOG_WARNING: - NBN_LogWarning("%s",msg); - break; - - case RTC_LOG_INFO: - NBN_LogInfo("%s",msg); - break; - - case RTC_LOG_DEBUG: - NBN_LogDebug("%s",msg); - break; - - case RTC_LOG_VERBOSE: - NBN_LogTrace("%s",msg); - break; - - case RTC_LOG_NONE: - break; - } -} - -static void NBN_WebRTC_C_OnWsError(int ws, const char *err_msg, void *user_ptr) -{ - (void)user_ptr; - - NBN_LogError("Error on WS %d: %s", ws, err_msg); -} - -static NBN_WebRTC_C_Peer *NBN_WebRTC_C_CreatePeer( - int ws, - rtcDescriptionCallbackFunc on_rtc_description_cb, - rtcStateChangeCallbackFunc on_state_change_cb) -{ - rtcConfiguration rtcCfg = { - .iceServers = nbn_wrtc_c_cfg.ice_servers, - .iceServersCount = (int)nbn_wrtc_c_cfg.ice_servers_count, - .disableAutoNegotiation = false - }; - int peer_id = rtcCreatePeerConnection(&rtcCfg); - - if (peer_id < 0) - { - NBN_LogError("Failed to create peer: %d", peer_id); - return NULL; - } - - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)NBN_Allocator(sizeof(NBN_WebRTC_C_Peer)); - - peer->id = peer_id; - peer->ws = ws; - peer->channel_id = -1; - - rtcSetUserPointer(peer_id, peer); - rtcSetUserPointer(ws, peer); - - int ret = rtcSetLocalDescriptionCallback(peer->id, on_rtc_description_cb); - - if (ret < 0) - { - NBN_LogError("Failed to register local description callback for peer %d: %d", peer->id, ret); - NBN_WebRTC_C_DestroyPeer(peer); - return NULL; - } - - ret = rtcSetStateChangeCallback(peer_id, on_state_change_cb); - - if (ret < 0) - { - NBN_LogError("Failed to register state change callback for peer %d: %d", peer_id, ret); - NBN_WebRTC_C_DestroyPeer(peer); - return NULL; - } - rtcDataChannelInit rtcDataChannel = { - .reliability = {.unordered = true, .unreliable = true, .maxPacketLifeTime = 1000, .maxRetransmits = 0}, - .negotiated = true, - .manualStream = true, - .stream = 0 - }; - int channel_id = rtcCreateDataChannelEx(peer_id, "unreliable", &rtcDataChannel); - - if (channel_id < 0) - { - NBN_LogError("Failed to create data channel for peer %d: %d", peer_id, channel_id); - NBN_WebRTC_C_DestroyPeer(peer); - return NULL; - } - - peer->channel_id = channel_id; - - NBN_LogDebug("Successfully created data channel for peer %d: %d", peer_id, channel_id); - - return peer; -} - -static void NBN_WebRTC_C_DestroyPeer(NBN_WebRTC_C_Peer *peer) -{ - NBN_LogDebug("Destroying peer %d", peer->id); - - if (peer->channel_id >= 0) - { - rtcDeleteDataChannel(peer->channel_id); - } - - rtcDeletePeerConnection(peer->id); - rtcDelete(peer->ws); - NBN_Deallocator(peer); -} - -static void NBN_WebRTC_C_ProcessLocalDescription(NBN_WebRTC_C_Peer *peer, const char *sdp, const char *type) -{ - char *escaped_sdp = NBN_WebRTC_C_EscapeSDP(sdp); - size_t signaling_json_size = snprintf(NULL, 0, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp) + 1; - - char* signaling_json = (char *) NBN_Allocator(signaling_json_size); - - snprintf(signaling_json, signaling_json_size, "{\"type\":\"%s\", \"sdp\":\"%s\"}", type, escaped_sdp); - NBN_LogDebug("Send signaling message of type %s to remote connection: %s", type, signaling_json); - - // pass -1 as the size (assume signaling_json to be a null-terminated string) - if (rtcSendMessage(peer->ws, signaling_json, -1) < 0) - { - NBN_WebRTC_C_DestroyPeer(peer); - } - NBN_Deallocator(signaling_json); - NBN_Deallocator(escaped_sdp); -} - -static void NBN_WebRTC_C_ProcessSignalingMessage(NBN_WebRTC_C_Peer *peer, int ws, const char *msg, int size, const char *type) -{ - // for some reason the size of the message is negative - // in libdatachannel documentation (https://github.com/paullouisageneau/libdatachannel/blob/master/DOC.md) there is mention of: - // size: if size >= 0, data is interpreted as a binary message of length size, otherwise it is interpreted as a null-terminated UTF-8 string. - // so I guess in this case msg is a null terminated string? I could not find more information about this so I decided to go with - // flipping the size to positive even though it feels weird, but it works so... ¯\_(ツ)_/¯ - - if (size < 0) size *= -1; - size -= 1; - - NBN_LogDebug("Received signaling message on WS %d (size: %d): %s", ws, size, msg); - - char *sdp = NBN_WebRTC_C_ParseSignalingMessage(msg, size, type); - - if (!sdp) - { - NBN_LogWarning("Failed to parse signaling data for WS %d", ws); - return; - } - - NBN_LogDebug("Successfully parsed signaling payload (sdp: %s)", sdp); - - int ret = rtcSetRemoteDescription(peer->id, sdp, type); - - if (ret < 0) - { - NBN_LogError("Failed to set remote description for peer %d (WS: %d): %d", peer->id, ws, ret); - rtcClose(ws); - } - - // IMPORTANT: not sure I can free this because it's passed to rtcSetRemoteDescription - NBN_Deallocator(sdp); -} - -#pragma endregion /* WebRTC common */ - -#pragma region Game server - -typedef struct NBN_WebRTC_C_Server -{ - int wsserver; - NBN_WebRTC_C_HTable *peers; - uint16_t ws_port; - uint32_t protocol_id; - char packet_buffer[NBN_PACKET_MAX_SIZE]; -} NBN_WebRTC_C_Server; - -static NBN_WebRTC_C_Server nbn_wrtc_c_serv = {0, NULL, 0, 0, {0}}; - -static void NBN_WebRTC_C_Serv_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) -{ - NBN_LogDebug("Processing local description of type '%s'", type); - - if (strncmp(type, "answer", strlen("answer")) != 0) - { - NBN_LogWarning("Ignoring local description of type '%s' (expected 'answer')", type); - return; - } - - NBN_WebRTC_C_ProcessLocalDescription((NBN_WebRTC_C_Peer *)user_ptr, sdp, "answer"); -} - -static void NBN_WebRTC_C_Serv_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) -{ - NBN_LogDebug("Peer %d state changed to %d", pc, state); - - if (state == RTC_CONNECTED) - { - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; - - NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_CONNECTED, peer->conn); - NBN_LogDebug("Peer %d is connected !", pc); - } -} - -static void NBN_WebRTC_C_Serv_OnWsOpen(int ws, void *user_ptr) -{ - NBN_LogDebug("WS %d is open", ws); - - NBN_WebRTC_C_Peer *peer = NBN_WebRTC_C_CreatePeer(ws, NBN_WebRTC_C_Serv_OnLocalDescription, NBN_WebRTC_C_Serv_OnPeerStateChanged); - - if (!peer) - { - NBN_LogError("Failed to create peer"); - return; - } - - peer->conn = NBN_GameServer_CreateClientConnection( - NBN_WEBRTC_C_DRIVER_ID, - peer, - nbn_wrtc_c_serv.protocol_id, - peer->id); - NBN_WebRTC_C_HTable_Add(nbn_wrtc_c_serv.peers, peer->id, peer); -} - -static void NBN_WebRTC_C_Serv_OnWsClosed(int ws, void *user_ptr) -{ - NBN_LogDebug("WS %d has closed", ws); - - if (user_ptr) - { - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; - - NBN_LogDebug("Closing WebRTC peer and channel (peer: %d, channel: %d)", peer->id, peer->channel_id); - - rtcClose(peer->channel_id); - rtcClosePeerConnection(peer->id); - } -} - -static void NBN_WebRTC_C_Serv_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) -{ - NBN_WebRTC_C_ProcessSignalingMessage((NBN_WebRTC_C_Peer *)user_ptr, ws, msg, size, "offer"); -} - -static void NBN_WebRTC_C_Serv_OnWsConnection(int wsserver, int ws, void *user_ptr) -{ - NBN_LogDebug("New WS connection %d (user_ptr: %p)", ws, user_ptr); - - rtcSetOpenCallback(ws, NBN_WebRTC_C_Serv_OnWsOpen); - rtcSetClosedCallback(ws, NBN_WebRTC_C_Serv_OnWsClosed); - rtcSetErrorCallback(ws, NBN_WebRTC_C_OnWsError); - rtcSetMessageCallback(ws, NBN_WebRTC_C_Serv_OnWsMessage); -} - -static int NBN_WebRTC_C_ServStart(uint32_t protocol_id, uint16_t port) -{ - nbn_wrtc_c_serv.ws_port = port; - nbn_wrtc_c_serv.peers = NBN_WebRTC_C_HTable_Create(); - nbn_wrtc_c_serv.protocol_id = protocol_id; - nbn_wrtc_c_serv.wsserver = -1; - - rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_C_Log); - rtcPreload(); - - rtcWsServerConfiguration cfg = { - .port = port, - .enableTls = nbn_wrtc_c_cfg.enable_tls, - .certificatePemFile = nbn_wrtc_c_cfg.cert_path, - .keyPemFile = nbn_wrtc_c_cfg.key_path, - .keyPemPass = nbn_wrtc_c_cfg.passphrase - }; - - int wsserver = rtcCreateWebSocketServer(&cfg, NBN_WebRTC_C_Serv_OnWsConnection); - - if (wsserver < 0) - { - NBN_LogError("Failed to start WS server (code: %d)", wsserver); - return NBN_ERROR; - } - - nbn_wrtc_c_serv.wsserver = wsserver; - - return 0; -} - -static void NBN_WebRTC_C_ServStop(void) -{ - NBN_WebRTC_C_HTable_Destroy(nbn_wrtc_c_serv.peers); - - if (nbn_wrtc_c_serv.wsserver >= 0) - { - rtcDeleteWebSocketServer(nbn_wrtc_c_serv.wsserver); - } - - rtcCleanup(); -} - -static int NBN_WebRTC_C_ServRecvPackets(void) -{ - const int buffer_size = sizeof(nbn_wrtc_c_serv.packet_buffer); - int size = buffer_size; - - for (unsigned int i = 0; i < nbn_wrtc_c_serv.peers->capacity; i++) - { - NBN_WebRTC_C_HTableEntry *entry = nbn_wrtc_c_serv.peers->internal_array[i]; - - if (entry) - { - while (rtcReceiveMessage(entry->peer->channel_id, nbn_wrtc_c_serv.packet_buffer, &size) == RTC_ERR_SUCCESS) - { - NBN_Packet packet; - - if (NBN_Packet_InitRead(&packet, entry->peer->conn, (uint8_t *)nbn_wrtc_c_serv.packet_buffer, size) < 0) - continue; - - packet.sender = entry->peer->conn; - NBN_Driver_RaiseEvent(NBN_DRIVER_SERV_CLIENT_PACKET_RECEIVED, &packet); - size = buffer_size; - } - } - } - - return 0; -} - -static void NBN_WebRTC_C_ServRemoveClientConnection(NBN_Connection *conn) -{ - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer*)conn->driver_data; - - NBN_WebRTC_C_HTable_Remove(nbn_wrtc_c_serv.peers, peer->id); - NBN_WebRTC_C_DestroyPeer(peer); -} - -static int NBN_WebRTC_C_ServSendPacketTo(NBN_Packet *packet, NBN_Connection *conn) -{ - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer*)conn->driver_data; - - if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) - { - NBN_LogError("rtcSendMessage failed for peer %d", peer->id); - } - - return 0; -} - -#pragma endregion /* Game server */ - -#pragma region Game client - -typedef struct NBN_WebRTC_C_Client -{ - uint32_t protocol_id; - bool is_connected; - NBN_WebRTC_C_Peer *peer; - char packet_buffer[NBN_PACKET_MAX_SIZE]; -} NBN_WebRTC_C_Client; - -static NBN_WebRTC_C_Client nbn_wrtc_c_cli = {0, false, NULL, {0}}; - -static void NBN_WebRTC_C_Cli_OnLocalDescription(int pc, const char *sdp, const char *type, void *user_ptr) -{ - NBN_LogDebug("Processing local description of type '%s'", type); - - if (strncmp(type, "offer", strlen("offer")) != 0) - { - NBN_LogWarning("Ignoring local description of type '%s' (expected 'offer')", type); - return; - } - - NBN_WebRTC_C_ProcessLocalDescription((NBN_WebRTC_C_Peer *)user_ptr, sdp, "offer"); -} - -static void NBN_WebRTC_C_Cli_OnPeerStateChanged(int pc, rtcState state, void *user_ptr) -{ - NBN_LogDebug("Server peer state changed to %d", pc, state); - - if (state == RTC_CONNECTED) - { - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; - - NBN_LogDebug("Server peer is connected !", pc); - nbn_wrtc_c_cli.is_connected = true; - } -} - -static void NBN_WebRTC_C_Cli_OnWsOpen(int ws, void *user_ptr) -{ - NBN_LogDebug("WS %d is open, creating peer...", ws); - - NBN_WebRTC_C_Peer *peer = NBN_WebRTC_C_CreatePeer(ws, NBN_WebRTC_C_Cli_OnLocalDescription, NBN_WebRTC_C_Cli_OnPeerStateChanged); - - if (!peer) - { - NBN_LogError("Failed to create peer"); - return; - } - - NBN_LogDebug("Successfully created peer: %d", peer->id); - - peer->conn = NBN_GameClient_CreateServerConnection(NBN_WEBRTC_C_DRIVER_ID, peer, nbn_wrtc_c_cli.protocol_id); - nbn_wrtc_c_cli.peer = peer; -} - -static void NBN_WebRTC_C_Cli_OnWsClosed(int ws, void *user_ptr) -{ - NBN_LogDebug("WS %d has closed", ws); - - if (user_ptr) - { - NBN_WebRTC_C_Peer *peer = (NBN_WebRTC_C_Peer *)user_ptr; - - NBN_LogDebug("Destroying server peer (peer: %d, channel: %d)", peer->id, peer->channel_id); - NBN_WebRTC_C_DestroyPeer(peer); - } -} - -static void NBN_WebRTC_C_Cli_OnWsMessage(int ws, const char *msg, int size, void *user_ptr) -{ - NBN_WebRTC_C_ProcessSignalingMessage((NBN_WebRTC_C_Peer *)user_ptr, ws, msg, size, "answer"); -} - -static int AttemptConnection(void) -{ - double waitTime = 1.0 / 3.0; // wait time between connection attempts in seconds - - int retries = 9; // approximatively 3 seconds to get connected - - struct timespec rqtp; - - rqtp.tv_sec = 0; - rqtp.tv_nsec = waitTime * 1e9; - - while (true) - { - #if defined(_WIN32) || defined(_WIN64) - Sleep(waitTime * 1000); - #else - if (nanosleep(&rqtp, NULL) < 0) - { - NBN_LogError("nanosleep failed"); - return NBN_ERROR; - } - #endif - if (--retries <= 0 || nbn_wrtc_c_cli.is_connected) - { - break; - } - } - - if (!nbn_wrtc_c_cli.is_connected) - { - NBN_LogError("Failed to connect"); - - return NBN_ERROR; - } - - return 0; -} - -static int NBN_WebRTC_C_CliStart(uint32_t protocol_id, const char *host, uint16_t port) -{ - rtcInitLogger(nbn_wrtc_c_cfg.log_level, NBN_WebRTC_C_Log); - rtcPreload(); - - char ws_addr[256] = {0}; - - snprintf(ws_addr, sizeof(ws_addr), "ws://%s:%d", host, port); - - int cli_ws; - - if ((cli_ws = rtcCreateWebSocket(ws_addr)) < 0) - { - NBN_LogError("Failed to create websocket"); - return NBN_ERROR; - } - - nbn_wrtc_c_cli.protocol_id = protocol_id; - - NBN_LogDebug("Successfully created client WS: %d", cli_ws); - - rtcSetOpenCallback(cli_ws, NBN_WebRTC_C_Cli_OnWsOpen); - rtcSetClosedCallback(cli_ws, NBN_WebRTC_C_Cli_OnWsClosed); - rtcSetErrorCallback(cli_ws, NBN_WebRTC_C_OnWsError); - rtcSetMessageCallback(cli_ws, NBN_WebRTC_C_Cli_OnWsMessage); - - return AttemptConnection(); -} - -static void NBN_WebRTC_C_CliStop(void) -{ - NBN_WebRTC_C_Peer *peer = nbn_wrtc_c_cli.peer; - - if (peer) - { - NBN_WebRTC_C_DestroyPeer(peer); - } - - nbn_wrtc_c_cli.is_connected = false; - rtcCleanup(); -} - -static int NBN_WebRTC_C_CliRecvPackets(void) -{ - const int buffer_size = sizeof(nbn_wrtc_c_cli.packet_buffer); - int size = buffer_size; - NBN_WebRTC_C_Peer *peer = nbn_wrtc_c_cli.peer; - - while (rtcReceiveMessage(peer->channel_id, nbn_wrtc_c_cli.packet_buffer, &size) == RTC_ERR_SUCCESS) - { - NBN_Packet packet; - - if (NBN_Packet_InitRead(&packet, peer->conn, (uint8_t *)nbn_wrtc_c_cli.packet_buffer, size) < 0) - continue; - - packet.sender = peer->conn; - NBN_Driver_RaiseEvent(NBN_DRIVER_CLI_PACKET_RECEIVED, &packet); - size = buffer_size; - } - - return 0; -} - -static int NBN_WebRTC_C_CliSendPacket(NBN_Packet *packet) -{ - NBN_WebRTC_C_Peer *peer = nbn_wrtc_c_cli.peer; - - if (rtcSendMessage(peer->channel_id, (char *)packet->buffer, packet->size) < 0) - { - NBN_LogError("rtcSendMessage failed for peer %d", peer->id); - return NBN_ERROR; - } - - return 0; -} - -#pragma endregion /* Game client */ - -void NBN_WebRTC_C_Register(NBN_WebRTC_C_Config config) -{ - const char **ice_servers = (const char**)NBN_Allocator(sizeof(char *) * config.ice_servers_count); - - for (unsigned int i = 0; i < config.ice_servers_count; i++) - { - // strdup equivalent using NBN_Allocator, make sure this get freed - const char *str = config.ice_servers[i]; - size_t len = strlen(str); - char *dup_str = (char*)NBN_Allocator(len + 1); - - memcpy(dup_str, str, len + 1); - ice_servers[i] = dup_str; - } - - nbn_wrtc_c_cfg.ice_servers = ice_servers; - nbn_wrtc_c_cfg.ice_servers_count = config.ice_servers_count; - nbn_wrtc_c_cfg.enable_tls = config.enable_tls; - nbn_wrtc_c_cfg.log_level = config.log_level; - - if (config.enable_tls) - { - nbn_wrtc_c_cfg.cert_path = config.cert_path; - nbn_wrtc_c_cfg.key_path = config.key_path; - nbn_wrtc_c_cfg.passphrase = config.passphrase; - } - - NBN_DriverImplementation driver_impl = { - // Client implementation - NBN_WebRTC_C_CliStart, - NBN_WebRTC_C_CliStop, - NBN_WebRTC_C_CliRecvPackets, - NBN_WebRTC_C_CliSendPacket, - - // Server implementation - NBN_WebRTC_C_ServStart, - NBN_WebRTC_C_ServStop, - NBN_WebRTC_C_ServRecvPackets, - NBN_WebRTC_C_ServSendPacketTo, - NBN_WebRTC_C_ServRemoveClientConnection - }; - - NBN_Driver_Register(NBN_WEBRTC_C_DRIVER_ID, NBN_WEBRTC_C_DRIVER_NAME, driver_impl); -} - -void NBN_WebRTC_C_Unregister(void) -{ - for (unsigned int i = 0; i < nbn_wrtc_c_cfg.ice_servers_count; i++) - { - NBN_Deallocator((void *)nbn_wrtc_c_cfg.ice_servers[i]); - } - - NBN_Deallocator(nbn_wrtc_c_cfg.ice_servers); -} - -#endif // NBNET_IMPL diff --git a/soak/CMakeLists.txt b/soak/CMakeLists.txt index d681fe7..3a24349 100644 --- a/soak/CMakeLists.txt +++ b/soak/CMakeLists.txt @@ -12,19 +12,40 @@ endif (CPP_COMPILE) unset(CPP_COMPILE) -add_compile_options(-Wall) +add_executable(client client.c soak.c log.c cargs.c ../nbnet.c) +add_executable(server server.c soak.c log.c cargs.c ../nbnet.c) -if(CMAKE_COMPILER_IS_GNUCXX) - add_compile_options(-Wextra -Wpedantic) -endif (CMAKE_COMPILER_IS_GNUCXX) +if (CMAKE_BUILD_TYPE MATCHES "Debug") + message("Compiling in Debug mode with packet simulator enabled") -add_executable(client client.c soak.c logging.c cargs.c) -add_executable(server server.c soak.c logging.c cargs.c) + target_compile_definitions(client PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) + target_compile_definitions(server PUBLIC NBN_DEBUG NBN_USE_PACKET_SIMULATOR) +endif() + +target_compile_options(server PUBLIC -Wall -Wextra) +target_compile_options(client PUBLIC -Wall -Wextra) -target_compile_definitions(client PUBLIC NBN_DEBUG NBN_DISABLE_STALE_CONNECTION_DETECTION NBN_USE_PACKET_SIMULATOR SOAK_CLIENT) -target_compile_definitions(server PUBLIC NBN_DEBUG NBN_DISABLE_STALE_CONNECTION_DETECTION NBN_USE_PACKET_SIMULATOR SOAK_SERVER) +target_compile_definitions(client PUBLIC SOAK_CLIENT) +target_compile_definitions(server PUBLIC SOAK_SERVER) + +target_include_directories(client PUBLIC "../") +target_include_directories(server PUBLIC "../") option(WEBRTC_NATIVE OFF) +option(UDP OFF) + +# compile with UDP driver +if (UDP) + # can't compile UDP driver with emscripten + if (EMSCRIPTEN) + message(SEND_ERROR "Can't compile UDP driver with emscripten") + endif (EMSCRIPTEN) + + message("Compiling with UDP driver") + + target_compile_definitions(server PUBLIC NBN_UDP) + target_compile_definitions(client PUBLIC NBN_UDP) +endif (UDP) # compile with C WebRTC driver if (WEBRTC_NATIVE) @@ -33,16 +54,13 @@ if (WEBRTC_NATIVE) message(SEND_ERROR "Can't compile WebRTC native driver with emscripten") endif (EMSCRIPTEN) - message("Compiling with WebRTC native driver") + message("Compiling server with WebRTC native driver") - target_compile_definitions(server PUBLIC WEBRTC_NATIVE) - target_compile_definitions(client PUBLIC WEBRTC_NATIVE) + target_compile_definitions(server PUBLIC NBN_WEBRTC_NATIVE) target_link_libraries(server ${LIBDATACHANNEL_LIBRARY_PATH} m) - target_link_libraries(client ${LIBDATACHANNEL_LIBRARY_PATH} m) target_include_directories(server PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") - target_include_directories(client PUBLIC "${LIBDATACHANNEL_INCLUDE_PATH}") endif (WEBRTC_NATIVE) @@ -66,21 +84,24 @@ endif (UNIX) if (EMSCRIPTEN) set(ASYNCIFY_IMPORTS "[\"__js_game_server_start\", \"__js_game_client_start\", \"__js_game_client_close\"]") - set_target_properties(server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../net_drivers/webrtc/js/api.js \ + set_target_properties(server PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../webrtc/js/api.js \ + -s EXPORTED_RUNTIME_METHODS=[HEAPU8] \ -s TOTAL_MEMORY=30MB \ -s USE_PTHREADS=1 \ -s PTHREAD_POOL_SIZE=4 \ -s EXIT_RUNTIME=1 \ + -s RUNTIME_DEBUG=1 \ -s ASSERTIONS=1 \ -s ASYNCIFY \ -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") - set_target_properties(client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../net_drivers/webrtc/js/api.js \ + set_target_properties(client PROPERTIES LINK_FLAGS "--js-library ${CMAKE_CURRENT_SOURCE_DIR}/../webrtc/js/api.js \ -s TOTAL_MEMORY=30MB \ -s USE_PTHREADS=1 \ -s PTHREAD_POOL_SIZE=4 \ -s EXIT_RUNTIME=1 \ -s ASSERTIONS=1 \ + -s RUNTIME_DEBUG=1 \ -s ASYNCIFY \ -s ASYNCIFY_IMPORTS=\"${ASYNCIFY_IMPORTS}\"") endif() diff --git a/soak/cli_out b/soak/cli_out new file mode 100644 index 0000000..3497e5a --- /dev/null +++ b/soak/cli_out @@ -0,0 +1,869 @@ + +> client +> node build_web/client.js --message_count=100 + +w:0,t:0x00000000: addRunDependency wasm-instantiate +w:0,t:0x00000000: locateFile: client.wasm scriptDirectory: /Users/nathb/Dev/GameDev/nbnet/soak/build_web/ +w:0,t:0x00000000: asynchronously preparing wasm +w:0,t:0x00000000: run() called, but dependencies remain, so not running +w:0,t:0x00000000: receiveInstance +w:0,t:0x00000000: assigning exports +w:0,t:0x00000000: removeRunDependency wasm-instantiate +w:0,t:0x00000000: writeStackCookie: 0x00000000 +w:0,t:0x00000000: addRunDependency loading-workers +w:0,t:0x00000000: run() called, but dependencies remain, so not running +w:1,t:0x00000000: addRunDependency wasm-instantiate +w:4,t:0x00000000: addRunDependency wasm-instantiate +w:1,t:0x00000000: receiveInstance +w:1,t:0x00000000: assigning exports +w:4,t:0x00000000: receiveInstance +w:1,t:0x00000000: removeRunDependency wasm-instantiate +w:1,t:0x00000000: initRuntime +w:4,t:0x00000000: assigning exports +w:4,t:0x00000000: removeRunDependency wasm-instantiate +w:2,t:0x00000000: addRunDependency wasm-instantiate +w:4,t:0x00000000: initRuntime +w:2,t:0x00000000: receiveInstance +w:2,t:0x00000000: assigning exports +w:2,t:0x00000000: removeRunDependency wasm-instantiate +w:2,t:0x00000000: initRuntime +w:3,t:0x00000000: addRunDependency wasm-instantiate +w:3,t:0x00000000: receiveInstance +w:3,t:0x00000000: assigning exports +w:3,t:0x00000000: removeRunDependency wasm-instantiate +w:3,t:0x00000000: initRuntime +w:0,t:0x00000000: removeRunDependency loading-workers +w:0,t:0x00000000: writeStackCookie: 0x00000000 +w:0,t:0x00000000: initRuntime +w:0,t:0x00021718: done __wasm_call_ctors +w:0,t:0x00021718: done ATPOSTCTORS +w:4,t:0x00000000: writeStackCookie: 0x00022bf0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:4044: Packet simulator started (Packet loss: 0.000000, Packet duplication: 0.000000, Ping: 0.000000, Jitter: 0.000000) +2026-02-14T07:49:11.311Z [GameClient] info: Connecting... +2026-02-14T07:49:11.312Z [StandaloneSignalingClient] info: 0 +2026-02-14T07:49:11.312Z [StandaloneSignalingClient] info: Connecting to ws://127.0.0.1:42044... +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: proc_exit: 0 +w:0,t:0x00021718: handleException: unwinding: EXITSTATUS=0 +2026-02-14T07:49:11.339Z [StandaloneSignalingClient] info: Connected +2026-02-14T07:49:11.339Z [GameClient] info: Creating server peer... +2026-02-14T07:49:11.345Z [Peer(0)] info: Offer created and set as local description +2026-02-14T07:49:11.346Z [StandaloneSignalingClient] info: Send signaling data: { + type: 'offer', + sdp: 'v=0\r\n' + + 'o=- 5703980242004240308 2 IN IP4 127.0.0.1\r\n' + + 's=-\r\n' + + 't=0 0\r\n' + + 'a=group:BUNDLE 0\r\n' + + 'a=extmap-allow-mixed\r\n' + + 'a=msid-semantic: WMS\r\n' + + 'm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n' + + 'c=IN IP4 0.0.0.0\r\n' + + 'a=ice-ufrag:/RvV\r\n' + + 'a=ice-pwd:IwolxOyvYLfRqGBG/XdeiUTA\r\n' + + 'a=ice-options:trickle\r\n' + + 'a=fingerprint:sha-256 F2:71:B9:94:53:D1:10:F4:5B:D8:4D:88:2B:54:B1:B0:68:76:0B:68:53:23:AC:1E:61:DA:4D:36:56:3A:8C:01\r\n' + + 'a=setup:actpass\r\n' + + 'a=mid:0\r\n' + + 'a=sctp-port:5000\r\n' + + 'a=max-message-size:262144\r\n' +} +2026-02-14T07:49:11.351Z [Peer(0)] info: Ice gathering state changed: gathering +2026-02-14T07:49:11.353Z [Peer(0)] info: Got new candidate, save it to the candidates list +2026-02-14T07:49:11.354Z [Peer(0)] info: Got new candidate, save it to the candidates list +2026-02-14T07:49:11.356Z [Peer(0)] info: Got new candidate, save it to the candidates list +2026-02-14T07:49:11.356Z [Peer(0)] info: Got new candidate, save it to the candidates list +2026-02-14T07:49:11.356Z [StandaloneSignalingClient] info: Received signaling data: {"type":"answer", "sdp":"v=0\r\no=rtc 422846998 0 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=msid-semantic:WMS *\r\na=ice-options:ice2,trickle\r\na=fingerprint:sha-256 6E:3B:36:4B:60:21:6F:14:66:E4:AC:D3:96:44:41:68:32:E1:34:1D:F7:0D:EB:56:74:C7:CF:D9:C7:C0:0C:68\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\na=mid:0\r\na=sendrecv\r\na=sctp-port:5000\r\na=max-message-size:262144\r\na=setup:active\r\na=ice-ufrag:Rcrb\r\na=ice-pwd:umhyogN/fCkQnBA7G/MEK6\r\na=candidate:1 1 UDP 2116026367 2a02:842a:3620:e701:4d3:7a7e:e243:2e21 63275 typ host\r\na=candidate:2 1 UDP 2114977535 192.168.1.212 63275 typ host\r\n"} +2026-02-14T07:49:11.356Z [Peer(0)] info: Got answer +2026-02-14T07:49:11.358Z [Peer(0)] info: Remote description set, notify remote peer that we are ready to receive his ice candidates +2026-02-14T07:49:11.358Z [StandaloneSignalingClient] info: Send signaling data: { signaling: [Object] } +2026-02-14T07:49:11.359Z [Peer(0)] error: Error while gathering ice candidates: 701 +2026-02-14T07:49:11.359Z [Peer(0)] error: Error while gathering ice candidates: 701 +2026-02-14T07:49:11.359Z [Peer(0)] error: Error while gathering ice candidates: 701 +2026-02-14T07:49:11.359Z [Peer(0)] error: Error while gathering ice candidates: 701 +2026-02-14T07:49:11.407Z [Peer(0)] info: Ice gathering state changed: complete +2026-02-14T07:49:11.408Z [Peer(0)] info: All candidates gathered, waiting for the remote peer to be ready to receive them +2026-02-14T07:49:11.414Z [Peer(0)] info: unreliable data channel opened (id: 0) +2026-02-14T07:49:11.414Z [GameClient] info: Connected +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 INFO /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1903: WebRTC_EMSCRIPTEN driver started +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1791: Enqueue message of type 252 on channel 1 +2026-02-14 08:49:11 INFO /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1949: Started +w:0,t:0x00021718: 08:49:11 INFO /Users/nathb/Dev/GameDev/nbnet/soak/soak.c:73: Soak test initialized (Packet loss: 0.000000, Packet duplication: 0.000000, Ping: 0.000000, Jitter: 0.000000) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 1 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 1 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 1 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 1 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 1 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 2 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 2 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 3 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 4 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 3 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 5 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 4 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 4 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 6 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 5 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 5 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 7 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 7 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 7 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 6 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 6 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 8 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 7 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 7 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 9 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 8 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 8 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 10 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 11 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 9 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 9 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 12 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 10 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 10 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 13 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 13 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 13 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 11 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 11 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 14 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 12 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 12 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 15 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 13 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 13 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 16 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 14 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 14 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 17 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 15 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 15 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 18 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 16 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 16 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 19 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 19 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 19 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 17 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 17 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 20 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 21 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 18 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 18 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 22 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 19 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 19 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 23 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 20 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 20 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 24 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 21 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 21 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 25 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 25 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 25 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 22 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 22 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 26 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 23 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 23 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 27 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 24 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 24 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 28 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 25 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 25 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 29 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 30 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 26 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 26 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 31 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 31 (length: 4, type: 252) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 31 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 27 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 27 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 32 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 28 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 28 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 33 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 29 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 29 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:11 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 34 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 30 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 30 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 35 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 31 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 31 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 36 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 32 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 32 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 37 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 37 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 37 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 33 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 33 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 38 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 34 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 34 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 39 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 35 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 35 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 40 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 41 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 36 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 36 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 42 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 37 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 37 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 43 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 43 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 43 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 38 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 38 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 44 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 39 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 39 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 45 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 40 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 40 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 46 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 41 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 41 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 47 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 42 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 42 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 48 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 43 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 43 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 49 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 49 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 49 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 44 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 44 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 50 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 45 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 45 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 51 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 52 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 46 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 46 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 53 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 47 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 47 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 54 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 48 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 48 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 55 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 55 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 55 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 49 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 49 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 56 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 50 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 50 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 57 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 51 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 51 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 58 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 52 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 52 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 59 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 53 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 53 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 60 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 54 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 54 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 61 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 61 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 61 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 62 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 55 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 55 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 63 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 56 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 56 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 64 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 57 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 57 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 65 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 58 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 58 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 66 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 59 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 59 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 67 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 67 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 67 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 60 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 60 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 68 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 61 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 61 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 69 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 62 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 62 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 70 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 63 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 63 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 71 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 64 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 64 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 72 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 65 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 65 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:863: Write message 0 (type: 252, length: 4) to packet 73 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1351: Message 0 added to packet 73 (length: 4, type: 252) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 73 to connection 252 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 66 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 66 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 74 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 67 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 67 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 75 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1760: Received packet 68 (conn id: 133780, ack: 0, messages count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1255: Processing received packet 68 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 76 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 +w:0,t:0x00021718: runtimeKeepalivePop -> counter=0 +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1294: Flushing all channels +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 0 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 1 (message count: 1) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 2 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1306: Flushing channel 3 (message count: 0) +2026-02-14 08:49:12 DEBUG /Users/nathb/Dev/GameDev/nbnet/nbnet.c:1519: Send packet 77 to connection 0 (messages count: 0) +w:0,t:0x00021718: runtimeKeepalivePush -> counter=1 diff --git a/soak/client.c b/soak/client.c index 1e66a29..25b6e3e 100644 --- a/soak/client.c +++ b/soak/client.c @@ -21,34 +21,28 @@ */ -#define NBNET_IMPL - +#include +#include +#include +#include +#include +#include "nbnet.h" #include "soak.h" +#include "log.h" #ifdef __EMSCRIPTEN__ - -#include "../net_drivers/webrtc.h" - -#else - -#include "../net_drivers/udp.h" - -#ifdef WEBRTC_NATIVE -#include "../net_drivers/webrtc_c.h" +#include "emscripten.h" #endif -#endif // __EMSCRIPTEN__ - -typedef struct -{ - uint8_t data[SOAK_MESSAGE_MAX_DATA_LENGTH]; +typedef struct { + uint8_t data[SOAK_MESSAGE_BIG_MAX_LENGTH]; uint8_t channel_id; unsigned int length; bool free; } Soak_MessageEntry; -typedef struct -{ +typedef struct { + uint8_t id; unsigned int message_count; unsigned int sent_message_count; unsigned int next_msg_id; @@ -57,137 +51,149 @@ typedef struct Soak_MessageEntry messages[SOAK_CLIENT_MAX_PENDING_MESSAGES]; } SoakChannel; -static bool connected = false; -static unsigned int done_channel_count = 0; +typedef struct { + unsigned int done_channel_count; + SoakChannel *channels; + NBN_Client *client; +} Soak_Client_State; -static void GenerateRandomBytes(uint8_t *data, unsigned int length) -{ +static void GenerateRandomBytes(uint8_t *data, unsigned int length) { for (unsigned int i = 0; i < length; i++) data[i] = rand() % 255 + 1; } -static int SendSoakMessages(SoakChannel *channel, uint8_t channel_id) -{ +static int SendSoakMessages(NBN_Client *client, SoakChannel *channel, uint8_t channel_id) { unsigned int msg_count = channel->message_count; - if (channel->sent_message_count < msg_count) - { + if (channel->sent_message_count < msg_count) { // number of messages yet to be sent unsigned int remaining_message_count = msg_count - channel->sent_message_count; // number of messages sent but not yet to be acked unsigned int pending_message_count = channel->last_sent_message_id - channel->last_recved_message_id; - Soak_LogInfo("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", - channel->sent_message_count, pending_message_count, remaining_message_count); + log_info("Compute number of soak messages to send (sent: %d, pending: %d, remaining: %d)", + channel->sent_message_count, pending_message_count, remaining_message_count); + + unsigned int capacity = NBN_Client_GetChannelCurrentCapacity(client, channel->id); + // make sure that we don't exceed channel capacity + unsigned int max_pending_messages = (unsigned int)fmin(SOAK_CLIENT_MAX_PENDING_MESSAGES, capacity); // don't send anything on this tick if we have reached the max number of unacked messages - if (pending_message_count >= SOAK_CLIENT_MAX_PENDING_MESSAGES) - { - Soak_LogInfo("Max number of pending messages has been reached, not sending anything this tick"); + if (pending_message_count >= max_pending_messages) { + log_info("Max number of pending messages has been reached, not sending anything this tick"); return 0; } // number of messages to send on this tick - unsigned int send_message_count = MIN( - SOAK_CLIENT_MAX_PENDING_MESSAGES - pending_message_count, remaining_message_count); - - Soak_LogInfo("Will send %d soak messages this tick", send_message_count); + unsigned int send_message_count = fmin(max_pending_messages - pending_message_count, remaining_message_count); - for (unsigned int i = 0; i < send_message_count; i++) - { - SoakMessage *msg = SoakMessage_CreateOutgoing(); - - if (msg == NULL) - { - Soak_LogError("Failed to create soak message"); - - return -1; - } + log_info("Will send %d soak messages this tick", send_message_count); + for (unsigned int i = 0; i < send_message_count; i++) { int percent = rand() % 100 + 1; - - if (percent <= SOAK_BIG_MESSAGE_PERCENTAGE) - { - // chunked - msg->data_length = rand() % (SOAK_MESSAGE_MAX_DATA_LENGTH - 1024) + 1024; - } - else - { - // not chuncked - msg->data_length = rand() % (200 - SOAK_MESSAGE_MIN_DATA_LENGTH) + SOAK_MESSAGE_MIN_DATA_LENGTH; + unsigned int min_len; + unsigned int max_len; + + if (percent <= SOAK_BIG_MESSAGE_PERCENTAGE) { + min_len = SOAK_MESSAGE_BIG_MIN_DATA_LENGTH; + max_len = SOAK_MESSAGE_BIG_MAX_DATA_LENGTH; + } else { + min_len = SOAK_MESSAGE_SMALL_MIN_DATA_LENGTH; + max_len = SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH; } - msg->id = channel->next_msg_id++; - - GenerateRandomBytes(msg->data, msg->data_length); - - Soak_MessageEntry *entry = &channel->messages[(msg->id - 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES]; + unsigned int data_length = rand() % (max_len - min_len) + min_len; + unsigned int msg_id = channel->next_msg_id++; + Soak_MessageEntry *entry = &channel->messages[(msg_id - 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES]; assert(entry->free); - entry->length = msg->data_length; + GenerateRandomBytes(entry->data, data_length); + + // entry->msg_id = msg_id; + entry->length = data_length; entry->free = false; entry->channel_id = channel_id; - memcpy(entry->data, msg->data, msg->data_length); - Soak_LogInfo("Send soak message (id: %d, data length: %d)", msg->id, msg->data_length); + log_info("Send soak message (id: %d, data length: %d)", msg_id, data_length); + + // TODO: support big messages + NBN_Writer *writer = NBN_Client_CreateMessage(client, SOAK_MESSAGE_SMALL, channel->id); - if (NBN_GameClient_SendMessage(SOAK_MESSAGE, channel_id, msg) < 0) + if (!writer) { return -1; + } + + SoakMessage_Write(writer, msg_id, entry->data, entry->length); channel->sent_message_count++; - channel->last_sent_message_id = msg->id; + channel->last_sent_message_id = msg_id; } } return 0; } -static int HandleReceivedSoakMessage(SoakMessage *msg, uint8_t channel_id, SoakChannel *channels) -{ - SoakChannel *channel = &channels[channel_id]; +static int HandleReceivedSoakMessage(Soak_Client_State *state, uint8_t channel_id) { + assert(channel_id >= 2 && channel_id < SOAK_CHANNEL_COUNT + 2); + + SoakChannel *channel = &state->channels[channel_id - 2]; + unsigned int msg_id; + unsigned int data_length; + static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; - if (msg->id != channel->last_recved_message_id + 1) - { - Soak_LogError("Expected to receive message %d but received message %d (channel_id: %d)", channel->last_recved_message_id + 1, msg->id, channel_id); + NBN_Reader *reader = NBN_Client_ReadMessage(state->client); + + if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { + log_error("Failed to read soak message"); return -1; } - Soak_MessageEntry *entry = &channel->messages[(msg->id - 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES]; + if (msg_id != channel->last_recved_message_id + 1) { + log_error("Expected to receive message %d but received message %d (channel_id: %d)", + channel->last_recved_message_id + 1, msg_id, channel_id); + + return -1; + } + + Soak_MessageEntry *entry = &channel->messages[(msg_id - 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES]; assert(!entry->free); assert(entry->channel_id == channel_id); - if (memcmp(msg->data, entry->data, msg->data_length) != 0) - { - Soak_LogError("Received invalid data for message %d (data length: %d, channel_id: %d)", msg->id, msg->data_length, channel_id); + if (data_length != entry->length) { + log_error("Expected message %d to have length %d but was %d (channel_id: %d)", msg_id, entry->length, + data_length, channel_id); + + return -1; + } + + if (memcmp(recv_buffer, entry->data, data_length) != 0) { + log_error("Received invalid data for message %d (data length: %d, channel_id: %d)", msg_id, data_length, + channel_id); return -1; } entry->free = true; - channel->last_recved_message_id = msg->id; + channel->last_recved_message_id = msg_id; SoakOptions options = Soak_GetOptions(); - unsigned int channel_count = options.channel_count; - - Soak_LogInfo("Received soak message (length: %d, %d/%d) on channel %d", msg->data_length, msg->id, channel->message_count, channel_id); - SoakMessage_Destroy(msg); + log_info("Received soak message (length: %d, %d/%d) on channel %d", data_length, msg_id, channel->message_count, + channel_id); - if (channel->last_recved_message_id == channel->message_count) - { - Soak_LogInfo("Received all soak message echoes on channel %d", channel_id); - done_channel_count++; + if (channel->last_recved_message_id == channel->message_count) { + log_info("Received all soak message echoes on channel %d", channel_id); + state->done_channel_count++; } - if (done_channel_count >= channel_count) - { - Soak_LogInfo("Received all soak message echoes on all channels"); + if (state->done_channel_count >= SOAK_CHANNEL_COUNT) { + log_info("Received all soak message echoes on all channels"); Soak_Stop(); return SOAK_DONE; @@ -196,75 +202,65 @@ static int HandleReceivedSoakMessage(SoakMessage *msg, uint8_t channel_id, SoakC return 0; } -static int HandleReceivedMessage(SoakChannel *channels) -{ - NBN_MessageInfo msg = NBN_GameClient_GetMessageInfo(); +static int HandleReceivedMessage(Soak_Client_State *state) { + NBN_MessageInfo msg = NBN_Client_GetMessageInfo(state->client); - switch (msg.type) - { - case SOAK_MESSAGE: - return HandleReceivedSoakMessage((SoakMessage *)msg.data, msg.channel_id, channels); + int ret; - default: - Soak_LogError("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); + if (msg.type == SOAK_MESSAGE_SMALL) { + ret = HandleReceivedSoakMessage(state, msg.channel_id); + } else { + log_error("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); - return -1; + ret = -1; } - return 0; + return ret; } -static int Tick(void *data) -{ - SoakChannel *channels = (SoakChannel *)data; +static int Tick(void *data) { + Soak_Client_State *state = (Soak_Client_State *)data; int ev; - while ((ev = NBN_GameClient_Poll()) != NBN_NO_EVENT) - { - if (ev < 0) + while ((ev = NBN_Client_Poll(state->client)) != NBN_CLIENT_NO_EVENT) { + if (ev < 0) { + log_error("Error while poling client events"); return -1; + } - switch (ev) - { - case NBN_DISCONNECTED: - connected = false; - - Soak_LogInfo("Disconnected from server (code: %d)", NBN_GameClient_GetServerCloseCode()); - Soak_Stop(); - return 0; + switch (ev) { + case NBN_CLIENT_DISCONNECTED: + log_info("Disconnected from server (code: %d)", NBN_Client_GetServerCloseCode(state->client)); + Soak_Stop(); + return 0; - case NBN_CONNECTED: - Soak_LogInfo("Connected to server"); - connected = true; - break; + case NBN_CLIENT_CONNECTED: + log_info("Connected to server"); + break; - case NBN_MESSAGE_RECEIVED: - if (HandleReceivedMessage(channels) < 0) - return -1; - break; + case NBN_CLIENT_MESSAGE_RECEIVED: + if (HandleReceivedMessage(state) < 0) { + log_error("Error processing received message"); + return -1; + } + break; } } - if (connected) - { - unsigned int channel_count = Soak_GetOptions().channel_count; - - for (unsigned int c = 0; c < channel_count; c++) - { - SoakChannel *channel = &channels[c]; + if (NBN_Client_IsConnected(state->client)) { + for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { + SoakChannel *channel = &state->channels[c]; - if (SendSoakMessages(channel, c) < 0) - { - Soak_LogError("An error occured while sending messages on channel %d", c); + if (SendSoakMessages(state->client, channel, channel->id) < 0) { + log_error("An error occured while sending messages on channel %d", c); return -1; } } } - if (NBN_GameClient_SendPackets() < 0) - { - Soak_LogError("Failed to flush game client send queue. Exit"); + if (NBN_Client_Flush(state->client) < 0) { + log_error("Failed to flush game client send queue. Exit"); return -1; } @@ -272,52 +268,30 @@ static int Tick(void *data) return 0; } -int main(int argc, char *argv[]) -{ - Soak_SetLogLevel(LOG_TRACE); +int main(int argc, char *argv[]) { + srand(SOAK_SEED); + NBN_SetLogLevel(NBN_LOG_DEBUG); if (Soak_ReadCommandLine(argc, argv) < 0) return -1; SoakOptions options = Soak_GetOptions(); -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = false}); // Register the WebRTC driver -#else + log_info("Starting soak test client... (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", + options.packet_loss, options.packet_duplication, options.ping, options.jitter); -#ifdef WEBRTC_NATIVE - - if (options.webrtc) - { - // Register native WebRTC driver - const char *ice_servers[] = { "stun:stun01.sipphone.com" }; - NBN_WebRTC_C_Config cfg = { - .ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = false, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; - - NBN_WebRTC_C_Register(cfg); - } - else - { - NBN_UDP_Register(); - } - -#else + NBN_Client *client = NBN_Client_Create(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT); - NBN_UDP_Register(); + for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { + uint8_t channel_id = + NBN_Client_CreateChannel(client, NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); -#endif // WEBRTC_NATIVE - -#endif // __EMSCRIPTEN__ + // channels 0 and 1 are the default nbnet channels + assert(channel_id == 2 + c); + } - if (NBN_GameClient_Start(SOAK_PROTOCOL_NAME, "127.0.0.1", SOAK_PORT) < 0) - { - Soak_LogError("Failed to start game client. Exit"); + if (NBN_Client_Start(client) < 0) { + log_error("Failed to start client. Exit"); #ifdef __EMSCRIPTEN__ emscripten_force_exit(1); @@ -326,78 +300,41 @@ int main(int argc, char *argv[]) #endif } - if (Soak_Init(argc, argv) < 0) - { - Soak_LogError("Failed to initialize soak test"); - return 1; - } + NBN_Client_SetPing(client, options.ping); + NBN_Client_SetJitter(client, options.jitter); + NBN_Client_SetPacketLoss(client, options.packet_loss); + NBN_Client_SetPacketDuplication(client, options.packet_duplication); - unsigned int channel_count = options.channel_count; unsigned int message_count = options.message_count; - unsigned int message_per_channel = message_count / channel_count; - unsigned int leftover_message_count = message_count % channel_count; - SoakChannel *channels = (SoakChannel *)malloc(sizeof(SoakChannel) * channel_count); - - for (unsigned int c = 0; c < channel_count; c++) - { - SoakChannel *channel = &channels[c]; - + unsigned int message_per_channel = message_count / SOAK_CHANNEL_COUNT; + unsigned int leftover_message_count = message_count % SOAK_CHANNEL_COUNT; + + Soak_Client_State state; + state.channels = (SoakChannel *)malloc(sizeof(SoakChannel) * SOAK_CHANNEL_COUNT); + state.done_channel_count = 0; + state.client = client; + + for (int c = 0; c < SOAK_CHANNEL_COUNT; c++) { + SoakChannel *channel = &state.channels[c]; + + channel->id = 2 + c; // channels 0 and 1 are the default nbnet channels channel->next_msg_id = 1; channel->sent_message_count = 0; channel->last_recved_message_id = 0; channel->last_sent_message_id = 0; channel->message_count = message_per_channel; - for (int i = 0; i < SOAK_CLIENT_MAX_PENDING_MESSAGES; i++) - { + for (int i = 0; i < SOAK_CLIENT_MAX_PENDING_MESSAGES; i++) { channel->messages[i].free = true; } } - channels[channel_count - 1].message_count += leftover_message_count; - - NBN_ConnectionDebugCallback cbs = { - .OnMessageAddedToRecvQueue = Soak_Debug_PrintAddedToRecvQueue, - }; - NBN_GameClient_Debug_RegisterCallback(cbs); - - int ret = Soak_MainLoop(Tick, channels); + state.channels[SOAK_CHANNEL_COUNT - 1].message_count += leftover_message_count; - NBN_GameClient_Stop(); - free(channels); - - unsigned int created_outgoing_message_count = Soak_GetCreatedOutgoingSoakMessageCount(); - unsigned int destroyed_outgoing_message_count = Soak_GetDestroyedOutgoingSoakMessageCount(); - unsigned int created_incoming_message_count = Soak_GetCreatedIncomingSoakMessageCount(); - unsigned int destroyed_incoming_message_count = Soak_GetDestroyedIncomingSoakMessageCount(); - - Soak_LogInfo("Outgoing soak messages created: %d", created_outgoing_message_count); - Soak_LogInfo("Outgoing soak messages destroyed: %d", destroyed_outgoing_message_count); - Soak_LogInfo("Incoming soak messages created: %d", created_incoming_message_count); - Soak_LogInfo("Incoming soak messages destroyed: %d", destroyed_incoming_message_count); - - if (created_outgoing_message_count != destroyed_outgoing_message_count) - { - Soak_LogError("created_outgoing_message_count != destroyed_outgoing_message_count (potential memory leak !)"); - return 1; - } - - if (created_incoming_message_count != destroyed_incoming_message_count) - { - Soak_LogError("created_incoming_message_count != destroyed_incoming_message_count (potential memory leak !)"); - return 1; - } - - Soak_LogInfo("No memory leak detected! Cool... cool cool cool"); - -#ifdef WEBRTC_NATIVE - - if (options.webrtc) - { - NBN_WebRTC_C_Unregister(); - } + int ret = Soak_MainLoop(Tick, &state); -#endif // WEBRTC_NATIVE + NBN_Client_Stop(client); + free(state.channels); #ifdef __EMSCRIPTEN__ emscripten_force_exit(ret); diff --git a/soak/log.c b/soak/log.c new file mode 100644 index 0000000..55275e4 --- /dev/null +++ b/soak/log.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2020 rxi + * + * 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. + */ + +#include "log.h" + +#define MAX_CALLBACKS 32 + +typedef struct { + log_LogFn fn; + void *udata; + int level; +} Callback; + +static struct { + void *udata; + log_LockFn lock; + int level; + bool quiet; + Callback callbacks[MAX_CALLBACKS]; +} L; + +static const char *level_strings[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; + +#ifdef LOG_USE_COLOR +static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; +#endif + +static void stdout_callback(log_Event *ev) { + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; +#ifdef LOG_USE_COLOR + fprintf((FILE *)ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, level_colors[ev->level], + level_strings[ev->level], ev->file, ev->line); +#else + fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); +#endif + vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); + fprintf((FILE *)ev->udata, "\n"); + fflush((FILE *)ev->udata); +} + +static void file_callback(log_Event *ev) { + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + fprintf((FILE *)ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line); + vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); + fprintf((FILE *)ev->udata, "\n"); + fflush((FILE *)ev->udata); +} + +static void lock(void) { + if (L.lock) { + L.lock(true, L.udata); + } +} + +static void unlock(void) { + if (L.lock) { + L.lock(false, L.udata); + } +} + +const char *log_level_string(int level) { return level_strings[level]; } + +void log_set_lock(log_LockFn fn, void *udata) { + L.lock = fn; + L.udata = udata; +} + +void log_set_level(int level) { L.level = level; } + +void log_set_quiet(bool enable) { L.quiet = enable; } + +int log_add_callback(log_LogFn fn, void *udata, int level) { + for (int i = 0; i < MAX_CALLBACKS; i++) { + if (!L.callbacks[i].fn) { + L.callbacks[i] = (Callback){fn, udata, level}; + return 0; + } + } + return -1; +} + +int log_add_fp(FILE *fp, int level) { return log_add_callback(file_callback, fp, level); } + +static void init_event(log_Event *ev, void *udata) { + if (!ev->time) { + time_t t = time(NULL); + ev->time = localtime(&t); + } + ev->udata = udata; +} + +void log_log(int level, const char *file, int line, const char *fmt, ...) { + log_Event ev = { + .fmt = fmt, + .file = file, + .line = line, + .level = level, + }; + + lock(); + + if (!L.quiet && level >= L.level) { + init_event(&ev, stderr); + va_start(ev.ap, fmt); + stdout_callback(&ev); + va_end(ev.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { + Callback *cb = &L.callbacks[i]; + if (level >= cb->level) { + init_event(&ev, cb->udata); + va_start(ev.ap, fmt); + cb->fn(&ev); + va_end(ev.ap); + } + } + + unlock(); +} diff --git a/soak/log.h b/soak/log.h new file mode 100644 index 0000000..b1fae24 --- /dev/null +++ b/soak/log.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2020 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include +#include +#include + +#define LOG_VERSION "0.1.0" + +typedef struct { + va_list ap; + const char *fmt; + const char *file; + struct tm *time; + void *udata; + int line; + int level; +} log_Event; + +typedef void (*log_LogFn)(log_Event *ev); +typedef void (*log_LockFn)(bool lock, void *udata); + +enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; + +#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +const char* log_level_string(int level); +void log_set_lock(log_LockFn fn, void *udata); +void log_set_level(int level); +void log_set_quiet(bool enable); +int log_add_callback(log_LogFn fn, void *udata, int level); +int log_add_fp(FILE *fp, int level); + +void log_log(int level, const char *file, int line, const char *fmt, ...); + +#endif diff --git a/soak/logging.c b/soak/logging.c deleted file mode 100644 index a852645..0000000 --- a/soak/logging.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#include "logging.h" - -/* I did not write this library: https://github.com/rxi/log.c */ - -/** - * Copyright (c) 2017 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See `log.c` for details. - */ - -static struct -{ - void *udata; - log_LockFn lock; - FILE *fp; - int level; - int quiet; -} L; - -static const char *level_names[] = { - "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; - -#ifdef LOG_USE_COLOR -static const char *level_colors[] = { - "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"}; -#endif - -static void lock(void) -{ - if (L.lock) - { - L.lock(L.udata, 1); - } -} - -static void unlock(void) -{ - if (L.lock) - { - L.lock(L.udata, 0); - } -} - -void log_set_udata(void *udata) -{ - L.udata = udata; -} - -void log_set_lock(log_LockFn fn) -{ - L.lock = fn; -} - -void log_set_fp(FILE *fp) -{ - L.fp = fp; -} - -void log_set_level(int level) -{ - L.level = level; -} - -void log_set_quiet(int enable) -{ - L.quiet = enable ? 1 : 0; -} - -void log_log(int level, const char *file, int line, const char *fmt, ...) -{ - if (level < L.level) - { - return; - } - - /* Acquire lock */ - lock(); - - /* Get current time */ - time_t t = time(NULL); - struct tm *lt = localtime(&t); - - /* Log to stderr */ - if (!L.quiet) - { - va_list args; - char buf[16]; - buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; -#ifdef LOG_USE_COLOR - fprintf( - stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", - buf, level_colors[level], level_names[level], file, line); -#else - fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line); -#endif - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fprintf(stderr, "\n"); - fflush(stderr); - } - - /* Log to file */ - if (L.fp) - { - va_list args; - char buf[32]; - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; - fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line); - va_start(args, fmt); - vfprintf(L.fp, fmt, args); - va_end(args); - fprintf(L.fp, "\n"); - fflush(L.fp); - } - - /* Release lock */ - unlock(); -} diff --git a/soak/logging.h b/soak/logging.h deleted file mode 100644 index 0e7291f..0000000 --- a/soak/logging.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - - Copyright (C) 2024 BIAGINI Nathan - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source distribution. - -*/ - -#ifndef SOAK_LOGGING_H -#define SOAK_LOGGING_H - -/* I did not write this library: https://github.com/rxi/log.c */ - -/** - * Copyright (c) 2017 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See `log.c` for details. - */ - -#include -#include -#include - -#define LOG_VERSION "0.1.0" - -typedef void (*log_LockFn)(void *udata, int lock); - -enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; - -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) - -#define Soak_LogTrace(...) log_log(LOG_TRACE, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogDebug(...) log_log(LOG_DEBUG, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogInfo(...) log_log(LOG_INFO, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogWarn(...) log_log(LOG_WARN, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogError(...) log_log(LOG_ERROR, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_LogFatal(...) log_log(LOG_FATAL, __FILENAME__, __LINE__, __VA_ARGS__) -#define Soak_SetLogLevel(level) log_set_level(level) - -void log_set_udata(void *udata); -void log_set_lock(log_LockFn fn); -void log_set_fp(FILE *fp); -void log_set_level(int level); -void log_set_quiet(int enable); - -void log_log(int level, const char *file, int line, const char *fmt, ...); - -#endif /* SOAK_LOGGING_H */ diff --git a/soak/package-lock.json b/soak/package-lock.json index 2aa2bdc..e9053d7 100644 --- a/soak/package-lock.json +++ b/soak/package-lock.json @@ -1,14 +1,14 @@ { "name": "soak", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "dependencies": { - "nbnet": "file:../net_drivers/webrtc" + "nbnet": "file:../webrtc" } }, - "../net_drivers/webrtc": { + "../webrtc": { "name": "nbnet", "version": "1.0", "license": "MIT", @@ -18,765 +18,9 @@ "winston": "^3.2.1" } }, - "../net_drivers/webrtc/node_modules/@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "../net_drivers/webrtc/node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "../net_drivers/webrtc/node_modules/bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../net_drivers/webrtc/node_modules/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "../net_drivers/webrtc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "../net_drivers/webrtc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "../net_drivers/webrtc/node_modules/color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "../net_drivers/webrtc/node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "../net_drivers/webrtc/node_modules/colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "dependencies": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "../net_drivers/webrtc/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "../net_drivers/webrtc/node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "../net_drivers/webrtc/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "../net_drivers/webrtc/node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "../net_drivers/webrtc/node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "../net_drivers/webrtc/node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "../net_drivers/webrtc/node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "../net_drivers/webrtc/node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dependencies": { - "type": "^2.0.0" - } - }, - "../net_drivers/webrtc/node_modules/ext/node_modules/type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - }, - "../net_drivers/webrtc/node_modules/fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "../net_drivers/webrtc/node_modules/fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "../net_drivers/webrtc/node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "../net_drivers/webrtc/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "../net_drivers/webrtc/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "../net_drivers/webrtc/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "../net_drivers/webrtc/node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "../net_drivers/webrtc/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "../net_drivers/webrtc/node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "../net_drivers/webrtc/node_modules/logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "dependencies": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - } - }, - "../net_drivers/webrtc/node_modules/logform/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "../net_drivers/webrtc/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "../net_drivers/webrtc/node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "../net_drivers/webrtc/node_modules/node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "../net_drivers/webrtc/node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "../net_drivers/webrtc/node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "../net_drivers/webrtc/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "../net_drivers/webrtc/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "../net_drivers/webrtc/node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "../net_drivers/webrtc/node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "../net_drivers/webrtc/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "../net_drivers/webrtc/node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "../net_drivers/webrtc/node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "../net_drivers/webrtc/node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "../net_drivers/webrtc/node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "../net_drivers/webrtc/node_modules/utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "dependencies": { - "node-gyp-build": "~3.7.0" - } - }, - "../net_drivers/webrtc/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "../net_drivers/webrtc/node_modules/websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "../net_drivers/webrtc/node_modules/winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "dependencies": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "../net_drivers/webrtc/node_modules/winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "dependencies": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - } - }, - "../net_drivers/webrtc/node_modules/winston-transport/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "../net_drivers/webrtc/node_modules/winston-transport/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "../net_drivers/webrtc/node_modules/winston-transport/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "../net_drivers/webrtc/node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, "node_modules/nbnet": { - "resolved": "../net_drivers/webrtc", + "resolved": "../webrtc", "link": true } - }, - "dependencies": { - "nbnet": { - "version": "file:../net_drivers/webrtc", - "requires": { - "@roamhq/wrtc": "^0.8.0", - "websocket": "^1.0.31", - "winston": "^3.2.1" - }, - "dependencies": { - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "requires": { - "node-gyp-build": "~3.7.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "websocket": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.32.tgz", - "integrity": "sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - } - } - } } } diff --git a/soak/package.json b/soak/package.json index ed5a951..8bf7fd1 100644 --- a/soak/package.json +++ b/soak/package.json @@ -1,9 +1,9 @@ { "scripts": { - "server": "node --experimental-wasm-threads build_web/server.js", - "client": "node --experimental-wasm-threads build_web/client.js" + "server": "node build_web/server.js", + "client": "node build_web/client.js" }, "dependencies": { - "nbnet": "file:../net_drivers/webrtc" + "nbnet": "file:../webrtc" } } diff --git a/soak/server.c b/soak/server.c index a207cb3..50da623 100644 --- a/soak/server.c +++ b/soak/server.c @@ -23,84 +23,59 @@ */ #include +#include #include - -#define NBNET_IMPL - +#include +#include +#include "nbnet.h" #include "soak.h" +#include "log.h" #ifdef __EMSCRIPTEN__ -#include "../net_drivers/webrtc.h" -#else -#include "../net_drivers/udp.h" - -#ifdef WEBRTC_NATIVE -#include "../net_drivers/webrtc_c.h" +#include "emscripten.h" #endif -#endif // __EMSCRIPTEN__ - -typedef struct -{ +typedef struct { uint8_t channel_id; - SoakMessage *msg; + unsigned int msg_id; + uint8_t data[SOAK_MESSAGE_BIG_MAX_LENGTH]; + unsigned int length; } Soak_MessageEntry; -typedef struct -{ +typedef struct { unsigned int head; unsigned int tail; unsigned int count; Soak_MessageEntry messages[SOAK_CLIENT_MAX_PENDING_MESSAGES]; } EchoMessageQueue; -typedef struct -{ +typedef struct { + uint8_t id; unsigned int recved_messages_count; unsigned int last_recved_message_id; EchoMessageQueue echo_queue; } SoakChannel; -typedef struct -{ - NBN_ConnectionHandle connection_handle; +typedef struct { bool error; bool is_closed; SoakChannel *channels; } SoakClient; -static SoakClient *clients[SOAK_MAX_CLIENTS] = {NULL}; -static unsigned int client_count = 0; - -static void HandleNewConnection(void) -{ - if (client_count == SOAK_MAX_CLIENTS) - { - NBN_LogInfo("Connection rejected"); - - NBN_GameServer_RejectIncomingConnectionWithCode(SOAK_SERVER_FULL_CODE); - - return; - } - - NBN_ConnectionHandle connection_handle = NBN_GameServer_GetIncomingConnection(); - - assert(clients[connection_handle - 1] == NULL); - - NBN_GameServer_AcceptIncomingConnection(); +static void HandleNewConnection(NBN_Server *server) { + NBN_Server_AcceptIncomingConnection(server); + NBN_ConnectionHandle *conn = NBN_Server_GetIncomingConnection(server); SoakClient *soak_client = (SoakClient *)malloc(sizeof(SoakClient)); - unsigned int channel_count = Soak_GetOptions().channel_count; - soak_client->connection_handle = connection_handle; soak_client->error = false; soak_client->is_closed = false; - soak_client->channels = (SoakChannel *)malloc(sizeof(SoakChannel) * channel_count); + soak_client->channels = (SoakChannel *)malloc(sizeof(SoakChannel) * SOAK_CHANNEL_COUNT); - for (unsigned int i = 0; i < channel_count; i++) - { - SoakChannel *channel = &soak_client->channels[i]; + for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { + SoakChannel *channel = &soak_client->channels[c]; + channel->id = 2 + c; channel->recved_messages_count = 0; channel->last_recved_message_id = 0; @@ -112,58 +87,50 @@ static void HandleNewConnection(void) memset(channel->echo_queue.messages, 0, sizeof(channel->echo_queue.messages)); } - clients[connection_handle - 1] = soak_client; - client_count++; + conn->user_data = soak_client; - Soak_LogInfo("Client has connected (ID: %d)", soak_client->connection_handle); + log_info("Client has connected (ID: %llu)", conn->id); } -static void HandleClientDisconnection(NBN_ConnectionHandle connection_handle) -{ - SoakClient *soak_client = clients[connection_handle - 1]; +static void HandleClientDisconnection(NBN_DisconnectionInfo info) { + SoakClient *soak_client = (SoakClient *)info.user_data; assert(soak_client != NULL); - Soak_LogInfo("Client has disconnected (ID: %d)", connection_handle); + log_info("Client has disconnected (ID: %lld)", info.conn_id); free(soak_client->channels); free(soak_client); - - clients[connection_handle - 1] = NULL; - client_count--; } -static void EchoReceivedSoakMessages(void) -{ - unsigned int channel_count = Soak_GetOptions().channel_count; +static void EchoReceivedSoakMessages(NBN_Server *server) { + NBN_Client_Iterator it = 0; + NBN_ConnectionHandle *conn; - for (unsigned int i = 0; i < SOAK_MAX_CLIENTS; i++) - { - SoakClient *soak_client = clients[i]; + while ((conn = NBN_Server_GetNextClient(server, &it))) { - if (soak_client == NULL || soak_client->is_closed) + SoakClient *soak_client = (SoakClient *)conn->user_data; + + assert(soak_client != NULL); + + if (soak_client->is_closed) continue; - for (unsigned int c = 0; c < channel_count; c++) - { + for (unsigned int c = 0; c < SOAK_CHANNEL_COUNT; c++) { SoakChannel *channel = &soak_client->channels[c]; + int send_count = NBN_Server_GetChannelCurrentCapacity(server, channel->id, conn); - while (channel->echo_queue.count > 0) - { + // make sure that we don't exceed channel capacity + while (channel->echo_queue.count > 0 && --send_count >= 0) { Soak_MessageEntry *msg_entry = &channel->echo_queue.messages[channel->echo_queue.head]; + NBN_Writer *writer = + NBN_Server_CreateMessage(server, SOAK_MESSAGE_SMALL, channel->id, conn); // TODO: support big - assert(msg_entry->msg); - assert(!msg_entry->msg->outgoing); - - SoakMessage *echo_msg = SoakMessage_CreateOutgoing(); - - if (echo_msg == NULL) - { - Soak_LogError("Failed to create soak message"); + if (!writer) { + log_error("Failed to send soak message to client %llu, closing client", conn->id); - if (NBN_GameServer_CloseClient(soak_client->connection_handle) < 0) - { - Soak_LogError("Failed to close client %d", soak_client->connection_handle); + if (NBN_Server_CloseClient(server, conn) < 0) { + log_error("Failed to close client %llu", conn->id); abort(); } @@ -171,19 +138,13 @@ static void EchoReceivedSoakMessages(void) return; } - echo_msg->id = msg_entry->msg->id; - echo_msg->data_length = msg_entry->msg->data_length; + SoakMessage_Write(writer, msg_entry->msg_id, msg_entry->data, msg_entry->length); - memcpy(echo_msg->data, msg_entry->msg->data, msg_entry->msg->data_length); + log_info("Send soak message %d's echo (length: %d) to client %llu", msg_entry->msg_id, + msg_entry->length, conn->id); - Soak_LogInfo("Send soak message %d's echo to client %d", echo_msg->id, soak_client->connection_handle); + msg_entry->length = 0; - if (NBN_GameServer_SendMessageTo(soak_client->connection_handle, SOAK_MESSAGE, msg_entry->channel_id, echo_msg) < 0) - break; - - SoakMessage_Destroy(msg_entry->msg); - - msg_entry->msg = NULL; channel->echo_queue.head = (channel->echo_queue.head + 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES; channel->echo_queue.count--; } @@ -191,111 +152,117 @@ static void EchoReceivedSoakMessages(void) } } -static int HandleReceivedSoakMessage(SoakMessage *msg, NBN_ConnectionHandle sender, uint8_t channel_id) -{ - SoakClient *soak_client = clients[sender - 1]; +static int HandleReceivedSoakMessage(NBN_Reader *reader, NBN_ConnectionHandle *sender, uint8_t channel_id) { + SoakClient *soak_client = (SoakClient *)sender->user_data; - if (!soak_client || soak_client->error) + assert(soak_client != NULL); + + if (soak_client->error) return 0; - SoakChannel *channel = &soak_client->channels[channel_id]; + SoakChannel *channel = &soak_client->channels[channel_id - 2]; + unsigned int msg_id; + unsigned int data_length; + static uint8_t recv_buffer[SOAK_MESSAGE_BIG_MAX_DATA_LENGTH]; + + if (SoakMessage_Read(reader, &msg_id, recv_buffer, &data_length) < 0) { + log_error("Failed to read soak message"); + + return -1; + } - if (msg->id != channel->last_recved_message_id + 1) - { - Soak_LogError("Expected to receive message %d but received message %d (from client: %d)", - channel->last_recved_message_id + 1, msg->id, sender); + if (msg_id != channel->last_recved_message_id + 1) { + log_error("Expected to receive message %d but received message %d (from client: %d)", + channel->last_recved_message_id + 1, msg_id, sender); soak_client->error = true; return -1; } - Soak_LogInfo("Received soak message %d from client %d on channel %d", msg->id, sender, channel_id); + log_info("Received soak message %d (length: %d) from client %llu on channel %d", msg_id, data_length, sender->id, + channel_id); channel->recved_messages_count++; - channel->last_recved_message_id = msg->id; + channel->last_recved_message_id = msg_id; Soak_MessageEntry *msg_entry = &channel->echo_queue.messages[channel->echo_queue.tail]; assert(channel->echo_queue.count < SOAK_CLIENT_MAX_PENDING_MESSAGES); - assert(!msg_entry->msg); + assert(msg_entry->length == 0); - Soak_LogInfo("Enqueue soak message %d's echo for client %d on channel %d", msg->id, soak_client->connection_handle, channel_id); + log_info("Enqueue soak message %d's echo for client %llu on channel %d", msg_id, sender->id, channel_id); - msg_entry->msg = msg; + memcpy(msg_entry->data, recv_buffer, data_length); + msg_entry->msg_id = msg_id; + msg_entry->length = data_length; msg_entry->channel_id = channel_id; + channel->echo_queue.tail = (channel->echo_queue.tail + 1) % SOAK_CLIENT_MAX_PENDING_MESSAGES; channel->echo_queue.count++; return 0; } -static void HandleReceivedMessage(void) -{ - NBN_MessageInfo msg = NBN_GameServer_GetMessageInfo(); - SoakClient *soak_client = clients[msg.sender - 1]; - - switch (msg.type) - { - case SOAK_MESSAGE: - if (HandleReceivedSoakMessage((SoakMessage *)msg.data, msg.sender, msg.channel_id) < 0) - { - if (NBN_GameServer_CloseClient(msg.sender) < 0) - { - Soak_LogError("Failed to close client %d", msg.sender); - abort(); - } - - soak_client->is_closed = true; - } - break; +static void HandleReceivedMessage(NBN_Server *server) { + NBN_MessageInfo msg_info = NBN_Server_GetMessageInfo(server); + NBN_Reader *reader = NBN_Server_ReadMessage(server); + SoakClient *soak_client = (SoakClient *)msg_info.sender->user_data; - default: - Soak_LogError("Received unexpected message (type: %d, channel_id: %d)", msg.type, msg.channel_id); - - if (NBN_GameServer_CloseClient(msg.sender) < 0) - { - Soak_LogError("Failed to close client %d", msg.sender); + switch (msg_info.type) { + case SOAK_MESSAGE_SMALL: + if (HandleReceivedSoakMessage(reader, msg_info.sender, msg_info.channel_id) < 0) { + if (NBN_Server_CloseClient(server, msg_info.sender) < 0) { + log_error("Failed to close client %llu", msg_info.sender->id); abort(); } soak_client->is_closed = true; - break; + } + break; + + // TODO: support big messages + + default: + log_error("Received unexpected message (type: %d, channel_id: %d)", msg_info.type, msg_info.channel_id); + + if (NBN_Server_CloseClient(server, msg_info.sender) < 0) { + log_error("Failed to close client %llu", msg_info.sender->id); + abort(); + } + + soak_client->is_closed = true; + break; } } -static int Tick(void *data) -{ - (void)data; - +static int Tick(void *data) { + NBN_Server *server = (NBN_Server *)data; int ev; - while ((ev = NBN_GameServer_Poll()) != NBN_NO_EVENT) - { + while ((ev = NBN_Server_Poll(server)) != NBN_SERVER_NO_EVENT) { if (ev < 0) return -1; - switch (ev) - { - case NBN_NEW_CONNECTION: - HandleNewConnection(); - break; + switch (ev) { + case NBN_SERVER_NEW_CONNECTION: + HandleNewConnection(server); + break; - case NBN_CLIENT_DISCONNECTED: - HandleClientDisconnection(NBN_GameServer_GetDisconnectedClient()); - break; + case NBN_SERVER_DISCONNECTION: + HandleClientDisconnection(NBN_Server_GetDisconnectionInfo(server)); + break; - case NBN_CLIENT_MESSAGE_RECEIVED: - HandleReceivedMessage(); - break; + case NBN_SERVER_MESSAGE_RECEIVED: + HandleReceivedMessage(server); + break; } } - EchoReceivedSoakMessages(); + EchoReceivedSoakMessages(server); - if (NBN_GameServer_SendPackets() < 0) - { - Soak_LogError("Failed to flush game server send queue. Exit"); + if (NBN_Server_Flush(server) < 0) { + log_error("Failed to flush game server send queue. Exit"); return -1; } @@ -303,91 +270,49 @@ static int Tick(void *data) return 0; } -static void SigintHandler(int dummy) -{ - unsigned int created_outgoing_message_count = Soak_GetCreatedOutgoingSoakMessageCount(); - unsigned int destroyed_outgoing_message_count = Soak_GetDestroyedOutgoingSoakMessageCount(); - unsigned int created_incoming_message_count = Soak_GetCreatedIncomingSoakMessageCount(); - unsigned int destroyed_incoming_message_count = Soak_GetDestroyedIncomingSoakMessageCount(); - (void) dummy; - - Soak_LogInfo("Outgoing soak messages created: %d", created_outgoing_message_count); - Soak_LogInfo("Outgoing soak messages destroyed: %d", destroyed_outgoing_message_count); - Soak_LogInfo("Incoming soak messages created: %d", created_incoming_message_count); - Soak_LogInfo("Incoming soak messages destroyed: %d", destroyed_incoming_message_count); - - if (created_outgoing_message_count != destroyed_outgoing_message_count) - { - Soak_LogError("created_outgoing_message_count != destroyed_outgoing_message_count (potential memory leak !)"); - } - else if (created_incoming_message_count != destroyed_incoming_message_count) - { - Soak_LogError("created_incoming_message_count != destroyed_incoming_message_count (potential memory leak !)"); - } - else - { - Soak_LogInfo("No memory leak detected! Cool... cool cool cool"); - } +static void SigintHandler(int dummy) { Soak_Stop(); } - Soak_Stop(); -} - -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { + srand(SOAK_SEED); signal(SIGINT, SigintHandler); - Soak_SetLogLevel(LOG_TRACE); + NBN_SetLogLevel(NBN_LOG_DEBUG); if (Soak_ReadCommandLine(argc, argv) < 0) return -1; -#ifdef __EMSCRIPTEN__ - NBN_WebRTC_Register((NBN_WebRTC_Config){.enable_tls = false}); // Register JS WebRTC driver -#else - NBN_UDP_Register(); // Register the UDP driver -#endif // __EMSCRIPTEN__ + SoakOptions options = Soak_GetOptions(); -#ifdef WEBRTC_NATIVE - // Register native WebRTC driver - const char *ice_servers[] = { "stun:stun01.sipphone.com" }; - NBN_WebRTC_C_Config cfg = { - .ice_servers = ice_servers, - .ice_servers_count = 1, - .enable_tls = false, - .cert_path = NULL, - .key_path = NULL, - .passphrase = NULL, - .log_level = RTC_LOG_VERBOSE}; - - NBN_WebRTC_C_Register(cfg); -#endif // WEBRTC_NATIVE - - if (NBN_GameServer_Start(SOAK_PROTOCOL_NAME, SOAK_PORT)) - { - Soak_LogError("Failed to start game server"); + log_info("Starting soak test server... (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", + options.packet_loss, options.packet_duplication, options.ping, options.jitter); - return 1; + NBN_Server *server = NBN_Server_Create(SOAK_PROTOCOL_NAME, SOAK_PORT); + + for (uint8_t c = 0; c < SOAK_CHANNEL_COUNT; c++) { + uint8_t channel_id = + NBN_Server_CreateChannel(server, NBN_CHANNEL_RELIABLE, SOAK_CHANNEL_BUFFER_SIZE, SOAK_MAX_MESSAGE_SIZE); + + // channels 0 and 1 are the default nbnet channels + assert(channel_id == 2 + c); } - if (Soak_Init(argc, argv) < 0) - { - Soak_LogError("Failed to initialize soak test"); + if (NBN_Server_Start(server)) { + log_error("Failed to start game server"); return 1; } - NBN_ConnectionDebugCallback cbs = { - .OnMessageAddedToRecvQueue = Soak_Debug_PrintAddedToRecvQueue, - }; - NBN_GameServer_Debug_RegisterCallback(cbs); + NBN_Server_SetPing(server, options.ping); + NBN_Server_SetJitter(server, options.jitter); + NBN_Server_SetPacketLoss(server, options.packet_loss); + NBN_Server_SetPacketDuplication(server, options.packet_duplication); - int ret = Soak_MainLoop(Tick, NULL); + int ret = Soak_MainLoop(Tick, server); - NBN_GameServer_Stop(); - Soak_Deinit(); + NBN_Server_Stop(server); #ifdef WEBRTC_NATIVE - NBN_WebRTC_C_Unregister(); + NBN_WebRTC_Native_Unregister(); #endif return ret; diff --git a/soak/soak.c b/soak/soak.c index d245a0c..d7c5df5 100644 --- a/soak/soak.c +++ b/soak/soak.c @@ -22,8 +22,12 @@ */ -#include +#include #include +#include +#include +#include +#include "log.h" #ifdef __EMSCRIPTEN__ #include @@ -32,8 +36,8 @@ #include #endif -#include "soak.h" #include "cargs.h" +#include "soak.h" static bool running = true; static SoakOptions soak_options = {0}; @@ -42,83 +46,26 @@ static unsigned int created_incoming_soak_message_count = 0; static unsigned int destroyed_outgoing_soak_message_count = 0; static unsigned int destroyed_incoming_soak_message_count = 0; -static void Usage(void) -{ +static void Usage(void) { #ifdef SOAK_CLIENT #ifdef WEBRTC_NATIVE - printf("Usage: client --message_count= --channel_count= [--packet_loss=] \ + printf("Usage: client --message_count= [--packet_loss=] \ [--packet_duplication=] [--ping=] [--jitter=] [--webrtc]\n"); #else - printf("Usage: client --message_count= --channel_count= [--packet_loss=] \ + printf("Usage: client --message_count= [--packet_loss=] \ [--packet_duplication=] [--ping=] [--jitter=]\n"); #endif // WEBRTC_NATIVE #endif // SOAK_CLIENT #ifdef SOAK_SERVER - printf("Usage: server --channel_count= [--packet_loss=] \ + printf("Usage: server [--packet_loss=] \ [--packet_duplication=] [--ping=] [--jitter=]\n"); #endif } -int Soak_Init(int argc, char *argv[]) -{ - (void) argc; - (void) argv; - - srand(SOAK_SEED); - - SoakOptions options = Soak_GetOptions(); - - Soak_LogInfo("Soak test initialized (Packet loss: %f, Packet duplication: %f, Ping: %f, Jitter: %f)", - options.packet_loss, options.packet_duplication, options.ping, options.jitter); - -#ifdef SOAK_CLIENT - - NBN_GameClient_RegisterMessage(SOAK_MESSAGE, - (NBN_MessageBuilder)SoakMessage_CreateIncoming, - (NBN_MessageDestructor)SoakMessage_Destroy, - (NBN_MessageSerializer)SoakMessage_Serialize); - -#endif - -#ifdef SOAK_SERVER - - NBN_GameServer_RegisterMessage(SOAK_MESSAGE, - (NBN_MessageBuilder)SoakMessage_CreateIncoming, - (NBN_MessageDestructor)SoakMessage_Destroy, - (NBN_MessageSerializer)SoakMessage_Serialize); - -#endif - - /* Packet simulator configuration */ -#ifdef SOAK_CLIENT - NBN_GameClient_SetPing(soak_options.ping); - NBN_GameClient_SetJitter(soak_options.jitter); - NBN_GameClient_SetPacketLoss(soak_options.packet_loss); - NBN_GameClient_SetPacketDuplication(soak_options.packet_duplication); -#endif - -#ifdef SOAK_SERVER - NBN_GameServer_SetPing(soak_options.ping); - NBN_GameServer_SetJitter(soak_options.jitter); - NBN_GameServer_SetPacketLoss(soak_options.packet_loss); - NBN_GameServer_SetPacketDuplication(soak_options.packet_duplication); -#endif - - return 0; -} - -void Soak_Deinit(void) -{ - Soak_LogInfo("Done."); - Soak_LogInfo("Memory report:\n"); - // TODO -} - -int Soak_ReadCommandLine(int argc, char *argv[]) -{ +int Soak_ReadCommandLine(int argc, char *argv[]) { struct cag_option options[] = { #ifdef SOAK_CLIENT @@ -132,98 +79,55 @@ int Soak_ReadCommandLine(int argc, char *argv[]) #endif // SOAK_CLIENT - {'c', NULL, "channel_count", "VALUE", "Number of channels (1-NBN_MAX_CHANNELS)"}, {'l', NULL, "packet_loss", "VALUE", "Packet loss frenquency (0-1)"}, {'d', NULL, "packet_duplication", "VALUE", "Packet duplication frequency (0-1)"}, {'p', NULL, "ping", "VALUE", "Ping in seconds"}, - {'j', NULL, "jitter", "VALUE", "Jitter in seconds"} - }; + {'j', NULL, "jitter", "VALUE", "Jitter in seconds"}}; cag_option_context context; cag_option_prepare(&context, options, CAG_ARRAY_SIZE(options), argc, argv); - while (cag_option_fetch(&context)) - { + while (cag_option_fetch(&context)) { char option = cag_option_get(&context); #ifdef SOAK_CLIENT - if (option == 'm') - { + if (option == 'm') { const char *val = cag_option_get_value(&context); - if (val) - { + if (val) { soak_options.message_count = atoi(val); } - } - else if (option == 'w') - { + } else if (option == 'w') { soak_options.webrtc = true; } #else - if (false) {} -#endif - else if (option == 'c') - { - const char *val = cag_option_get_value(&context); - - if (val) - { - soak_options.channel_count = atoi(val); - } + if (false) { } - else if (option == 'l') - { +#endif + else if (option == 'l') { soak_options.packet_loss = atof(cag_option_get_value(&context)); - } - else if (option == 'd') - { + } else if (option == 'd') { soak_options.packet_duplication = atof(cag_option_get_value(&context)); - } - else if (option == 'p') - { + } else if (option == 'p') { soak_options.ping = atof(cag_option_get_value(&context)); - } - else if (option == 'j') - { + } else if (option == 'j') { soak_options.jitter = atof(cag_option_get_value(&context)); } } - if (soak_options.channel_count <= 0) - { - Usage(); - return -1; - } - - if (soak_options.channel_count > NBN_MAX_CHANNELS - NBN_LIBRARY_RESERVED_CHANNELS) - { - Soak_LogError("Channel count cannot exceed %d", NBN_MAX_CHANNELS - NBN_LIBRARY_RESERVED_CHANNELS); - return -1; - } - #ifdef SOAK_CLIENT - if (soak_options.message_count <= 0) - { + if (soak_options.message_count <= 0) { Usage(); return -1; } #endif - if (soak_options.channel_count > SOAK_MAX_CHANNELS) - { - Soak_LogError("Too many channels (max: %d)", SOAK_MAX_CHANNELS); - return -1; - } - return 0; } -int Soak_MainLoop(int (*Tick)(void *), void *data) -{ - while (running) - { +int Soak_MainLoop(int (*Tick)(void *), void *data) { + while (running) { int ret = Tick(data); if (ret < 0) // Error @@ -238,7 +142,7 @@ int Soak_MainLoop(int (*Tick)(void *), void *data) Sleep(SOAK_TICK_DT * 1000); #else long nanos = SOAK_TICK_DT * 1e9; - struct timespec t = { .tv_sec = nanos / 999999999, .tv_nsec = nanos % 999999999 }; + struct timespec t = {.tv_sec = nanos / 999999999, .tv_nsec = nanos % 999999999}; nanosleep(&t, &t); #endif @@ -247,102 +151,35 @@ int Soak_MainLoop(int (*Tick)(void *), void *data) return 0; } -void Soak_Stop(void) -{ +void Soak_Stop(void) { running = false; - Soak_LogInfo("Soak test stopped"); -} - -SoakOptions Soak_GetOptions(void) -{ - return soak_options; -} - -void Soak_Debug_PrintAddedToRecvQueue(NBN_Connection *conn, NBN_Message *msg) -{ - (void) conn; - (void) msg; - // FIXME - /*if (msg->header.type == NBN_MESSAGE_CHUNK_TYPE) - { - NBN_MessageChunk *chunk = (NBN_MessageChunk *)msg->data; - - Soak_LogDebug("Soak message chunk added to recv queue (chunk id: %d, chunk total: %d)", - chunk->id, chunk->total); - } - else - { - SoakMessage *soak_message = (SoakMessage *)msg->data; - - Soak_LogDebug("Soak message added to recv queue (conn id: %d, msg id: %d, soak msg id: %d)", - conn->id, msg->header.id, soak_message->id); - }*/ -} - -unsigned int Soak_GetCreatedOutgoingSoakMessageCount(void) -{ - return created_outgoing_soak_message_count; -} - -unsigned int Soak_GetDestroyedOutgoingSoakMessageCount(void) -{ - return destroyed_outgoing_soak_message_count; -} - -unsigned int Soak_GetCreatedIncomingSoakMessageCount(void) -{ - return created_incoming_soak_message_count; -} - -unsigned int Soak_GetDestroyedIncomingSoakMessageCount(void) -{ - return destroyed_incoming_soak_message_count; -} - -SoakMessage *SoakMessage_CreateIncoming(void) -{ - SoakMessage *msg = (SoakMessage *)malloc(sizeof(SoakMessage)); - - msg->outgoing = false; - - created_incoming_soak_message_count++; - - return msg; + log_info("Soak test stopped"); } -SoakMessage *SoakMessage_CreateOutgoing(void) -{ - SoakMessage *msg = (SoakMessage *)malloc(sizeof(SoakMessage)); +SoakOptions Soak_GetOptions(void) { return soak_options; } - msg->outgoing = true; +unsigned int Soak_GetCreatedOutgoingSoakMessageCount(void) { return created_outgoing_soak_message_count; } - created_outgoing_soak_message_count++; +unsigned int Soak_GetDestroyedOutgoingSoakMessageCount(void) { return destroyed_outgoing_soak_message_count; } - return msg; -} +unsigned int Soak_GetCreatedIncomingSoakMessageCount(void) { return created_incoming_soak_message_count; } -void SoakMessage_Destroy(SoakMessage *msg) -{ - if (msg->outgoing) - { - destroyed_outgoing_soak_message_count++; - Soak_LogDebug("Destroying outgoing soak message (destroyed count: %d, created count: %d)", - destroyed_outgoing_soak_message_count, created_outgoing_soak_message_count); - } - else - { - destroyed_incoming_soak_message_count++; - } +unsigned int Soak_GetDestroyedIncomingSoakMessageCount(void) { return destroyed_incoming_soak_message_count; } - free(msg); +void SoakMessage_Write(NBN_Writer *writer, unsigned int msg_id, uint8_t *data, unsigned int data_length) { + NBN_Writer_WriteUInt32(writer, msg_id); + NBN_Writer_WriteUInt32(writer, data_length); + NBN_Writer_WriteBytes(writer, data, data_length); } -int SoakMessage_Serialize(SoakMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt(stream, msg->id, 0, UINT32_MAX); - NBN_SerializeUInt(stream, msg->data_length, 1, SOAK_MESSAGE_MAX_DATA_LENGTH); - NBN_SerializeBytes(stream, msg->data, msg->data_length); +int SoakMessage_Read(NBN_Reader *reader, unsigned int *msg_id, uint8_t *data, unsigned int *data_length) { + if (NBN_Reader_ReadUInt32(reader, msg_id) < 0) + return -1; + if (NBN_Reader_ReadUInt32(reader, data_length) < 0) + return -1; + if (NBN_Reader_ReadBytes(reader, data, *data_length) < 0) + return -1; return 0; } diff --git a/soak/soak.h b/soak/soak.h index c37a947..68c3656 100644 --- a/soak/soak.h +++ b/soak/soak.h @@ -27,73 +27,56 @@ #if defined(_WIN32) || defined(_WIN64) -#include #include +#include #endif -#include #include - -#include "logging.h" - -/* nbnet logging */ -#define NBN_LogInfo Soak_LogInfo -#define NBN_LogTrace Soak_LogTrace -#define NBN_LogDebug Soak_LogDebug -#define NBN_LogError Soak_LogError -#define NBN_LogWarning Soak_LogWarn - +#include #include "../nbnet.h" #define SOAK_PROTOCOL_NAME "nbnet_soak" -#define SOAK_PORT 42043 +#define SOAK_PORT 42044 #define SOAK_TICK_RATE 60 #define SOAK_TICK_DT (1.0 / SOAK_TICK_RATE) -#define SOAK_MESSAGE_MIN_DATA_LENGTH 50 -#define SOAK_MESSAGE_MAX_DATA_LENGTH 4096 -#define SOAK_BIG_MESSAGE_PERCENTAGE 25 -#define SOAK_MESSAGE 42 +#define SOAK_MESSAGE_HEADER_LENGTH 8 // 4 bytes for ID, 4 bytes for data length +#define SOAK_MESSAGE_SMALL_MIN_DATA_LENGTH 50 +#define SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH 200 +#define SOAK_MESSAGE_BIG_MIN_DATA_LENGTH 1024 +#define SOAK_MESSAGE_BIG_MAX_DATA_LENGTH 4096 +#define SOAK_MESSAGE_SMALL_MAX_LENGTH (SOAK_MESSAGE_HEADER_LENGTH + SOAK_MESSAGE_SMALL_MAX_DATA_LENGTH) +#define SOAK_MESSAGE_BIG_MAX_LENGTH (SOAK_MESSAGE_HEADER_LENGTH + SOAK_MESSAGE_BIG_MAX_DATA_LENGTH) +#define SOAK_BIG_MESSAGE_PERCENTAGE 0 // TODO: chunks are currently unsupported +#define SOAK_MESSAGE_SMALL 42 +#define SOAK_MESSAGE_BIG 43 // may get chunked #define SOAK_SEED time(NULL) #define SOAK_DONE 1 -#define SOAK_MAX_CLIENTS 256 #define SOAK_CLIENT_MAX_PENDING_MESSAGES 50 // max number of unacked messages at a time -#define SOAK_SERVER_FULL_CODE 42 -#define SOAK_MAX_CHANNELS (NBN_MAX_CHANNELS - 3) +#define SOAK_SERVER_FULL_CODE 1234 +#define SOAK_MAX_MESSAGE_SIZE 256 +#define SOAK_CHANNEL_COUNT 4 +#define SOAK_CHANNEL_BUFFER_SIZE 128 -typedef struct -{ +typedef struct { unsigned int message_count; - unsigned int channel_count; - float packet_loss; /* 0 - 1 */ + float packet_loss; /* 0 - 1 */ float packet_duplication; /* 0 - 1 */ - float ping; /* in seconds */ - float jitter; /* in seconds */ - bool webrtc; /* use native WebRTC driver */ + float ping; /* in seconds */ + float jitter; /* in seconds */ + bool webrtc; /* use native WebRTC driver */ } SoakOptions; -typedef struct -{ - uint32_t id; - unsigned int data_length; - bool outgoing; - uint8_t data[SOAK_MESSAGE_MAX_DATA_LENGTH]; -} SoakMessage; - int Soak_Init(int, char *[]); -void Soak_Deinit(void); int Soak_ReadCommandLine(int, char *[]); int Soak_MainLoop(int (*Tick)(void *), void *data); void Soak_Stop(void); SoakOptions Soak_GetOptions(void); -void Soak_Debug_PrintAddedToRecvQueue(NBN_Connection *, NBN_Message *); unsigned int Soak_GetCreatedOutgoingSoakMessageCount(void); unsigned int Soak_GetDestroyedOutgoingSoakMessageCount(void); unsigned int Soak_GetCreatedIncomingSoakMessageCount(void); unsigned int Soak_GetDestroyedIncomingSoakMessageCount(void); -SoakMessage *SoakMessage_CreateOutgoing(void); -SoakMessage *SoakMessage_CreateIncoming(void); -void SoakMessage_Destroy(SoakMessage *); -int SoakMessage_Serialize(SoakMessage *, NBN_Stream *); +void SoakMessage_Write(NBN_Writer *, unsigned int, uint8_t *, unsigned int); +int SoakMessage_Read(NBN_Reader *reader, unsigned int *msg_id, uint8_t *data, unsigned int *data_length); #endif // SOAK_H_INCLUDED diff --git a/stb_ds.h b/stb_ds.h new file mode 100644 index 0000000..e84c82d --- /dev/null +++ b/stb_ds.h @@ -0,0 +1,1895 @@ +/* stb_ds.h - v0.67 - public domain data structures - Sean Barrett 2019 + + This is a single-header-file library that provides easy-to-use + dynamic arrays and hash tables for C (also works in C++). + + For a gentle introduction: + http://nothings.org/stb_ds + + To use this library, do this in *one* C or C++ file: + #define STB_DS_IMPLEMENTATION + #include "stb_ds.h" + +TABLE OF CONTENTS + + Table of Contents + Compile-time options + License + Documentation + Notes + Notes - Dynamic arrays + Notes - Hash maps + Credits + +COMPILE-TIME OPTIONS + + #define STBDS_NO_SHORT_NAMES + + This flag needs to be set globally. + + By default stb_ds exposes shorter function names that are not qualified + with the "stbds_" prefix. If these names conflict with the names in your + code, define this flag. + + #define STBDS_SIPHASH_2_4 + + This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION. + + By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for + 4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force + stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes + hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on + 64-byte keys, and 10% slower on 256-byte keys on my test computer. + + #define STBDS_REALLOC(context,ptr,size) better_realloc + #define STBDS_FREE(context,ptr) better_free + + These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION. + + By default stb_ds uses stdlib realloc() and free() for memory management. You can + substitute your own functions instead by defining these symbols. You must either + define both, or neither. Note that at the moment, 'context' will always be NULL. + @TODO add an array/hash initialization function that takes a memory context pointer. + + #define STBDS_UNIT_TESTS + + Defines a function stbds_unit_tests() that checks the functioning of the data structures. + + Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x' + (or equivalentally '-std=c++11') when using anonymous structures as seen on the web + page or in STBDS_UNIT_TESTS. + +LICENSE + + Placed in the public domain and also MIT licensed. + See end of file for detailed license information. + +DOCUMENTATION + + Dynamic Arrays + + Non-function interface: + + Declare an empty dynamic array of type T + T* foo = NULL; + + Access the i'th item of a dynamic array 'foo' of type T, T* foo: + foo[i] + + Functions (actually macros) + + arrfree: + void arrfree(T*); + Frees the array. + + arrlen: + ptrdiff_t arrlen(T*); + Returns the number of elements in the array. + + arrlenu: + size_t arrlenu(T*); + Returns the number of elements in the array as an unsigned type. + + arrpop: + T arrpop(T* a) + Removes the final element of the array and returns it. + + arrput: + T arrput(T* a, T b); + Appends the item b to the end of array a. Returns b. + + arrins: + T arrins(T* a, int p, T b); + Inserts the item b into the middle of array a, into a[p], + moving the rest of the array over. Returns b. + + arrinsn: + void arrinsn(T* a, int p, int n); + Inserts n uninitialized items into array a starting at a[p], + moving the rest of the array over. + + arraddnptr: + T* arraddnptr(T* a, int n) + Appends n uninitialized items onto array at the end. + Returns a pointer to the first uninitialized item added. + + arraddnindex: + size_t arraddnindex(T* a, int n) + Appends n uninitialized items onto array at the end. + Returns the index of the first uninitialized item added. + + arrdel: + void arrdel(T* a, int p); + Deletes the element at a[p], moving the rest of the array over. + + arrdeln: + void arrdeln(T* a, int p, int n); + Deletes n elements starting at a[p], moving the rest of the array over. + + arrdelswap: + void arrdelswap(T* a, int p); + Deletes the element at a[p], replacing it with the element from + the end of the array. O(1) performance. + + arrsetlen: + void arrsetlen(T* a, int n); + Changes the length of the array to n. Allocates uninitialized + slots at the end if necessary. + + arrsetcap: + size_t arrsetcap(T* a, int n); + Sets the length of allocated storage to at least n. It will not + change the length of the array. + + arrcap: + size_t arrcap(T* a); + Returns the number of total elements the array can contain without + needing to be reallocated. + + Hash maps & String hash maps + + Given T is a structure type: struct { TK key; TV value; }. Note that some + functions do not require TV value and can have other fields. For string + hash maps, TK must be 'char *'. + + Special interface: + + stbds_rand_seed: + void stbds_rand_seed(size_t seed); + For security against adversarially chosen data, you should seed the + library with a strong random number. Or at least seed it with time(). + + stbds_hash_string: + size_t stbds_hash_string(char *str, size_t seed); + Returns a hash value for a string. + + stbds_hash_bytes: + size_t stbds_hash_bytes(void *p, size_t len, size_t seed); + These functions hash an arbitrary number of bytes. The function + uses a custom hash for 4- and 8-byte data, and a weakened version + of SipHash for everything else. On 64-bit platforms you can get + specification-compliant SipHash-2-4 on all data by defining + STBDS_SIPHASH_2_4, at a significant cost in speed. + + Non-function interface: + + Declare an empty hash map of type T + T* foo = NULL; + + Access the i'th entry in a hash table T* foo: + foo[i] + + Function interface (actually macros): + + hmfree + shfree + void hmfree(T*); + void shfree(T*); + Frees the hashmap and sets the pointer to NULL. + + hmlen + shlen + ptrdiff_t hmlen(T*) + ptrdiff_t shlen(T*) + Returns the number of elements in the hashmap. + + hmlenu + shlenu + size_t hmlenu(T*) + size_t shlenu(T*) + Returns the number of elements in the hashmap. + + hmgeti + shgeti + hmgeti_ts + ptrdiff_t hmgeti(T*, TK key) + ptrdiff_t shgeti(T*, char* key) + ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar) + Returns the index in the hashmap which has the key 'key', or -1 + if the key is not present. + + hmget + hmget_ts + shget + TV hmget(T*, TK key) + TV shget(T*, char* key) + TV hmget_ts(T*, TK key, ptrdiff_t tempvar) + Returns the value corresponding to 'key' in the hashmap. + The structure must have a 'value' field + + hmgets + shgets + T hmgets(T*, TK key) + T shgets(T*, char* key) + Returns the structure corresponding to 'key' in the hashmap. + + hmgetp + shgetp + hmgetp_ts + hmgetp_null + shgetp_null + T* hmgetp(T*, TK key) + T* shgetp(T*, char* key) + T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar) + T* hmgetp_null(T*, TK key) + T* shgetp_null(T*, char *key) + Returns a pointer to the structure corresponding to 'key' in + the hashmap. Functions ending in "_null" return NULL if the key + is not present in the hashmap; the others return a pointer to a + structure holding the default value (but not the searched-for key). + + hmdefault + shdefault + TV hmdefault(T*, TV value) + TV shdefault(T*, TV value) + Sets the default value for the hashmap, the value which will be + returned by hmget/shget if the key is not present. + + hmdefaults + shdefaults + TV hmdefaults(T*, T item) + TV shdefaults(T*, T item) + Sets the default struct for the hashmap, the contents which will be + returned by hmgets/shgets if the key is not present. + + hmput + shput + TV hmput(T*, TK key, TV value) + TV shput(T*, char* key, TV value) + Inserts a pair into the hashmap. If the key is already + present in the hashmap, updates its value. + + hmputs + shputs + T hmputs(T*, T item) + T shputs(T*, T item) + Inserts a struct with T.key into the hashmap. If the struct is already + present in the hashmap, updates it. + + hmdel + shdel + int hmdel(T*, TK key) + int shdel(T*, char* key) + If 'key' is in the hashmap, deletes its entry and returns 1. + Otherwise returns 0. + + Function interface (actually macros) for strings only: + + sh_new_strdup + void sh_new_strdup(T*); + Overwrites the existing pointer with a newly allocated + string hashmap which will automatically allocate and free + each string key using realloc/free + + sh_new_arena + void sh_new_arena(T*); + Overwrites the existing pointer with a newly allocated + string hashmap which will automatically allocate each string + key to a string arena. Every string key ever used by this + hash table remains in the arena until the arena is freed. + Additionally, any key which is deleted and reinserted will + be allocated multiple times in the string arena. + +NOTES + + * These data structures are realloc'd when they grow, and the macro + "functions" write to the provided pointer. This means: (a) the pointer + must be an lvalue, and (b) the pointer to the data structure is not + stable, and you must maintain it the same as you would a realloc'd + pointer. For example, if you pass a pointer to a dynamic array to a + function which updates it, the function must return back the new + pointer to the caller. This is the price of trying to do this in C. + + * The following are the only functions that are thread-safe on a single data + structure, i.e. can be run in multiple threads simultaneously on the same + data structure + hmlen shlen + hmlenu shlenu + hmget_ts shget_ts + hmgeti_ts shgeti_ts + hmgets_ts shgets_ts + + * You iterate over the contents of a dynamic array and a hashmap in exactly + the same way, using arrlen/hmlen/shlen: + + for (i=0; i < arrlen(foo); ++i) + ... foo[i] ... + + * All operations except arrins/arrdel are O(1) amortized, but individual + operations can be slow, so these data structures may not be suitable + for real time use. Dynamic arrays double in capacity as needed, so + elements are copied an average of once. Hash tables double/halve + their size as needed, with appropriate hysteresis to maintain O(1) + performance. + +NOTES - DYNAMIC ARRAY + + * If you know how long a dynamic array is going to be in advance, you can avoid + extra memory allocations by using arrsetlen to allocate it to that length in + advance and use foo[n] while filling it out, or arrsetcap to allocate the memory + for that length and use arrput/arrpush as normal. + + * Unlike some other versions of the dynamic array, this version should + be safe to use with strict-aliasing optimizations. + +NOTES - HASH MAP + + * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel + and variants, the key must be an lvalue (so the macro can take the address of it). + Extensions are used that eliminate this requirement if you're using C99 and later + in GCC or clang, or if you're using C++ in GCC. But note that this can make your + code less portable. + + * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'. + + * The iteration order of your data in the hashmap is determined solely by the + order of insertions and deletions. In particular, if you never delete, new + keys are always added at the end of the array. This will be consistent + across all platforms and versions of the library. However, you should not + attempt to serialize the internal hash table, as the hash is not consistent + between different platforms, and may change with future versions of the library. + + * Use sh_new_arena() for string hashmaps that you never delete from. Initialize + with NULL if you're managing the memory for your strings, or your strings are + never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup(). + @TODO: make an arena variant that garbage collects the strings with a trivial + copy collector into a new arena whenever the table shrinks / rebuilds. Since + current arena recommendation is to only use arena if it never deletes, then + this can just replace current arena implementation. + + * If adversarial input is a serious concern and you're on a 64-bit platform, + enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass + a strong random number to stbds_rand_seed. + + * The default value for the hash table is stored in foo[-1], so if you + use code like 'hmget(T,k)->value = 5' you can accidentally overwrite + the value stored by hmdefault if 'k' is not present. + +CREDITS + + Sean Barrett -- library, idea for dynamic array API/implementation + Per Vognsen -- idea for hash table API/implementation + Rafael Sachetto -- arrpop() + github:HeroicKatora -- arraddn() reworking + + Bugfixes: + Andy Durdin + Shane Liesegang + Vinh Truong + Andreas Molzer + github:hashitaku + github:srdjanstipic + Macoy Madson + Andreas Vennstrom + Tobias Mansfield-Williams +*/ + +#ifdef STBDS_UNIT_TESTS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef INCLUDE_STB_DS_H +#define INCLUDE_STB_DS_H + +#include +#include + +#ifndef STBDS_NO_SHORT_NAMES +#define arrlen stbds_arrlen +#define arrlenu stbds_arrlenu +#define arrput stbds_arrput +#define arrpush stbds_arrput +#define arrpop stbds_arrpop +#define arrfree stbds_arrfree +#define arraddn stbds_arraddn // deprecated, use one of the following instead: +#define arraddnptr stbds_arraddnptr +#define arraddnindex stbds_arraddnindex +#define arrsetlen stbds_arrsetlen +#define arrlast stbds_arrlast +#define arrins stbds_arrins +#define arrinsn stbds_arrinsn +#define arrdel stbds_arrdel +#define arrdeln stbds_arrdeln +#define arrdelswap stbds_arrdelswap +#define arrcap stbds_arrcap +#define arrsetcap stbds_arrsetcap + +#define hmput stbds_hmput +#define hmputs stbds_hmputs +#define hmget stbds_hmget +#define hmget_ts stbds_hmget_ts +#define hmgets stbds_hmgets +#define hmgetp stbds_hmgetp +#define hmgetp_ts stbds_hmgetp_ts +#define hmgetp_null stbds_hmgetp_null +#define hmgeti stbds_hmgeti +#define hmgeti_ts stbds_hmgeti_ts +#define hmdel stbds_hmdel +#define hmlen stbds_hmlen +#define hmlenu stbds_hmlenu +#define hmfree stbds_hmfree +#define hmdefault stbds_hmdefault +#define hmdefaults stbds_hmdefaults + +#define shput stbds_shput +#define shputi stbds_shputi +#define shputs stbds_shputs +#define shget stbds_shget +#define shgeti stbds_shgeti +#define shgets stbds_shgets +#define shgetp stbds_shgetp +#define shgetp_null stbds_shgetp_null +#define shdel stbds_shdel +#define shlen stbds_shlen +#define shlenu stbds_shlenu +#define shfree stbds_shfree +#define shdefault stbds_shdefault +#define shdefaults stbds_shdefaults +#define sh_new_arena stbds_sh_new_arena +#define sh_new_strdup stbds_sh_new_strdup + +#define stralloc stbds_stralloc +#define strreset stbds_strreset +#endif + +#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE) +#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither." +#endif +#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE) +#include +#define STBDS_REALLOC(c,p,s) realloc(p,s) +#define STBDS_FREE(c,p) free(p) +#endif + +#ifdef _MSC_VER +#define STBDS_NOTUSED(v) (void)(v) +#else +#define STBDS_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// for security against attackers, seed the library with a random number, at least time() but stronger is better +extern void stbds_rand_seed(size_t seed); + +// these are the hash functions used internally if you want to test them or use them for other purposes +extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed); +extern size_t stbds_hash_string(char *str, size_t seed); + +// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'. +typedef struct stbds_string_arena stbds_string_arena; +extern char * stbds_stralloc(stbds_string_arena *a, char *str); +extern void stbds_strreset(stbds_string_arena *a); + +// have to #define STBDS_UNIT_TESTS to call this +extern void stbds_unit_tests(void); + +/////////////// +// +// Everything below here is implementation details +// + +extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap); +extern void stbds_arrfreef(void *a); +extern void stbds_hmfree_func(void *p, size_t elemsize); +extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode); +extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode); +extern void * stbds_hmput_default(void *a, size_t elemsize); +extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode); +extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode); +extern void * stbds_shmode_func(size_t elemsize, int mode); + +#ifdef __cplusplus +} +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define STBDS_HAS_TYPEOF +#ifdef __cplusplus +//#define STBDS_HAS_LITERAL_ARRAY // this is currently broken for clang +#endif +#endif + +#if !defined(__cplusplus) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define STBDS_HAS_LITERAL_ARRAY +#endif +#endif + +// this macro takes the address of the argument, but on gcc/clang can accept rvalues +#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF) + #if __clang__ + #define STBDS_ADDRESSOF(typevar, value) ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value + #else + #define STBDS_ADDRESSOF(typevar, value) ((typeof(typevar)[1]){value}) // literal array decays to pointer to value + #endif +#else +#define STBDS_ADDRESSOF(typevar, value) &(value) +#endif + +#define STBDS_OFFSETOF(var,field) ((char *) &(var)->field - (char *) (var)) + +#define stbds_header(t) ((stbds_array_header *) (t) - 1) +#define stbds_temp(t) stbds_header(t)->temp +#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table) + +#define stbds_arrsetcap(a,n) (stbds_arrgrow(a,0,n)) +#define stbds_arrsetlen(a,n) ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0) +#define stbds_arrcap(a) ((a) ? stbds_header(a)->capacity : 0) +#define stbds_arrlen(a) ((a) ? (ptrdiff_t) stbds_header(a)->length : 0) +#define stbds_arrlenu(a) ((a) ? stbds_header(a)->length : 0) +#define stbds_arrput(a,v) (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v)) +#define stbds_arrpush stbds_arrput // synonym +#define stbds_arrpop(a) (stbds_header(a)->length--, (a)[stbds_header(a)->length]) +#define stbds_arraddn(a,n) ((void)(stbds_arraddnindex(a, n))) // deprecated, use one of the following instead: +#define stbds_arraddnptr(a,n) (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a)) +#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a)) +#define stbds_arraddnoff stbds_arraddnindex +#define stbds_arrlast(a) ((a)[stbds_header(a)->length-1]) +#define stbds_arrfree(a) ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL) +#define stbds_arrdel(a,i) stbds_arrdeln(a,i,1) +#define stbds_arrdeln(a,i,n) (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n)) +#define stbds_arrdelswap(a,i) ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1) +#define stbds_arrinsn(a,i,n) (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i)))) +#define stbds_arrins(a,i,v) (stbds_arrinsn((a),(i),1), (a)[i]=(v)) + +#define stbds_arrmaybegrow(a,n) ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \ + ? (stbds_arrgrow(a,n,0),0) : 0) + +#define stbds_arrgrow(a,b,c) ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c))) + +#define stbds_hmput(t, k, v) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0), \ + (t)[stbds_temp((t)-1)].key = (k), \ + (t)[stbds_temp((t)-1)].value = (v)) + +#define stbds_hmputs(t, s) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \ + (t)[stbds_temp((t)-1)] = (s)) + +#define stbds_hmgeti(t,k) \ + ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \ + stbds_temp((t)-1)) + +#define stbds_hmgeti_ts(t,k,temp) \ + ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \ + (temp)) + +#define stbds_hmgetp(t, k) \ + ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)]) + +#define stbds_hmgetp_ts(t, k, temp) \ + ((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp]) + +#define stbds_hmdel(t,k) \ + (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0) + +#define stbds_hmdefault(t, v) \ + ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v)) + +#define stbds_hmdefaults(t, s) \ + ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s)) + +#define stbds_hmfree(p) \ + ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL) + +#define stbds_hmgets(t, k) (*stbds_hmgetp(t,k)) +#define stbds_hmget(t, k) (stbds_hmgetp(t,k)->value) +#define stbds_hmget_ts(t, k, temp) (stbds_hmgetp_ts(t,k,temp)->value) +#define stbds_hmlen(t) ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0) +#define stbds_hmlenu(t) ((t) ? stbds_header((t)-1)->length-1 : 0) +#define stbds_hmgetp_null(t,k) (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)]) + +#define stbds_shput(t, k, v) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ + (t)[stbds_temp((t)-1)].value = (v)) + +#define stbds_shputi(t, k, v) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ + (t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1)) + +#define stbds_shputs(t, s) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \ + (t)[stbds_temp((t)-1)] = (s), \ + (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally + +#define stbds_pshput(t, p) \ + ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \ + (t)[stbds_temp((t)-1)] = (p)) + +#define stbds_shgeti(t,k) \ + ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ + stbds_temp((t)-1)) + +#define stbds_pshgeti(t,k) \ + ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \ + stbds_temp((t)-1)) + +#define stbds_shgetp(t, k) \ + ((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)]) + +#define stbds_pshget(t, k) \ + ((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)]) + +#define stbds_shdel(t,k) \ + (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0) +#define stbds_pshdel(t,k) \ + (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0) + +#define stbds_sh_new_arena(t) \ + ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA)) +#define stbds_sh_new_strdup(t) \ + ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP)) + +#define stbds_shdefault(t, v) stbds_hmdefault(t,v) +#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s) + +#define stbds_shfree stbds_hmfree +#define stbds_shlenu stbds_hmlenu + +#define stbds_shgets(t, k) (*stbds_shgetp(t,k)) +#define stbds_shget(t, k) (stbds_shgetp(t,k)->value) +#define stbds_shgetp_null(t,k) (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)]) +#define stbds_shlen stbds_hmlen + +typedef struct +{ + size_t length; + size_t capacity; + void * hash_table; + ptrdiff_t temp; +} stbds_array_header; + +typedef struct stbds_string_block +{ + struct stbds_string_block *next; + char storage[8]; +} stbds_string_block; + +struct stbds_string_arena +{ + stbds_string_block *storage; + size_t remaining; + unsigned char block; + unsigned char mode; // this isn't used by the string arena itself +}; + +#define STBDS_HM_BINARY 0 +#define STBDS_HM_STRING 1 + +enum +{ + STBDS_SH_NONE, + STBDS_SH_DEFAULT, + STBDS_SH_STRDUP, + STBDS_SH_ARENA +}; + +#ifdef __cplusplus +// in C we use implicit assignment from these void*-returning functions to T*. +// in C++ these templates make the same code work +template static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) { + return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap); +} +template static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) { + return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode); +} +template static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) { + return (T*)stbds_hmget_key_ts((void*)a, elemsize, key, keysize, temp, mode); +} +template static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) { + return (T*)stbds_hmput_default((void *)a, elemsize); +} +template static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) { + return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode); +} +template static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){ + return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode); +} +template static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) { + return (T*)stbds_shmode_func(elemsize, mode); +} +#else +#define stbds_arrgrowf_wrapper stbds_arrgrowf +#define stbds_hmget_key_wrapper stbds_hmget_key +#define stbds_hmget_key_ts_wrapper stbds_hmget_key_ts +#define stbds_hmput_default_wrapper stbds_hmput_default +#define stbds_hmput_key_wrapper stbds_hmput_key +#define stbds_hmdel_key_wrapper stbds_hmdel_key +#define stbds_shmode_func_wrapper(t,e,m) stbds_shmode_func(e,m) +#endif + +#endif // INCLUDE_STB_DS_H + + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// + +#ifdef STB_DS_IMPLEMENTATION +#include +#include + +#ifndef STBDS_ASSERT +#define STBDS_ASSERT_WAS_UNDEFINED +#define STBDS_ASSERT(x) ((void) 0) +#endif + +#ifdef STBDS_STATISTICS +#define STBDS_STATS(x) x +size_t stbds_array_grow; +size_t stbds_hash_grow; +size_t stbds_hash_shrink; +size_t stbds_hash_rebuild; +size_t stbds_hash_probes; +size_t stbds_hash_alloc; +size_t stbds_rehash_probes; +size_t stbds_rehash_items; +#else +#define STBDS_STATS(x) +#endif + +// +// stbds_arr implementation +// + +//int *prev_allocs[65536]; +//int num_prev; + +void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) +{ + stbds_array_header temp={0}; // force debugging + void *b; + size_t min_len = stbds_arrlen(a) + addlen; + (void) sizeof(temp); + + // compute the minimum capacity needed + if (min_len > min_cap) + min_cap = min_len; + + if (min_cap <= stbds_arrcap(a)) + return a; + + // increase needed capacity to guarantee O(1) amortized + if (min_cap < 2 * stbds_arrcap(a)) + min_cap = 2 * stbds_arrcap(a); + else if (min_cap < 4) + min_cap = 4; + + //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1); + //if (num_prev == 2201) + // num_prev = num_prev; + b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header)); + //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b; + b = (char *) b + sizeof(stbds_array_header); + if (a == NULL) { + stbds_header(b)->length = 0; + stbds_header(b)->hash_table = 0; + stbds_header(b)->temp = 0; + } else { + STBDS_STATS(++stbds_array_grow); + } + stbds_header(b)->capacity = min_cap; + + return b; +} + +void stbds_arrfreef(void *a) +{ + STBDS_FREE(NULL, stbds_header(a)); +} + +// +// stbds_hm hash table implementation +// + +#ifdef STBDS_INTERNAL_SMALL_BUCKET +#define STBDS_BUCKET_LENGTH 4 +#else +#define STBDS_BUCKET_LENGTH 8 +#endif + +#define STBDS_BUCKET_SHIFT (STBDS_BUCKET_LENGTH == 8 ? 3 : 2) +#define STBDS_BUCKET_MASK (STBDS_BUCKET_LENGTH-1) +#define STBDS_CACHE_LINE_SIZE 64 + +#define STBDS_ALIGN_FWD(n,a) (((n) + (a) - 1) & ~((a)-1)) + +typedef struct +{ + size_t hash [STBDS_BUCKET_LENGTH]; + ptrdiff_t index[STBDS_BUCKET_LENGTH]; +} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line + +typedef struct +{ + char * temp_key; // this MUST be the first field of the hash table + size_t slot_count; + size_t used_count; + size_t used_count_threshold; + size_t used_count_shrink_threshold; + size_t tombstone_count; + size_t tombstone_count_threshold; + size_t seed; + size_t slot_count_log2; + stbds_string_arena string; + stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct +} stbds_hash_index; + +#define STBDS_INDEX_EMPTY -1 +#define STBDS_INDEX_DELETED -2 +#define STBDS_INDEX_IN_USE(x) ((x) >= 0) + +#define STBDS_HASH_EMPTY 0 +#define STBDS_HASH_DELETED 1 + +static size_t stbds_hash_seed=0x31415926; + +void stbds_rand_seed(size_t seed) +{ + stbds_hash_seed = seed; +} + +#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo) \ + temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */ \ + var = v64_hi, var <<= 16, var <<= 16, /* discard if 32-bit */ \ + var ^= temp ^ v32 + +#define STBDS_SIZE_T_BITS ((sizeof (size_t)) * 8) + +static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2) +{ + size_t pos; + STBDS_NOTUSED(slot_log2); + pos = hash & (slot_count-1); + #ifdef STBDS_INTERNAL_BUCKET_START + pos &= ~STBDS_BUCKET_MASK; + #endif + return pos; +} + +static size_t stbds_log2(size_t slot_count) +{ + size_t n=0; + while (slot_count > 1) { + slot_count >>= 1; + ++n; + } + return n; +} + +static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot) +{ + stbds_hash_index *t; + t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1); + t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE); + t->slot_count = slot_count; + t->slot_count_log2 = stbds_log2(slot_count); + t->tombstone_count = 0; + t->used_count = 0; + + #if 0 // A1 + t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow + t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild + t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink + #elif 1 // A2 + //t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow + //t->tombstone_count_threshold = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild + //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink + + // compute without overflowing + t->used_count_threshold = slot_count - (slot_count>>2); + t->tombstone_count_threshold = (slot_count>>3) + (slot_count>>4); + t->used_count_shrink_threshold = slot_count >> 2; + + #elif 0 // B1 + t->used_count_threshold = slot_count*13/16; // if 13/16th of table is occupied, grow + t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild + t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink + #else // C1 + t->used_count_threshold = slot_count*14/16; // if 14/16th of table is occupied, grow + t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild + t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink + #endif + // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2 + // Note that the larger tables have high variance as they were run fewer times + // A1 A2 B1 C1 + // 0.10ms : 0.10ms : 0.10ms : 0.11ms : 2,000 inserts creating 2K table + // 0.96ms : 0.95ms : 0.97ms : 1.04ms : 20,000 inserts creating 20K table + // 14.48ms : 14.46ms : 10.63ms : 11.00ms : 200,000 inserts creating 200K table + // 195.74ms : 196.35ms : 203.69ms : 214.92ms : 2,000,000 inserts creating 2M table + // 2193.88ms : 2209.22ms : 2285.54ms : 2437.17ms : 20,000,000 inserts creating 20M table + // 65.27ms : 53.77ms : 65.33ms : 65.47ms : 500,000 inserts & deletes in 2K table + // 72.78ms : 62.45ms : 71.95ms : 72.85ms : 500,000 inserts & deletes in 20K table + // 89.47ms : 77.72ms : 96.49ms : 96.75ms : 500,000 inserts & deletes in 200K table + // 97.58ms : 98.14ms : 97.18ms : 97.53ms : 500,000 inserts & deletes in 2M table + // 118.61ms : 119.62ms : 120.16ms : 118.86ms : 500,000 inserts & deletes in 20M table + // 192.11ms : 194.39ms : 196.38ms : 195.73ms : 500,000 inserts & deletes in 200M table + + if (slot_count <= STBDS_BUCKET_LENGTH) + t->used_count_shrink_threshold = 0; + // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes + STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count); + STBDS_STATS(++stbds_hash_alloc); + if (ot) { + t->string = ot->string; + // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing + t->seed = ot->seed; + } else { + size_t a,b,temp; + memset(&t->string, 0, sizeof(t->string)); + t->seed = stbds_hash_seed; + // LCG + // in 32-bit, a = 2147001325 b = 715136305 + // in 64-bit, a = 2862933555777941757 b = 3037000493 + stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd); + stbds_load_32_or_64(b,temp, 715136305, 0, 0xb504f32d); + stbds_hash_seed = stbds_hash_seed * a + b; + } + + { + size_t i,j; + for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) { + stbds_hash_bucket *b = &t->storage[i]; + for (j=0; j < STBDS_BUCKET_LENGTH; ++j) + b->hash[j] = STBDS_HASH_EMPTY; + for (j=0; j < STBDS_BUCKET_LENGTH; ++j) + b->index[j] = STBDS_INDEX_EMPTY; + } + } + + // copy out the old data, if any + if (ot) { + size_t i,j; + t->used_count = ot->used_count; + for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) { + stbds_hash_bucket *ob = &ot->storage[i]; + for (j=0; j < STBDS_BUCKET_LENGTH; ++j) { + if (STBDS_INDEX_IN_USE(ob->index[j])) { + size_t hash = ob->hash[j]; + size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2); + size_t step = STBDS_BUCKET_LENGTH; + STBDS_STATS(++stbds_rehash_items); + for (;;) { + size_t limit,z; + stbds_hash_bucket *bucket; + bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT]; + STBDS_STATS(++stbds_rehash_probes); + + for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) { + if (bucket->hash[z] == 0) { + bucket->hash[z] = hash; + bucket->index[z] = ob->index[j]; + goto done; + } + } + + limit = pos & STBDS_BUCKET_MASK; + for (z = 0; z < limit; ++z) { + if (bucket->hash[z] == 0) { + bucket->hash[z] = hash; + bucket->index[z] = ob->index[j]; + goto done; + } + } + + pos += step; // quadratic probing + step += STBDS_BUCKET_LENGTH; + pos &= (t->slot_count-1); + } + } + done: + ; + } + } + } + + return t; +} + +#define STBDS_ROTATE_LEFT(val, n) (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n)))) +#define STBDS_ROTATE_RIGHT(val, n) (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n)))) + +size_t stbds_hash_string(char *str, size_t seed) +{ + size_t hash = seed; + while (*str) + hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++; + + // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits + hash ^= seed; + hash = (~hash) + (hash << 18); + hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31); + hash = hash * 21; + hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11); + hash += (hash << 6); + hash ^= STBDS_ROTATE_RIGHT(hash,22); + return hash+seed; +} + +#ifdef STBDS_SIPHASH_2_4 +#define STBDS_SIPHASH_C_ROUNDS 2 +#define STBDS_SIPHASH_D_ROUNDS 4 +typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1]; +#endif + +#ifndef STBDS_SIPHASH_C_ROUNDS +#define STBDS_SIPHASH_C_ROUNDS 1 +#endif +#ifndef STBDS_SIPHASH_D_ROUNDS +#define STBDS_SIPHASH_D_ROUNDS 1 +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()== +#endif + +static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed) +{ + unsigned char *d = (unsigned char *) p; + size_t i,j; + size_t v0,v1,v2,v3, data; + + // hash that works on 32- or 64-bit registers without knowing which we have + // (computes different results on 32-bit and 64-bit platform) + // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit + v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^ seed; + v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed; + v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^ seed; + v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed; + + #ifdef STBDS_TEST_SIPHASH_2_4 + // hardcoded with key material in the siphash test vectors + v0 ^= 0x0706050403020100ull ^ seed; + v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; + v2 ^= 0x0706050403020100ull ^ seed; + v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; + #endif + + #define STBDS_SIPROUND() \ + do { \ + v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13); v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \ + v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16); v3 ^= v2; \ + v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17); v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \ + v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21); v3 ^= v0; \ + } while (0) + + for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) { + data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4 + + v3 ^= data; + for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j) + STBDS_SIPROUND(); + v0 ^= data; + } + data = len << (STBDS_SIZE_T_BITS-8); + switch (len - i) { + case 7: data |= ((size_t) d[6] << 24) << 24; // fall through + case 6: data |= ((size_t) d[5] << 20) << 20; // fall through + case 5: data |= ((size_t) d[4] << 16) << 16; // fall through + case 4: data |= (d[3] << 24); // fall through + case 3: data |= (d[2] << 16); // fall through + case 2: data |= (d[1] << 8); // fall through + case 1: data |= d[0]; // fall through + case 0: break; + } + v3 ^= data; + for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j) + STBDS_SIPROUND(); + v0 ^= data; + v2 ^= 0xff; + for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j) + STBDS_SIPROUND(); + +#ifdef STBDS_SIPHASH_2_4 + return v0^v1^v2^v3; +#else + return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply +#endif +} + +size_t stbds_hash_bytes(void *p, size_t len, size_t seed) +{ +#ifdef STBDS_SIPHASH_2_4 + return stbds_siphash_bytes(p,len,seed); +#else + unsigned char *d = (unsigned char *) p; + + if (len == 4) { + unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + #if 0 + // HASH32-A Bob Jenkin's hash function w/o large constants + hash ^= seed; + hash -= (hash<<6); + hash ^= (hash>>17); + hash -= (hash<<9); + hash ^= seed; + hash ^= (hash<<4); + hash -= (hash<<3); + hash ^= (hash<<10); + hash ^= (hash>>15); + #elif 1 + // HASH32-BB Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts. + // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm + // not really sure what's going on. + hash ^= seed; + hash = (hash ^ 61) ^ (hash >> 16); + hash = hash + (hash << 3); + hash = hash ^ (hash >> 4); + hash = hash * 0x27d4eb2d; + hash ^= seed; + hash = hash ^ (hash >> 15); + #else // HASH32-C - Murmur3 + hash ^= seed; + hash *= 0xcc9e2d51; + hash = (hash << 17) | (hash >> 15); + hash *= 0x1b873593; + hash ^= seed; + hash = (hash << 19) | (hash >> 13); + hash = hash*5 + 0xe6546b64; + hash ^= hash >> 16; + hash *= 0x85ebca6b; + hash ^= seed; + hash ^= hash >> 13; + hash *= 0xc2b2ae35; + hash ^= hash >> 16; + #endif + // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2 + // Note that the larger tables have high variance as they were run fewer times + // HASH32-A // HASH32-BB // HASH32-C + // 0.10ms // 0.10ms // 0.10ms : 2,000 inserts creating 2K table + // 0.96ms // 0.95ms // 0.99ms : 20,000 inserts creating 20K table + // 14.69ms // 14.43ms // 14.97ms : 200,000 inserts creating 200K table + // 199.99ms // 195.36ms // 202.05ms : 2,000,000 inserts creating 2M table + // 2234.84ms // 2187.74ms // 2240.38ms : 20,000,000 inserts creating 20M table + // 55.68ms // 53.72ms // 57.31ms : 500,000 inserts & deletes in 2K table + // 63.43ms // 61.99ms // 65.73ms : 500,000 inserts & deletes in 20K table + // 80.04ms // 77.96ms // 81.83ms : 500,000 inserts & deletes in 200K table + // 100.42ms // 97.40ms // 102.39ms : 500,000 inserts & deletes in 2M table + // 119.71ms // 120.59ms // 121.63ms : 500,000 inserts & deletes in 20M table + // 185.28ms // 195.15ms // 187.74ms : 500,000 inserts & deletes in 200M table + // 15.58ms // 14.79ms // 15.52ms : 200,000 inserts creating 200K table with varying key spacing + + return (((size_t) hash << 16 << 16) | hash) ^ seed; + } else if (len == 8 && sizeof(size_t) == 8) { + size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4 + hash ^= seed; + hash = (~hash) + (hash << 21); + hash ^= STBDS_ROTATE_RIGHT(hash,24); + hash *= 265; + hash ^= STBDS_ROTATE_RIGHT(hash,14); + hash ^= seed; + hash *= 21; + hash ^= STBDS_ROTATE_RIGHT(hash,28); + hash += (hash << 31); + hash = (~hash) + (hash << 18); + return hash; + } else { + return stbds_siphash_bytes(p,len,seed); + } +#endif +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i) +{ + if (mode >= STBDS_HM_STRING) + return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset)); + else + return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize); +} + +#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize)) +#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize)) + +#define stbds_hash_table(a) ((stbds_hash_index *) stbds_header(a)->hash_table) + +void stbds_hmfree_func(void *a, size_t elemsize) +{ + if (a == NULL) return; + if (stbds_hash_table(a) != NULL) { + if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) { + size_t i; + // skip 0th element, which is default + for (i=1; i < stbds_header(a)->length; ++i) + STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i)); + } + stbds_strreset(&stbds_hash_table(a)->string); + } + STBDS_FREE(NULL, stbds_header(a)->hash_table); + STBDS_FREE(NULL, stbds_header(a)); +} + +static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode) +{ + void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); + stbds_hash_index *table = stbds_hash_table(raw_a); + size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed); + size_t step = STBDS_BUCKET_LENGTH; + size_t limit,i; + size_t pos; + stbds_hash_bucket *bucket; + + if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots + + pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2); + + for (;;) { + STBDS_STATS(++stbds_hash_probes); + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + + // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache + for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { + return (pos & ~STBDS_BUCKET_MASK)+i; + } + } else if (bucket->hash[i] == STBDS_HASH_EMPTY) { + return -1; + } + } + + // search from beginning of bucket to pos + limit = pos & STBDS_BUCKET_MASK; + for (i = 0; i < limit; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { + return (pos & ~STBDS_BUCKET_MASK)+i; + } + } else if (bucket->hash[i] == STBDS_HASH_EMPTY) { + return -1; + } + } + + // quadratic probing + pos += step; + step += STBDS_BUCKET_LENGTH; + pos &= (table->slot_count-1); + } + /* NOTREACHED */ +} + +void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) +{ + size_t keyoffset = 0; + if (a == NULL) { + // make it non-empty so we can return a temp + a = stbds_arrgrowf(0, elemsize, 0, 1); + stbds_header(a)->length += 1; + memset(a, 0, elemsize); + *temp = STBDS_INDEX_EMPTY; + // adjust a to point after the default element + return STBDS_ARR_TO_HASH(a,elemsize); + } else { + stbds_hash_index *table; + void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); + // adjust a to point to the default element + table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; + if (table == 0) { + *temp = -1; + } else { + ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); + if (slot < 0) { + *temp = STBDS_INDEX_EMPTY; + } else { + stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + *temp = b->index[slot & STBDS_BUCKET_MASK]; + } + } + return a; + } +} + +void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode) +{ + ptrdiff_t temp; + void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode); + stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp; + return p; +} + +void * stbds_hmput_default(void *a, size_t elemsize) +{ + // three cases: + // a is NULL <- allocate + // a has a hash table but no entries, because of shmode <- grow + // a has entries <- do nothing + if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) { + a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1); + stbds_header(a)->length += 1; + memset(a, 0, elemsize); + a=STBDS_ARR_TO_HASH(a,elemsize); + } + return a; +} + +static char *stbds_strdup(char *str); + +void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode) +{ + size_t keyoffset=0; + void *raw_a; + stbds_hash_index *table; + + if (a == NULL) { + a = stbds_arrgrowf(0, elemsize, 0, 1); + memset(a, 0, elemsize); + stbds_header(a)->length += 1; + // adjust a to point AFTER the default element + a = STBDS_ARR_TO_HASH(a,elemsize); + } + + // adjust a to point to the default element + raw_a = a; + a = STBDS_HASH_TO_ARR(a,elemsize); + + table = (stbds_hash_index *) stbds_header(a)->hash_table; + + if (table == NULL || table->used_count >= table->used_count_threshold) { + stbds_hash_index *nt; + size_t slot_count; + + slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2; + nt = stbds_make_hash_index(slot_count, table); + if (table) + STBDS_FREE(NULL, table); + else + nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0; + stbds_header(a)->hash_table = table = nt; + STBDS_STATS(++stbds_hash_grow); + } + + // we iterate hash table explicitly because we want to track if we saw a tombstone + { + size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed); + size_t step = STBDS_BUCKET_LENGTH; + size_t pos; + ptrdiff_t tombstone = -1; + stbds_hash_bucket *bucket; + + // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly + if (hash < 2) hash += 2; + + pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2); + + for (;;) { + size_t limit, i; + STBDS_STATS(++stbds_hash_probes); + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + + // start searching from pos to end of bucket + for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { + stbds_temp(a) = bucket->index[i]; + if (mode >= STBDS_HM_STRING) + stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset); + return STBDS_ARR_TO_HASH(a,elemsize); + } + } else if (bucket->hash[i] == 0) { + pos = (pos & ~STBDS_BUCKET_MASK) + i; + goto found_empty_slot; + } else if (tombstone < 0) { + if (bucket->index[i] == STBDS_INDEX_DELETED) + tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i); + } + } + + // search from beginning of bucket to pos + limit = pos & STBDS_BUCKET_MASK; + for (i = 0; i < limit; ++i) { + if (bucket->hash[i] == hash) { + if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { + stbds_temp(a) = bucket->index[i]; + return STBDS_ARR_TO_HASH(a,elemsize); + } + } else if (bucket->hash[i] == 0) { + pos = (pos & ~STBDS_BUCKET_MASK) + i; + goto found_empty_slot; + } else if (tombstone < 0) { + if (bucket->index[i] == STBDS_INDEX_DELETED) + tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i); + } + } + + // quadratic probing + pos += step; + step += STBDS_BUCKET_LENGTH; + pos &= (table->slot_count-1); + } + found_empty_slot: + if (tombstone >= 0) { + pos = tombstone; + --table->tombstone_count; + } + ++table->used_count; + + { + ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a); + // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type + if ((size_t) i+1 > stbds_arrcap(a)) + *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0); + raw_a = STBDS_ARR_TO_HASH(a,elemsize); + + STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a)); + stbds_header(a)->length = i+1; + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + bucket->hash[pos & STBDS_BUCKET_MASK] = hash; + bucket->index[pos & STBDS_BUCKET_MASK] = i-1; + stbds_temp(a) = i-1; + + switch (table->string.mode) { + case STBDS_SH_STRDUP: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break; + case STBDS_SH_ARENA: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break; + case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break; + default: memcpy((char *) a + elemsize*i, key, keysize); break; + } + } + return STBDS_ARR_TO_HASH(a,elemsize); + } +} + +void * stbds_shmode_func(size_t elemsize, int mode) +{ + void *a = stbds_arrgrowf(0, elemsize, 0, 1); + stbds_hash_index *h; + memset(a, 0, elemsize); + stbds_header(a)->length = 1; + stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL); + h->string.mode = (unsigned char) mode; + return STBDS_ARR_TO_HASH(a,elemsize); +} + +void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode) +{ + if (a == NULL) { + return 0; + } else { + stbds_hash_index *table; + void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); + table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; + stbds_temp(raw_a) = 0; + if (table == 0) { + return a; + } else { + ptrdiff_t slot; + slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); + if (slot < 0) + return a; + else { + stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + int i = slot & STBDS_BUCKET_MASK; + ptrdiff_t old_index = b->index[i]; + ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last' + STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count); + --table->used_count; + ++table->tombstone_count; + stbds_temp(raw_a) = 1; + STBDS_ASSERT(table->used_count >= 0); + //STBDS_ASSERT(table->tombstone_count < table->slot_count/4); + b->hash[i] = STBDS_HASH_DELETED; + b->index[i] = STBDS_INDEX_DELETED; + + if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP) + STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index)); + + // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip + if (old_index != final_index) { + // swap delete + memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize); + + // now find the slot for the last element + if (mode == STBDS_HM_STRING) + slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode); + else + slot = stbds_hm_find_slot(a, elemsize, (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode); + STBDS_ASSERT(slot >= 0); + b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + i = slot & STBDS_BUCKET_MASK; + STBDS_ASSERT(b->index[i] == final_index); + b->index[i] = old_index; + } + stbds_header(raw_a)->length -= 1; + + if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) { + stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table); + STBDS_FREE(NULL, table); + STBDS_STATS(++stbds_hash_shrink); + } else if (table->tombstone_count > table->tombstone_count_threshold) { + stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count , table); + STBDS_FREE(NULL, table); + STBDS_STATS(++stbds_hash_rebuild); + } + + return a; + } + } + } + /* NOTREACHED */ +} + +static char *stbds_strdup(char *str) +{ + // to keep replaceable allocator simple, we don't want to use strdup. + // rolling our own also avoids problem of strdup vs _strdup + size_t len = strlen(str)+1; + char *p = (char*) STBDS_REALLOC(NULL, 0, len); + memmove(p, str, len); + return p; +} + +#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN +#define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512u +#endif +#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX +#define STBDS_STRING_ARENA_BLOCKSIZE_MAX (1u<<20) +#endif + +char *stbds_stralloc(stbds_string_arena *a, char *str) +{ + char *p; + size_t len = strlen(str)+1; + if (len > a->remaining) { + // compute the next blocksize + size_t blocksize = a->block; + + // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that + // there are log(SIZE) allocations to free when we destroy the table + blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1); + + // if size is under 1M, advance to next blocktype + if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX)) + ++a->block; + + if (len > blocksize) { + // if string is larger than blocksize, then just allocate the full size. + // note that we still advance string_block so block size will continue + // increasing, so e.g. if somebody only calls this with 1000-long strings, + // eventually the arena will start doubling and handling those as well + stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len); + memmove(sb->storage, str, len); + if (a->storage) { + // insert it after the first element, so that we don't waste the space there + sb->next = a->storage->next; + a->storage->next = sb; + } else { + sb->next = 0; + a->storage = sb; + a->remaining = 0; // this is redundant, but good for clarity + } + return sb->storage; + } else { + stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize); + sb->next = a->storage; + a->storage = sb; + a->remaining = blocksize; + } + } + + STBDS_ASSERT(len <= a->remaining); + p = a->storage->storage + a->remaining - len; + a->remaining -= len; + memmove(p, str, len); + return p; +} + +void stbds_strreset(stbds_string_arena *a) +{ + stbds_string_block *x,*y; + x = a->storage; + while (x) { + y = x->next; + STBDS_FREE(NULL, x); + x = y; + } + memset(a, 0, sizeof(*a)); +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// UNIT TESTS +// + +#ifdef STBDS_UNIT_TESTS +#include +#ifdef STBDS_ASSERT_WAS_UNDEFINED +#undef STBDS_ASSERT +#endif +#ifndef STBDS_ASSERT +#define STBDS_ASSERT assert +#include +#endif + +typedef struct { int key,b,c,d; } stbds_struct; +typedef struct { int key[2],b,c,d; } stbds_struct2; + +static char buffer[256]; +char *strkey(int n) +{ +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + sprintf_s(buffer, sizeof(buffer), "test_%d", n); +#else + sprintf(buffer, "test_%d", n); +#endif + return buffer; +} + +void stbds_unit_tests(void) +{ +#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus) + // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing! + STBDS_ASSERT(0); +#else + const int testsize = 100000; + const int testsize2 = testsize/20; + int *arr=NULL; + struct { int key; int value; } *intmap = NULL; + struct { char *key; int value; } *strmap = NULL, s; + struct { stbds_struct key; int value; } *map = NULL; + stbds_struct *map2 = NULL; + stbds_struct2 *map3 = NULL; + stbds_string_arena sa = { 0 }; + int key3[2] = { 1,2 }; + ptrdiff_t temp; + + int i,j; + + STBDS_ASSERT(arrlen(arr)==0); + for (i=0; i < 20000; i += 50) { + for (j=0; j < i; ++j) + arrpush(arr,j); + arrfree(arr); + } + + for (i=0; i < 4; ++i) { + arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); + arrdel(arr,i); + arrfree(arr); + arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); + arrdelswap(arr,i); + arrfree(arr); + } + + for (i=0; i < 5; ++i) { + arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); + stbds_arrins(arr,i,5); + STBDS_ASSERT(arr[i] == 5); + if (i < 4) + STBDS_ASSERT(arr[4] == 4); + arrfree(arr); + } + + i = 1; + STBDS_ASSERT(hmgeti(intmap,i) == -1); + hmdefault(intmap, -2); + STBDS_ASSERT(hmgeti(intmap, i) == -1); + STBDS_ASSERT(hmget (intmap, i) == -2); + for (i=0; i < testsize; i+=2) + hmput(intmap, i, i*5); + for (i=0; i < testsize; i+=1) { + if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); + else STBDS_ASSERT(hmget(intmap, i) == i*5); + if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 ); + else STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5); + } + for (i=0; i < testsize; i+=2) + hmput(intmap, i, i*3); + for (i=0; i < testsize; i+=1) + if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); + else STBDS_ASSERT(hmget(intmap, i) == i*3); + for (i=2; i < testsize; i+=4) + hmdel(intmap, i); // delete half the entries + for (i=0; i < testsize; i+=1) + if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 ); + else STBDS_ASSERT(hmget(intmap, i) == i*3); + for (i=0; i < testsize; i+=1) + hmdel(intmap, i); // delete the rest of the entries + for (i=0; i < testsize; i+=1) + STBDS_ASSERT(hmget(intmap, i) == -2 ); + hmfree(intmap); + for (i=0; i < testsize; i+=2) + hmput(intmap, i, i*3); + hmfree(intmap); + + #if defined(__clang__) || defined(__GNUC__) + #ifndef __cplusplus + intmap = NULL; + hmput(intmap, 15, 7); + hmput(intmap, 11, 3); + hmput(intmap, 9, 5); + STBDS_ASSERT(hmget(intmap, 9) == 5); + STBDS_ASSERT(hmget(intmap, 11) == 3); + STBDS_ASSERT(hmget(intmap, 15) == 7); + #endif + #endif + + for (i=0; i < testsize; ++i) + stralloc(&sa, strkey(i)); + strreset(&sa); + + { + s.key = "a", s.value = 1; + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key == s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); + } + + { + s.key = "a", s.value = 1; + sh_new_strdup(strmap); + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key != s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); + } + + { + s.key = "a", s.value = 1; + sh_new_arena(strmap); + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key != s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); + } + + for (j=0; j < 2; ++j) { + STBDS_ASSERT(shgeti(strmap,"foo") == -1); + if (j == 0) + sh_new_strdup(strmap); + else + sh_new_arena(strmap); + STBDS_ASSERT(shgeti(strmap,"foo") == -1); + shdefault(strmap, -2); + STBDS_ASSERT(shgeti(strmap,"foo") == -1); + for (i=0; i < testsize; i+=2) + shput(strmap, strkey(i), i*3); + for (i=0; i < testsize; i+=1) + if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); + else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); + for (i=2; i < testsize; i+=4) + shdel(strmap, strkey(i)); // delete half the entries + for (i=0; i < testsize; i+=1) + if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); + else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); + for (i=0; i < testsize; i+=1) + shdel(strmap, strkey(i)); // delete the rest of the entries + for (i=0; i < testsize; i+=1) + STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); + shfree(strmap); + } + + { + struct { char *key; char value; } *hash = NULL; + char name[4] = "jen"; + shput(hash, "bob" , 'h'); + shput(hash, "sally" , 'e'); + shput(hash, "fred" , 'l'); + shput(hash, "jen" , 'x'); + shput(hash, "doug" , 'o'); + + shput(hash, name , 'l'); + shfree(hash); + } + + for (i=0; i < testsize; i += 2) { + stbds_struct s = { i,i*2,i*3,i*4 }; + hmput(map, s, i*5); + } + + for (i=0; i < testsize; i += 1) { + stbds_struct s = { i,i*2,i*3 ,i*4 }; + stbds_struct t = { i,i*2,i*3+1,i*4 }; + if (i & 1) STBDS_ASSERT(hmget(map, s) == 0); + else STBDS_ASSERT(hmget(map, s) == i*5); + if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0); + else STBDS_ASSERT(hmget_ts(map, s, temp) == i*5); + //STBDS_ASSERT(hmget(map, t.key) == 0); + } + + for (i=0; i < testsize; i += 2) { + stbds_struct s = { i,i*2,i*3,i*4 }; + hmputs(map2, s); + } + hmfree(map); + + for (i=0; i < testsize; i += 1) { + stbds_struct s = { i,i*2,i*3,i*4 }; + stbds_struct t = { i,i*2,i*3+1,i*4 }; + if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0); + else STBDS_ASSERT(hmgets(map2, s.key).d == i*4); + //STBDS_ASSERT(hmgetp(map2, t.key) == 0); + } + hmfree(map2); + + for (i=0; i < testsize; i += 2) { + stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 }; + hmputs(map3, s); + } + for (i=0; i < testsize; i += 1) { + stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 }; + stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 }; + if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0); + else STBDS_ASSERT(hmgets(map3, s.key).d == i*5); + //STBDS_ASSERT(hmgetp(map3, t.key) == 0); + } +#endif +} +#endif + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2019 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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. +------------------------------------------------------------------------------ +*/ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f1875b5..e324353 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,14 +11,7 @@ endif (CMAKE_COMPILER_IS_GNUCXX) # add_executable(message_chunks message_chunks.c CuTest.c) add_executable(string_tests string_tests.c CuTest.c) -add_executable(serialization_tests serialization.c CuTest.c) target_link_libraries(string_tests -lm) -target_link_libraries(serialization_tests -lm) -# FIXME: adapt to latest API -# add_test(message_chunks message_chunks) -add_test(serialization_tests serialization_tests) add_test(string_tests string_tests) - -target_compile_definitions(serialization_tests PUBLIC NBN_DEBUG) diff --git a/tests/message_chunks.c b/tests/message_chunks.c deleted file mode 100644 index 649c632..0000000 --- a/tests/message_chunks.c +++ /dev/null @@ -1,301 +0,0 @@ -#include -#include - -#include "CuTest.h" - -#define NBNET_IMPL - -#define NBN_LogInfo printf -#define NBN_LogTrace printf -#define NBN_LogDebug printf -#define NBN_LogError printf - -#define NBN_Allocator malloc -#define NBN_Deallocator free - -#include "../nbnet.h" - -typedef struct -{ - uint8_t data[4096]; -} BigMessage; - -#define BIG_MESSAGE_TYPE 0 - -BigMessage *BigMessage_Create() -{ - return malloc(sizeof(BigMessage)); -} - -int BigMessage_Serialize(BigMessage *msg, NBN_Stream *stream) -{ - SERIALIZE_BYTES(msg->data, 4096); - - return 0; -} - -void BigMessage_Destroy(BigMessage *msg) -{ - free(msg); -} - -static NBN_Endpoint endpoint; - -NBN_Connection *Begin(NBN_Endpoint *endpoint) -{ - NBN_Endpoint_Init(endpoint, (NBN_Config){ .protocol_name = "tests" }); - NBN_Endpoint_RegisterMessageBuilder(endpoint, (NBN_MessageBuilder)BigMessage_Create, BIG_MESSAGE_TYPE); - NBN_Endpoint_RegisterMessageSerializer(endpoint, (NBN_MessageSerializer)BigMessage_Serialize, BIG_MESSAGE_TYPE); - NBN_Endpoint_RegisterMessageDestructor(endpoint, (NBN_MessageDestructor)BigMessage_Destroy, BIG_MESSAGE_TYPE); - - return NBN_Endpoint_CreateConnection(endpoint, 0); -} - -static void End(NBN_Connection *conn, NBN_Endpoint *endpoint) -{ - NBN_ListNode *current_node = conn->send_queue->head; - - for (int i = 0; current_node != NULL; i++) - { - NBN_Message *m = current_node->data; - - current_node = current_node->next; - - NBN_List_Remove(conn->send_queue, m); - } - - NBN_Connection_Destroy(conn); - NBN_Endpoint_Deinit(endpoint); -} - -void Test_ChunksGeneration(CuTest *tc) -{ - NBN_Endpoint endpoint; - NBN_Connection *conn = Begin(&endpoint); - BigMessage *msg = NBN_Endpoint_CreateOutgoingMessage(&endpoint, BIG_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_RELIABLE); - - CuAssertPtrNotNull(tc, msg); - - /* Fill the "big message" with random bytes */ - for (int i = 0; i < sizeof(msg->data); i++) - msg->data[i] = rand() % 255 + 1; - - NBN_MeasureStream m_stream; - - NBN_MeasureStream_Init(&m_stream); - - NBN_Message message = { - .header = {.type = BIG_MESSAGE_TYPE, .channel_id = NBN_CHANNEL_RESERVED_RELIABLE}, - .serializer = (NBN_MessageSerializer)BigMessage_Serialize, - .data = msg}; - unsigned int message_size = (NBN_Message_Measure(&message, &m_stream) - 1) / 8 + 1; - uint8_t buffer[4096 * 8]; - NBN_WriteStream w_stream; - - NBN_WriteStream_Init(&w_stream, buffer, message_size); - - CuAssertIntEquals(tc, 0, NBN_Message_SerializeHeader( - &message.header, (NBN_Stream *)&w_stream)); - CuAssertIntEquals(tc, 0, BigMessage_Serialize(msg, (NBN_Stream *)&w_stream)); - - CuAssertIntEquals(tc, 0, NBN_Connection_EnqueueOutgoingMessage(conn)); - - /* Should have generated 5 chunks */ - CuAssertIntEquals(tc, 5, conn->send_queue->count); - - /* Merging the chunks together should reconstruct the initial message */ - uint8_t *r_buffer = malloc(message_size); /* used to merge chunks together */ - NBN_ListNode *current_node = conn->send_queue->head; - - for (int i = 0; current_node != NULL; i++) - { - NBN_Message *chunk_msg = current_node->data; - NBN_MessageChunk *chunk = ((NBN_OutgoingMessageInfo *)chunk_msg->data)->data; - - CuAssertIntEquals(tc, NBN_MESSAGE_CHUNK_TYPE, chunk_msg->header.type); - CuAssertIntEquals(tc, i, chunk->id); - CuAssertIntEquals(tc, 5, chunk->total); - - unsigned int cpy_size = MIN( - message_size - (i * NBN_MESSAGE_CHUNK_SIZE), - NBN_MESSAGE_CHUNK_SIZE); - - NBN_LogDebug("Read chunk %d (bytes: %d)", i, cpy_size); - - memcpy(r_buffer + (i * NBN_MESSAGE_CHUNK_SIZE), chunk->data, cpy_size); - - current_node = current_node->next; - } - - CuAssertIntEquals(tc, 0, memcmp(r_buffer, buffer, message_size)); - - free(r_buffer); - - End(conn, &endpoint); -} - -/* TODO: add more tests for cases like missing chunks etc. */ -void Test_NBN_Channel_AddChunk(CuTest *tc) -{ - NBN_Endpoint endpoint; - NBN_Connection *conn = Begin(&endpoint); - BigMessage *msg = NBN_Endpoint_CreateOutgoingMessage(&endpoint, BIG_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_RELIABLE); - - CuAssertPtrNotNull(tc, msg); - - /* Fill the "big message" with random bytes */ - for (int i = 0; i < sizeof(msg->data); i++) - msg->data[i] = rand() % 255 + 1; - - NBN_Connection_EnqueueOutgoingMessage(conn); - - BigMessage *msg2 = NBN_Endpoint_CreateOutgoingMessage(&endpoint, BIG_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_RELIABLE); - - CuAssertPtrNotNull(tc, msg); - - /* Fill the "big message" with random bytes */ - for (int i = 0; i < sizeof(msg2->data); i++) - msg2->data[i] = rand() % 255 + 1; - - NBN_Connection_EnqueueOutgoingMessage(conn); - - /* Should have generated 10 chunks */ - CuAssertIntEquals(tc, 10, conn->send_queue->count); - - NBN_Message *msg_chunks[10]; - - for (int i = 0; i < 10; i++) - { - NBN_Message *m = NBN_List_GetAt(conn->send_queue, i); - NBN_MessageChunk *chunk = ((NBN_OutgoingMessageInfo *)m->data)->data; - - msg_chunks[i] = NBN_Message_Create( - NBN_MESSAGE_CHUNK_TYPE, - NBN_CHANNEL_RESERVED_RELIABLE, - (NBN_MessageSerializer)NBN_MessageChunk_Serialize, - (NBN_MessageDestructor)NBN_MessageChunk_Destroy, - false, - chunk); - } - - NBN_Channel *channel = conn->channels[NBN_CHANNEL_RESERVED_RELIABLE]; - - /* First message chunks */ - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[0])); - CuAssertIntEquals(tc, 0, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 1, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[1])); - CuAssertIntEquals(tc, 1, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 2, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[2])); - CuAssertIntEquals(tc, 2, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 3, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[3])); - CuAssertIntEquals(tc, 3, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 4, channel->chunk_count); - - /* This is the last chunk of the first message so it should return true */ - CuAssertTrue(tc, NBN_Channel_AddChunk(channel, msg_chunks[4])); - CuAssertIntEquals(tc, -1, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 5, channel->chunk_count); - - /* Reconstruct the message so the chunks buffer gets cleared */ - NBN_Channel_ReconstructMessageFromChunks(channel, conn); - - /* Second message chunks */ - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[5])); - CuAssertIntEquals(tc, 0, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 1, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[6])); - CuAssertIntEquals(tc, 1, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 2, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[7])); - CuAssertIntEquals(tc, 2, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 3, channel->chunk_count); - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[8])); - CuAssertIntEquals(tc, 3, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 4, channel->chunk_count); - - /* This is the last chunk of the second message so it should return true */ - CuAssertTrue(tc, NBN_Channel_AddChunk(channel, msg_chunks[9])); - CuAssertIntEquals(tc, -1, channel->last_received_chunk_id); - CuAssertIntEquals(tc, 5, channel->chunk_count); - - End(conn, &endpoint); -} - -void Test_NBN_Channel_ReconstructMessageFromChunks(CuTest *tc) -{ - NBN_Endpoint endpoint; - NBN_Connection *conn = Begin(&endpoint); - BigMessage *msg = NBN_Endpoint_CreateOutgoingMessage(&endpoint, BIG_MESSAGE_TYPE, NBN_CHANNEL_RESERVED_RELIABLE); - - CuAssertPtrNotNull(tc, msg); - - /* Fill the "big message" with random bytes */ - for (int i = 0; i < sizeof(msg->data); i++) - msg->data[i] = rand() % 255 + 1; - - CuAssertIntEquals(tc, 0, NBN_Connection_EnqueueOutgoingMessage(conn)); - - /* Should have generated 5 chunks */ - CuAssertIntEquals(tc, 5, conn->send_queue->count); - - NBN_Message *msg_chunks[5]; - - for (int i = 0; i < 5; i++) - { - NBN_Message *m = NBN_List_GetAt(conn->send_queue, i); - NBN_MessageChunk *chunk = ((NBN_OutgoingMessageInfo *)m->data)->data; - - msg_chunks[i] = NBN_Message_Create( - NBN_MESSAGE_CHUNK_TYPE, - NBN_CHANNEL_RESERVED_RELIABLE, - (NBN_MessageSerializer)NBN_MessageChunk_Serialize, - (NBN_MessageDestructor)NBN_MessageChunk_Destroy, - false, - chunk); - } - - NBN_Channel *channel = conn->channels[NBN_CHANNEL_RESERVED_RELIABLE]; - - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[0])); - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[1])); - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[2])); - CuAssertTrue(tc, !NBN_Channel_AddChunk(channel, msg_chunks[3])); - CuAssertTrue(tc, NBN_Channel_AddChunk(channel, msg_chunks[4])); - - NBN_Message *r_msg = NBN_Channel_ReconstructMessageFromChunks(channel, conn); - - CuAssertIntEquals(tc, 0, r_msg->header.type); - CuAssertIntEquals(tc, NBN_CHANNEL_RESERVED_RELIABLE, r_msg->header.channel_id); - CuAssertIntEquals(tc, 0, memcmp(r_msg->data, msg->data, 4096)); - - End(conn, &endpoint); -} - -int main(int argc, char *argv[]) -{ - CuString *output = CuStringNew(); - CuSuite* suite = CuSuiteNew(); - - SUITE_ADD_TEST(suite, Test_ChunksGeneration); - SUITE_ADD_TEST(suite, Test_NBN_Channel_AddChunk); - SUITE_ADD_TEST(suite, Test_NBN_Channel_ReconstructMessageFromChunks); - - CuSuiteRun(suite); - CuSuiteSummary(suite, output); - CuSuiteDetails(suite, output); - - printf("%s\n", output->buffer); - - return suite->failCount; -} diff --git a/tests/serialization.c b/tests/serialization.c deleted file mode 100644 index c4375ea..0000000 --- a/tests/serialization.c +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include - -#include "CuTest.h" - -#define NBNET_IMPL - -#define NBN_LogInfo printf -#define NBN_LogTrace printf -#define NBN_LogDebug printf -#define NBN_LogError printf -#define NBN_LogWarning printf - -#define NBN_Allocator malloc -#define NBN_Deallocator free - -#include "../nbnet.h" - -typedef struct -{ - float v1; - float v2; - float v3; -} BogusMessage; - -int BogusMessage_Serialize(BogusMessage *msg, NBN_Stream *stream) -{ - NBN_SerializeFloat(stream, msg->v1, -100, 100, 1); - NBN_SerializeFloat(stream, msg->v2, -100, 100, 2); - NBN_SerializeFloat(stream, msg->v3, -100, 100, 3); - - return 0; -} - -typedef struct -{ - uint64_t v1; - uint64_t v2; - uint64_t v3; - uint64_t v4; -} BogusMessage2; - -int BogusMessage2_Serialize(BogusMessage2 *msg, NBN_Stream *stream) -{ - NBN_SerializeUInt64(stream, msg->v1); - NBN_SerializeUInt64(stream, msg->v2); - NBN_SerializeUInt64(stream, msg->v3); - NBN_SerializeUInt64(stream, msg->v4); - - return 0; -} - -void Test_SerializeFloat(CuTest *tc) -{ - BogusMessage msg = { .v1 = 42.5, .v2 = -12.42, .v3 = -89.123 }; - NBN_WriteStream w_stream; - uint8_t buffer[32]; - - NBN_WriteStream_Init(&w_stream, buffer, sizeof(buffer)); - - CuAssertIntEquals(tc, 0, BogusMessage_Serialize(&msg, (NBN_Stream *)&w_stream)); - - NBN_WriteStream_Flush(&w_stream); - - BogusMessage r_msg; - NBN_ReadStream r_stream; - - NBN_ReadStream_Init(&r_stream, buffer, sizeof(buffer)); - - CuAssertIntEquals(tc, 0, BogusMessage_Serialize(&r_msg, (NBN_Stream *)&r_stream)); - CuAssertTrue(tc, r_msg.v1 == 42.5); - CuAssertIntEquals(tc, -1242, r_msg.v2 * 100); - CuAssertIntEquals(tc, -89123, r_msg.v3 * 1000); -} - -void Test_SerializeUInt64(CuTest *tc) -{ - BogusMessage2 msg = { - .v1 = 0xFFFFFFFFFFFFFFFF, - .v2 = 9223372036854775807, - .v3 = 4611686018427387903, - .v4 = 42000 - }; - NBN_WriteStream w_stream; - uint8_t buffer[32]; - - NBN_WriteStream_Init(&w_stream, buffer, sizeof(buffer)); - CuAssertIntEquals(tc, 0, BogusMessage2_Serialize(&msg, (NBN_Stream *)&w_stream)); - NBN_WriteStream_Flush(&w_stream); - - BogusMessage2 r_msg; - NBN_ReadStream r_stream; - - NBN_ReadStream_Init(&r_stream, buffer, sizeof(buffer)); - - CuAssertIntEquals(tc, 0, BogusMessage2_Serialize(&r_msg, (NBN_Stream *)&r_stream)); - CuAssertTrue(tc, r_msg.v1 == 0xFFFFFFFFFFFFFFFF); - CuAssertTrue(tc, r_msg.v2 == 9223372036854775807); - CuAssertTrue(tc, r_msg.v3 == 4611686018427387903); - CuAssertTrue(tc, r_msg.v4 == 42000); -} - -int main(int argc, char *argv[]) -{ - (void) argc; - (void) argv; - - CuString *output = CuStringNew(); - CuSuite* suite = CuSuiteNew(); - - SUITE_ADD_TEST(suite, Test_SerializeFloat); - SUITE_ADD_TEST(suite, Test_SerializeUInt64); - - CuSuiteRun(suite); - CuSuiteSummary(suite, output); - CuSuiteDetails(suite, output); - - printf("%s\n", output->buffer); - - return suite->failCount; -} diff --git a/net_drivers/webrtc/js/api.js b/webrtc/js/api.js similarity index 95% rename from net_drivers/webrtc/js/api.js rename to webrtc/js/api.js index 207451d..510c318 100644 --- a/net_drivers/webrtc/js/api.js +++ b/webrtc/js/api.js @@ -66,7 +66,7 @@ mergeInto(LibraryManager.library, { }, __js_game_server_send_packet_to: function (packetPtr, packetSize, peerId) { - const data = new Uint8Array(Module.HEAPU8.subarray(packetPtr, packetPtr + packetSize)) + const data = new Uint8Array(HEAPU8.subarray(packetPtr, packetPtr + packetSize)) this.gameServer.send(data, peerId) }, @@ -128,7 +128,7 @@ mergeInto(LibraryManager.library, { }, __js_game_client_send_packet: function (packetPtr, packetSize) { - const data = new Uint8Array(Module.HEAPU8.subarray(packetPtr, packetPtr + packetSize)) + const data = new Uint8Array(HEAPU8.subarray(packetPtr, packetPtr + packetSize)) this.gameClient.send(data) }, diff --git a/net_drivers/webrtc/js/game_client.js b/webrtc/js/game_client.js similarity index 100% rename from net_drivers/webrtc/js/game_client.js rename to webrtc/js/game_client.js diff --git a/net_drivers/webrtc/js/game_server.js b/webrtc/js/game_server.js similarity index 100% rename from net_drivers/webrtc/js/game_server.js rename to webrtc/js/game_server.js diff --git a/net_drivers/webrtc/js/index.js b/webrtc/js/index.js similarity index 100% rename from net_drivers/webrtc/js/index.js rename to webrtc/js/index.js diff --git a/net_drivers/webrtc/js/logger.js b/webrtc/js/logger.js similarity index 100% rename from net_drivers/webrtc/js/logger.js rename to webrtc/js/logger.js diff --git a/net_drivers/webrtc/js/nbnet.js b/webrtc/js/nbnet.js similarity index 100% rename from net_drivers/webrtc/js/nbnet.js rename to webrtc/js/nbnet.js diff --git a/net_drivers/webrtc/js/peer.js b/webrtc/js/peer.js similarity index 100% rename from net_drivers/webrtc/js/peer.js rename to webrtc/js/peer.js diff --git a/net_drivers/webrtc/js/standalone/connection.js b/webrtc/js/standalone/connection.js similarity index 100% rename from net_drivers/webrtc/js/standalone/connection.js rename to webrtc/js/standalone/connection.js diff --git a/net_drivers/webrtc/js/standalone/signaling_client.js b/webrtc/js/standalone/signaling_client.js similarity index 100% rename from net_drivers/webrtc/js/standalone/signaling_client.js rename to webrtc/js/standalone/signaling_client.js diff --git a/net_drivers/webrtc/js/standalone/signaling_server.js b/webrtc/js/standalone/signaling_server.js similarity index 100% rename from net_drivers/webrtc/js/standalone/signaling_server.js rename to webrtc/js/standalone/signaling_server.js diff --git a/webrtc/package-lock.json b/webrtc/package-lock.json new file mode 100644 index 0000000..e925308 --- /dev/null +++ b/webrtc/package-lock.json @@ -0,0 +1,594 @@ +{ + "name": "nbnet", + "version": "1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nbnet", + "version": "1.0", + "license": "MIT", + "dependencies": { + "@roamhq/wrtc": "^0.8.0", + "websocket": "^1.0.31", + "winston": "^3.2.1" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@roamhq/wrtc": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc/-/wrtc-0.8.0.tgz", + "integrity": "sha512-C0V/nqc4/2xzORI5qa4mIeN/8UO3ywN1kInrJ9u6GljFx0D18JMUJEqe8yYHa61RrEeoWN3PKdW++k8TocSx/A==", + "license": "BSD-2-Clause", + "optionalDependencies": { + "@roamhq/wrtc-darwin-arm64": "0.8.0", + "@roamhq/wrtc-darwin-x64": "0.8.0", + "@roamhq/wrtc-linux-arm64": "0.8.1", + "@roamhq/wrtc-linux-x64": "0.8.1", + "@roamhq/wrtc-win32-x64": "0.8.0", + "domexception": "^4.0.0" + } + }, + "node_modules/@roamhq/wrtc-darwin-arm64": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-arm64/-/wrtc-darwin-arm64-0.8.0.tgz", + "integrity": "sha512-OtV2KWO7zOG3L8TF3KCt9aucynVCD/ww2xeXXgg+FLkya3ca0uzehN8EQJ3BL4tkInksbFJ2ssyu9cehfJ3ZuA==", + "cpu": [ + "arm64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@roamhq/wrtc-darwin-x64": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-darwin-x64/-/wrtc-darwin-x64-0.8.0.tgz", + "integrity": "sha512-VY7Vzt/SDDDCpW//h8GW9bOZrOr8gWXPZVD9473ypl4jyBIoO57yyLbHzd1G0vBUkS6szsHlQCz1WwpI30YL+g==", + "cpu": [ + "x64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@roamhq/wrtc-linux-arm64": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-arm64/-/wrtc-linux-arm64-0.8.1.tgz", + "integrity": "sha512-FBJLLazlWkGQUXaokC/rTbrUQbb0CNFYry52fZGstufrGLTWu+g4HcwXdVvxh1tnVtVMvkQGk+mlOL52sCxw0A==", + "cpu": [ + "arm64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@roamhq/wrtc-linux-x64": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-linux-x64/-/wrtc-linux-x64-0.8.1.tgz", + "integrity": "sha512-I9oWG7b4uvWO1IOR/aF34n+ID6TKVuSs0jd19h5KdhfRtw7FFh9xxuwN9rONPxLVa6fS0q+MCZgAf8Scz89L8Q==", + "cpu": [ + "x64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@roamhq/wrtc-win32-x64": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@roamhq/wrtc-win32-x64/-/wrtc-win32-x64-0.8.0.tgz", + "integrity": "sha512-R2fxl41BLWPiP4eaTHGLzbbVvRjx1mV/OsgINCvawO7Hwz5Zx9I45+Fhrw3hd4n5amIeSG9VIF7Kz8eeTFXTGQ==", + "cpu": [ + "x64" + ], + "license": "BSD-2-Clause", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/bufferutil": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz", + "integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "license": "MIT", + "optional": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "license": "Apache-2.0", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "engines": { + "node": ">=0.10.32" + } + } + } +} diff --git a/net_drivers/webrtc/package.json b/webrtc/package.json similarity index 100% rename from net_drivers/webrtc/package.json rename to webrtc/package.json