From cb925d005c198ea9a4ab953f8c9a7478a1702567 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 10 Nov 2024 22:37:28 +0300 Subject: [PATCH 01/56] add libcurl --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- src/CMakeLists.txt | 4 +++- test/curltest.cpp | 18 ++++++++++++++++++ vcpkg.json | 3 ++- 6 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 test/curltest.cpp diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index a0e6e6fa..1bf4ae09 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -2,7 +2,7 @@ name: C/C++ AppImage on: push: - branches: [ "main", "release-**"] + branches: [ "main", "release-**", "curl"] pull_request: branches: [ "main" ] diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e4ddf55a..c8050bcb 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -2,7 +2,7 @@ name: Macos Build on: push: - branches: [ "main", "release-**"] + branches: [ "main", "release-**", "curl"] pull_request: branches: [ "main" ] diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b14c32d0..c5c52290 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -2,7 +2,7 @@ name: Windows Build on: push: - branches: [ "main", "release-**"] + branches: [ "main", "release-**", "curl"] pull_request: branches: [ "main" ] diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ecd0c98b..7eecaef9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ find_package(GLEW REQUIRED) find_package(OpenAL REQUIRED) find_package(ZLIB REQUIRED) find_package(PNG REQUIRED) +find_package(CURL REQUIRED) if (NOT APPLE) find_package(EnTT REQUIRED) endif() @@ -61,5 +62,6 @@ if(UNIX) endif() include_directories(${LUA_INCLUDE_DIR}) +include_directories(${CURL_INCLUDE_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(${PROJECT_NAME} ${LIBS} glfw OpenGL::GL ${OPENAL_LIBRARY} GLEW::GLEW ZLIB::ZLIB PNG::PNG ${VORBISLIB} ${LUA_LIBRARIES} ${CMAKE_DL_LIBS}) +target_link_libraries(${PROJECT_NAME} ${LIBS} glfw OpenGL::GL ${OPENAL_LIBRARY} GLEW::GLEW ZLIB::ZLIB PNG::PNG CURL::libcurl ${VORBISLIB} ${LUA_LIBRARIES} ${CMAKE_DL_LIBS}) diff --git a/test/curltest.cpp b/test/curltest.cpp new file mode 100644 index 00000000..a8d3424e --- /dev/null +++ b/test/curltest.cpp @@ -0,0 +1,18 @@ +#include + +#include + +size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) { + return size * nmemb; +} + +TEST(curltest, curltest) { + if (CURL* curl = curl_easy_init()) { + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "https://github.com"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + res = curl_easy_perform(curl); + std::cout << curl_easy_strerror(res) << std::endl; + curl_easy_cleanup(curl); + } +} diff --git a/vcpkg.json b/vcpkg.json index 54ee4ec8..17145c4a 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -12,6 +12,7 @@ "luajit", "libvorbis", "entt", - "gtest" + "gtest", + "curl" ] } From a5d87c9fc4c2f78df49dfed52522f93c6e8584bc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 10 Nov 2024 22:43:55 +0300 Subject: [PATCH 02/56] update appimage workflow --- .github/workflows/appimage.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 1bf4ae09..96aa3c39 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -23,7 +23,8 @@ jobs: - name: install dependencies run: | sudo apt-get update - sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev cmake squashfs-tools + sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev \ + libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev cmake squashfs-tools # fix luajit paths sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a sudo ln -s /usr/include/luajit-2.1 /usr/include/lua From 46dfdac9981c7da8ce6f55f3b97aa161b8753517 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 11 Nov 2024 03:50:00 +0300 Subject: [PATCH 03/56] add Network.httpGet (WIP) --- src/network/Network.cpp | 60 +++++++++++++++++++++++++++++++++++++++ src/network/Network.hpp | 31 ++++++++++++++++++++ src/settings.hpp | 4 +++ test/curltest.cpp | 18 ------------ test/network/curltest.cpp | 18 ++++++++++++ 5 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 src/network/Network.cpp create mode 100644 src/network/Network.hpp delete mode 100644 test/curltest.cpp create mode 100644 test/network/curltest.cpp diff --git a/src/network/Network.cpp b/src/network/Network.cpp new file mode 100644 index 00000000..bdd2a2ac --- /dev/null +++ b/src/network/Network.cpp @@ -0,0 +1,60 @@ +#include "Network.hpp" + +#include +#include + +#include "debug/Logger.hpp" + +using namespace network; + +static debug::Logger logger("network"); + +size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) { + auto& buffer = *reinterpret_cast*>(userdata); + size_t psize = buffer.size(); + buffer.resize(psize + size * nmemb); + std::memcpy(buffer.data() + psize, ptr, size * nmemb); + return size * nmemb; +} + +class CurlHttp : public Http { + CURL* curl; +public: + CurlHttp(CURL* curl) : curl(curl) { + } + + virtual ~CurlHttp() { + curl_easy_cleanup(curl); + } + + void get(const std::string& url, const OnResponse& callback) override { + std::vector buffer; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); + CURLcode res = curl_easy_perform(curl); + callback(res, std::move(buffer)); + } + + static std::unique_ptr create() { + if (auto curl = curl_easy_init()) { + return std::make_unique(curl); + } + throw std::runtime_error("could not initialzie cURL"); + } +}; + + +Network::Network(std::unique_ptr http) : http(std::move(http)) { +} + +Network::~Network() = default; + +void Network::httpGet(const std::string& url, const OnResponse& callback) { + http->get(url, callback); +} + +std::unique_ptr Network::create(const NetworkSettings& settings) { + auto http = CurlHttp::create(); + return std::make_unique(std::move(http)); +} diff --git a/src/network/Network.hpp b/src/network/Network.hpp new file mode 100644 index 00000000..2e92a302 --- /dev/null +++ b/src/network/Network.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +#include "typedefs.hpp" +#include "settings.hpp" +#include "util/Buffer.hpp" + +namespace network { + using OnResponse = std::function)>; + + class Http { + public: + virtual ~Http() {} + + virtual void get(const std::string& url, const OnResponse& callback) = 0; + }; + + class Network { + std::unique_ptr http; + public: + Network(std::unique_ptr http); + ~Network(); + + void httpGet(const std::string& url, const OnResponse& callback); + + static std::unique_ptr create(const NetworkSettings& settings); + }; +} diff --git a/src/settings.hpp b/src/settings.hpp index 8343ee68..b0eee035 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -81,6 +81,9 @@ struct UiSettings { IntegerSetting worldPreviewSize {64, 1, 512}; }; +struct NetworkSettings { +}; + struct EngineSettings { AudioSettings audio; DisplaySettings display; @@ -89,4 +92,5 @@ struct EngineSettings { GraphicsSettings graphics; DebugSettings debug; UiSettings ui; + NetworkSettings network; }; diff --git a/test/curltest.cpp b/test/curltest.cpp deleted file mode 100644 index a8d3424e..00000000 --- a/test/curltest.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -#include - -size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) { - return size * nmemb; -} - -TEST(curltest, curltest) { - if (CURL* curl = curl_easy_init()) { - CURLcode res; - curl_easy_setopt(curl, CURLOPT_URL, "https://github.com"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); - res = curl_easy_perform(curl); - std::cout << curl_easy_strerror(res) << std::endl; - curl_easy_cleanup(curl); - } -} diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp new file mode 100644 index 00000000..ea4924be --- /dev/null +++ b/test/network/curltest.cpp @@ -0,0 +1,18 @@ +#include + +#include "network/Network.hpp" +#include "coders/json.hpp" + +TEST(curltest, curltest) { + NetworkSettings settings {}; + auto network = network::Network::create(settings); + network->httpGet( + "https://raw.githubusercontent.com/MihailRis/VoxelEngine-Cpp/refs/" + "heads/curl/res/content/base/blocks/lamp.json", + [=](int code, std::vector data) { + auto v = std::string_view(data.data(), data.size()); + auto value = json::parse(v); + std::cout << value << std::endl; + } + ); +} From d7389c222094b852265c8d8007875d6a255b36e4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 11 Nov 2024 04:26:07 +0300 Subject: [PATCH 04/56] add Network.getTotalUpload, getTotalDownload --- src/network/Network.cpp | 29 +++++++++++++++++++++++++++++ src/network/Network.hpp | 5 +++++ test/network/curltest.cpp | 2 ++ 3 files changed, 36 insertions(+) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index bdd2a2ac..b91e51df 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -19,6 +19,9 @@ size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) { class CurlHttp : public Http { CURL* curl; + + size_t totalUpload = 0; + size_t totalDownload = 0; public: CurlHttp(CURL* curl) : curl(curl) { } @@ -33,9 +36,27 @@ public: curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); CURLcode res = curl_easy_perform(curl); + if (res == CURLE_OK) { + long size; + if (!curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &size)) { + totalUpload += size; + } + if (!curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size)) { + totalDownload += size; + } + totalDownload += buffer.size(); + } callback(res, std::move(buffer)); } + size_t getTotalUpload() const override { + return totalUpload; + } + + size_t getTotalDownload() const override { + return totalDownload; + } + static std::unique_ptr create() { if (auto curl = curl_easy_init()) { return std::make_unique(curl); @@ -54,6 +75,14 @@ void Network::httpGet(const std::string& url, const OnResponse& callback) { http->get(url, callback); } +size_t Network::getTotalUpload() const { + return http->getTotalUpload(); +} + +size_t Network::getTotalDownload() const { + return http->getTotalDownload(); +} + std::unique_ptr Network::create(const NetworkSettings& settings) { auto http = CurlHttp::create(); return std::make_unique(std::move(http)); diff --git a/src/network/Network.hpp b/src/network/Network.hpp index 2e92a302..c3a131da 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -16,6 +16,8 @@ namespace network { virtual ~Http() {} virtual void get(const std::string& url, const OnResponse& callback) = 0; + virtual size_t getTotalUpload() const = 0; + virtual size_t getTotalDownload() const = 0; }; class Network { @@ -26,6 +28,9 @@ namespace network { void httpGet(const std::string& url, const OnResponse& callback); + size_t getTotalUpload() const; + size_t getTotalDownload() const; + static std::unique_ptr create(const NetworkSettings& settings); }; } diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index ea4924be..ade22242 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -15,4 +15,6 @@ TEST(curltest, curltest) { std::cout << value << std::endl; } ); + std::cout << "upload: " << network->getTotalUpload() << " B" << std::endl; + std::cout << "download: " << network->getTotalDownload() << " B" << std::endl; } From 4328c83c79dde82d256b2e748626a29c28bccbdb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 11 Nov 2024 14:47:09 +0300 Subject: [PATCH 05/56] add httpGet error handling --- src/network/Network.cpp | 23 ++++++++++++++++++----- src/network/Network.hpp | 15 ++++++++++++--- test/network/curltest.cpp | 9 ++++++--- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index b91e51df..a2b98680 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -9,7 +9,9 @@ using namespace network; static debug::Logger logger("network"); -size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) { +static size_t write_callback( + char* ptr, size_t size, size_t nmemb, void* userdata +) { auto& buffer = *reinterpret_cast*>(userdata); size_t psize = buffer.size(); buffer.resize(psize + size * nmemb); @@ -30,7 +32,8 @@ public: curl_easy_cleanup(curl); } - void get(const std::string& url, const OnResponse& callback) override { + void get(const std::string& url, OnResponse onResponse, OnReject onReject) + override { std::vector buffer; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); @@ -45,8 +48,16 @@ public: totalDownload += size; } totalDownload += buffer.size(); + if (onResponse) { + onResponse(std::move(buffer)); + } + } else { + auto message = curl_easy_strerror(res); + logger.error() << message << " (" << url << ")"; + if (onReject) { + onReject(message); + } } - callback(res, std::move(buffer)); } size_t getTotalUpload() const override { @@ -71,8 +82,10 @@ Network::Network(std::unique_ptr http) : http(std::move(http)) { Network::~Network() = default; -void Network::httpGet(const std::string& url, const OnResponse& callback) { - http->get(url, callback); +void Network::httpGet( + const std::string& url, OnResponse onResponse, OnReject onReject +) { + http->get(url, onResponse, onReject); } size_t Network::getTotalUpload() const { diff --git a/src/network/Network.hpp b/src/network/Network.hpp index c3a131da..fbf77926 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -9,13 +9,18 @@ #include "util/Buffer.hpp" namespace network { - using OnResponse = std::function)>; + using OnResponse = std::function)>; + using OnReject = std::function; class Http { public: virtual ~Http() {} - virtual void get(const std::string& url, const OnResponse& callback) = 0; + virtual void get( + const std::string& url, + OnResponse onResponse, + OnReject onReject=nullptr + ) = 0; virtual size_t getTotalUpload() const = 0; virtual size_t getTotalDownload() const = 0; }; @@ -26,7 +31,11 @@ namespace network { Network(std::unique_ptr http); ~Network(); - void httpGet(const std::string& url, const OnResponse& callback); + void httpGet( + const std::string& url, + OnResponse onResponse, + OnReject onReject = nullptr + ); size_t getTotalUpload() const; size_t getTotalDownload() const; diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index ade22242..ac4d04dd 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -9,9 +9,12 @@ TEST(curltest, curltest) { network->httpGet( "https://raw.githubusercontent.com/MihailRis/VoxelEngine-Cpp/refs/" "heads/curl/res/content/base/blocks/lamp.json", - [=](int code, std::vector data) { - auto v = std::string_view(data.data(), data.size()); - auto value = json::parse(v); + [](std::vector data) { + if (data.empty()) { + return; + } + auto view = std::string_view(data.data(), data.size()); + auto value = json::parse(view); std::cout << value << std::endl; } ); From dc84fe1f0731311aa0c6f49138fac6090b3b87c4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 11 Nov 2024 19:55:23 +0300 Subject: [PATCH 06/56] add Network.connect (WIP) --- src/network/Network.cpp | 187 +++++++++++++++++++++++++++++++++++++- src/network/Network.hpp | 27 +++++- test/network/curltest.cpp | 10 ++ 3 files changed, 218 insertions(+), 6 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index a2b98680..8766ff28 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -77,7 +77,171 @@ public: }; -Network::Network(std::unique_ptr http) : http(std::move(http)) { +#ifdef _WIN32 +/// ... +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifndef _WIN32 +static inline int closesocket(int descriptor) noexcept { + return close(descriptor); +} +#endif + +static inline int connectsocket( + int descriptor, const sockaddr* addr, socklen_t len +) noexcept { + return connect(descriptor, addr, len); +} + +static inline int recvsocket( + int descriptor, void* buf, size_t len, int flags +) noexcept { + return recv(descriptor, buf, len, flags); +} + +static inline int sendsocket( + int descriptor, const void* buf, size_t len, int flags +) noexcept { + return send(descriptor, buf, len, flags); +} + +static std::string to_string(const addrinfo* addr) { + if (addr->ai_family == AF_INET) { + auto psai = reinterpret_cast(addr->ai_addr); + char ip[INET_ADDRSTRLEN]; + if (inet_ntop(addr->ai_family, &(psai->sin_addr), ip, INET_ADDRSTRLEN)) { + return std::string(ip); + } + } else if (addr->ai_family == AF_INET6) { + auto psai = reinterpret_cast(addr->ai_addr); + char ip[INET6_ADDRSTRLEN]; + if (inet_ntop(addr->ai_family, &(psai->sin6_addr), ip, INET6_ADDRSTRLEN)) { + return std::string(ip); + } + } + return ""; +} + +class SocketImpl : public Socket { + int descriptor; + bool open = true; + addrinfo* addr; + size_t totalUpload = 0; + size_t totalDownload = 0; +public: + SocketImpl(int descriptor, addrinfo* addr) + : descriptor(descriptor), addr(addr) { + } + + ~SocketImpl() { + closesocket(descriptor); + freeaddrinfo(addr); + } + + int recv(void* buffer, size_t length, bool blocking) override { + int len = recvsocket(descriptor, buffer, length, blocking ? MSG_WAITALL : MSG_DONTWAIT); + if (len == 0) { + int err = errno; + close(); + throw std::runtime_error( + "Read failed [errno=" + std::to_string(err) + + "]: " + std::string(strerror(err)) + ); + } else if (len == -1) { + return 0; + } + totalDownload += len; + return len; + } + + int send(const void* buffer, size_t length) override { + int len = sendsocket(descriptor, buffer, length, 0); + if (len == -1) { + int err = errno; + close(); + throw std::runtime_error( + "Send failed [errno=" + std::to_string(err) + + "]: " + std::string(strerror(err)) + ); + } + totalUpload += len; + return len; + } + + void close() override { + closesocket(descriptor); + open = false; + } + + bool isOpen() const override { + return open; + } + + size_t getTotalUpload() const override { + return totalUpload; + } + + size_t getTotalDownload() const override { + return totalDownload; + } + + static std::shared_ptr connect( + const std::string& address, int port + ) { + addrinfo hints {}; + + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + addrinfo* addrinfo; + if (int res = getaddrinfo( + address.c_str(), std::to_string(port).c_str(), &hints, &addrinfo + )) { + throw std::runtime_error(gai_strerror(res)); + } + int descriptor = socket( + addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol + ); + if (descriptor == -1) { + freeaddrinfo(addrinfo); + throw std::runtime_error("Could not create socket"); + } + int res = connectsocket(descriptor, addrinfo->ai_addr, addrinfo->ai_addrlen); + if (res == -1) { + closesocket(descriptor); + freeaddrinfo(addrinfo); + + int err = errno; + throw std::runtime_error( + "Connect failed [errno=" + std::to_string(err) + + "]: " + std::string(strerror(err)) + ); + } + logger.info() << "connected to " << address << " [" + << to_string(addrinfo) << ":" << port << "]"; + return std::make_shared(descriptor, addrinfo); + } +}; + +class SocketTcp : public Tcp { +public: + SocketTcp() {}; + + std::shared_ptr connect(const std::string& address, int port) override { + return SocketImpl::connect(address, port); + } +}; + +Network::Network(std::unique_ptr http, std::unique_ptr tcp) + : http(std::move(http)), tcp(std::move(tcp)) { } Network::~Network() = default; @@ -88,15 +252,30 @@ void Network::httpGet( http->get(url, onResponse, onReject); } +std::shared_ptr Network::connect(const std::string& address, int port) { + auto socket = tcp->connect(address, port); + connections.push_back(socket); + return socket; +} + size_t Network::getTotalUpload() const { - return http->getTotalUpload(); + size_t totalUpload = 0; + for (const auto& socket : connections) { + totalUpload += socket->getTotalUpload(); + } + return http->getTotalUpload() + totalUpload; } size_t Network::getTotalDownload() const { - return http->getTotalDownload(); + size_t totalDownload = 0; + for (const auto& socket : connections) { + totalDownload += socket->getTotalDownload(); + } + return http->getTotalDownload() + totalDownload; } std::unique_ptr Network::create(const NetworkSettings& settings) { auto http = CurlHttp::create(); - return std::make_unique(std::move(http)); + auto tcp = std::make_unique(); + return std::make_unique(std::move(http), std::move(tcp)); } diff --git a/src/network/Network.hpp b/src/network/Network.hpp index fbf77926..099d8df7 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -2,7 +2,6 @@ #include #include -#include #include "typedefs.hpp" #include "settings.hpp" @@ -25,10 +24,32 @@ namespace network { virtual size_t getTotalDownload() const = 0; }; + class Socket { + public: + virtual int recv(void* buffer, size_t length, bool blocking) = 0; + virtual int send(const void* buffer, size_t length) = 0; + virtual void close() = 0; + virtual bool isOpen() const = 0; + + virtual size_t getTotalUpload() const = 0; + virtual size_t getTotalDownload() const = 0; + }; + + class Tcp { + public: + virtual ~Tcp() {} + + virtual std::shared_ptr connect( + const std::string& address, int port + ) = 0; + }; + class Network { std::unique_ptr http; + std::unique_ptr tcp; + std::vector> connections; public: - Network(std::unique_ptr http); + Network(std::unique_ptr http, std::unique_ptr tcp); ~Network(); void httpGet( @@ -37,6 +58,8 @@ namespace network { OnReject onReject = nullptr ); + std::shared_ptr connect(const std::string& address, int port); + size_t getTotalUpload() const; size_t getTotalDownload() const; diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index ac4d04dd..79ab3332 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -18,6 +18,16 @@ TEST(curltest, curltest) { std::cout << value << std::endl; } ); + if (false) { + auto socket = network->connect("localhost", 8000); + const char* string = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; + socket->send(string, strlen(string)); + char data[1024]; + + int len = socket->recv(data, 1024, true); + std::cout << len << " " << std::string(data, len) << std::endl; + } + std::cout << "upload: " << network->getTotalUpload() << " B" << std::endl; std::cout << "download: " << network->getTotalDownload() << " B" << std::endl; } From 40ba7705b25bcc8a4cc4a1152863ee3cc7adba50 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 11 Nov 2024 20:09:56 +0300 Subject: [PATCH 07/56] update Socket.send, recv --- src/network/Network.cpp | 8 ++++---- src/network/Network.hpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 8766ff28..2ba72eaf 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -102,13 +102,13 @@ static inline int connectsocket( } static inline int recvsocket( - int descriptor, void* buf, size_t len, int flags + int descriptor, char* buf, size_t len, int flags ) noexcept { return recv(descriptor, buf, len, flags); } static inline int sendsocket( - int descriptor, const void* buf, size_t len, int flags + int descriptor, const char* buf, size_t len, int flags ) noexcept { return send(descriptor, buf, len, flags); } @@ -146,7 +146,7 @@ public: freeaddrinfo(addr); } - int recv(void* buffer, size_t length, bool blocking) override { + int recv(char* buffer, size_t length, bool blocking) override { int len = recvsocket(descriptor, buffer, length, blocking ? MSG_WAITALL : MSG_DONTWAIT); if (len == 0) { int err = errno; @@ -162,7 +162,7 @@ public: return len; } - int send(const void* buffer, size_t length) override { + int send(const char* buffer, size_t length) override { int len = sendsocket(descriptor, buffer, length, 0); if (len == -1) { int err = errno; diff --git a/src/network/Network.hpp b/src/network/Network.hpp index 099d8df7..4b89ae1f 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -26,8 +26,8 @@ namespace network { class Socket { public: - virtual int recv(void* buffer, size_t length, bool blocking) = 0; - virtual int send(const void* buffer, size_t length) = 0; + virtual int recv(char* buffer, size_t length, bool blocking) = 0; + virtual int send(const char* buffer, size_t length) = 0; virtual void close() = 0; virtual bool isOpen() const = 0; From cc603f5c9f0e466c19fbced5965db59d9b760c09 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 15 Nov 2024 06:44:32 +0300 Subject: [PATCH 08/56] update workflows --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 96aa3c39..250fd001 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -2,7 +2,7 @@ name: C/C++ AppImage on: push: - branches: [ "main", "release-**", "curl"] + branches: [ "main", "release-**"] pull_request: branches: [ "main" ] diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c8050bcb..e4ddf55a 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -2,7 +2,7 @@ name: Macos Build on: push: - branches: [ "main", "release-**", "curl"] + branches: [ "main", "release-**"] pull_request: branches: [ "main" ] diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c5c52290..b14c32d0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -2,7 +2,7 @@ name: Windows Build on: push: - branches: [ "main", "release-**", "curl"] + branches: [ "main", "release-**"] pull_request: branches: [ "main" ] From ee0b7457698262f067dbbc22d482835b73bde230 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 15 Nov 2024 06:50:51 +0300 Subject: [PATCH 09/56] update .github/workflows/cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 343d35e7..69fb0a46 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -39,7 +39,7 @@ jobs: # make && make install INSTALL_INC=/usr/include/lua run: | sudo apt-get update - sudo apt-get install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libgtest-dev + sudo apt-get install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libgtest-dev libcurl4-openssl-dev # fix luajit paths sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua-5.1.a sudo ln -s /usr/include/luajit-2.1 /usr/include/lua From 4aacd130ab6d5e56eb2298c1e3e02a6f573c0361 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 15 Nov 2024 07:09:55 +0300 Subject: [PATCH 10/56] update Socket interface --- src/network/Network.cpp | 8 ++++---- src/network/Network.hpp | 2 +- test/network/curltest.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 2ba72eaf..3e05b1b0 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -102,9 +102,9 @@ static inline int connectsocket( } static inline int recvsocket( - int descriptor, char* buf, size_t len, int flags + int descriptor, char* buf, size_t len ) noexcept { - return recv(descriptor, buf, len, flags); + return recv(descriptor, buf, len, 0); } static inline int sendsocket( @@ -146,8 +146,8 @@ public: freeaddrinfo(addr); } - int recv(char* buffer, size_t length, bool blocking) override { - int len = recvsocket(descriptor, buffer, length, blocking ? MSG_WAITALL : MSG_DONTWAIT); + int recv(char* buffer, size_t length) override { + int len = recvsocket(descriptor, buffer, length); if (len == 0) { int err = errno; close(); diff --git a/src/network/Network.hpp b/src/network/Network.hpp index 4b89ae1f..c5475834 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -26,7 +26,7 @@ namespace network { class Socket { public: - virtual int recv(char* buffer, size_t length, bool blocking) = 0; + virtual int recv(char* buffer, size_t length) = 0; virtual int send(const char* buffer, size_t length) = 0; virtual void close() = 0; virtual bool isOpen() const = 0; diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index 79ab3332..50add51e 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -24,7 +24,7 @@ TEST(curltest, curltest) { socket->send(string, strlen(string)); char data[1024]; - int len = socket->recv(data, 1024, true); + int len = socket->recv(data, 1024); std::cout << len << " " << std::string(data, len) << std::endl; } From 0a3daad86e260152c55559fcf8963c423124ed6a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 15 Nov 2024 07:23:41 +0300 Subject: [PATCH 11/56] fix --- src/network/Network.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 3e05b1b0..8ecb3356 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -1,5 +1,7 @@ #include "Network.hpp" +#pragma comment(lib, "Ws2_32.lib") + #include #include From 81e8e3081a441361fe884f1745a2600875feb917 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 15 Nov 2024 07:24:30 +0300 Subject: [PATCH 12/56] Revert "update workflows" This reverts commit cc603f5c9f0e466c19fbced5965db59d9b760c09. --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 250fd001..96aa3c39 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -2,7 +2,7 @@ name: C/C++ AppImage on: push: - branches: [ "main", "release-**"] + branches: [ "main", "release-**", "curl"] pull_request: branches: [ "main" ] diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e4ddf55a..c8050bcb 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -2,7 +2,7 @@ name: Macos Build on: push: - branches: [ "main", "release-**"] + branches: [ "main", "release-**", "curl"] pull_request: branches: [ "main" ] diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b14c32d0..c5c52290 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -2,7 +2,7 @@ name: Windows Build on: push: - branches: [ "main", "release-**"] + branches: [ "main", "release-**", "curl"] pull_request: branches: [ "main" ] From 5092cff73090c6a3eac2a61b591378f8e5a81c1f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 21 Nov 2024 09:01:39 +0300 Subject: [PATCH 13/56] update workflows --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 96aa3c39..250fd001 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -2,7 +2,7 @@ name: C/C++ AppImage on: push: - branches: [ "main", "release-**", "curl"] + branches: [ "main", "release-**"] pull_request: branches: [ "main" ] diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c8050bcb..e4ddf55a 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -2,7 +2,7 @@ name: Macos Build on: push: - branches: [ "main", "release-**", "curl"] + branches: [ "main", "release-**"] pull_request: branches: [ "main" ] diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c5c52290..b14c32d0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -2,7 +2,7 @@ name: Windows Build on: push: - branches: [ "main", "release-**", "curl"] + branches: [ "main", "release-**"] pull_request: branches: [ "main" ] From 0fec17a8b69ac81255b77022f3af5addf8fcc8f8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 22 Nov 2024 07:13:49 +0300 Subject: [PATCH 14/56] fix file.write_bytes --- src/logic/scripting/lua/libs/libfile.cpp | 60 +++++++++--------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/src/logic/scripting/lua/libs/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp index d436bdfd..1e6beb25 100644 --- a/src/logic/scripting/lua/libs/libfile.cpp +++ b/src/logic/scripting/lua/libs/libfile.cpp @@ -149,24 +149,24 @@ static int l_read_bytes(lua::State* L) { ); } -static int read_bytes_from_table( +static void read_bytes_from_table( lua::State* L, int tableIndex, std::vector& bytes ) { if (!lua::istable(L, tableIndex)) { throw std::runtime_error("table expected"); } else { - lua::pushnil(L); - while (lua::next(L, tableIndex - 1) != 0) { + size_t size = lua::objlen(L, tableIndex); + for (size_t i = 0; i < size; i++) { + lua::rawgeti(L, i + 1, tableIndex); const int byte = lua::tointeger(L, -1); + lua::pop(L); if (byte < 0 || byte > 255) { throw std::runtime_error( "invalid byte '" + std::to_string(byte) + "'" ); } bytes.push_back(byte); - lua::pop(L); } - return 1; } } @@ -181,14 +181,10 @@ static int l_write_bytes(lua::State* L) { } std::vector bytes; - int result = read_bytes_from_table(L, -1, bytes); - if (result != 1) { - return result; - } else { - return lua::pushboolean( - L, files::write_bytes(path, bytes.data(), bytes.size()) - ); - } + read_bytes_from_table(L, 2, bytes); + return lua::pushboolean( + L, files::write_bytes(path, bytes.data(), bytes.size()) + ); } static int l_list_all_res(lua::State* L, const std::string& path) { @@ -227,39 +223,29 @@ static int l_list(lua::State* L) { static int l_gzip_compress(lua::State* L) { std::vector bytes; - int result = read_bytes_from_table(L, -1, bytes); + read_bytes_from_table(L, 1, bytes); + auto compressed_bytes = gzip::compress(bytes.data(), bytes.size()); + int newTable = lua::gettop(L); - if (result != 1) { - return result; - } else { - auto compressed_bytes = gzip::compress(bytes.data(), bytes.size()); - int newTable = lua::gettop(L); - - for (size_t i = 0; i < compressed_bytes.size(); i++) { - lua::pushinteger(L, compressed_bytes.data()[i]); - lua::rawseti(L, i + 1, newTable); - } - return 1; + for (size_t i = 0; i < compressed_bytes.size(); i++) { + lua::pushinteger(L, compressed_bytes.data()[i]); + lua::rawseti(L, i + 1, newTable); } + return 1; } static int l_gzip_decompress(lua::State* L) { std::vector bytes; - int result = read_bytes_from_table(L, -1, bytes); + read_bytes_from_table(L, 1, bytes); + auto decompressed_bytes = gzip::decompress(bytes.data(), bytes.size()); + int newTable = lua::gettop(L); - if (result != 1) { - return result; - } else { - auto decompressed_bytes = gzip::decompress(bytes.data(), bytes.size()); - int newTable = lua::gettop(L); - - for (size_t i = 0; i < decompressed_bytes.size(); i++) { - lua::pushinteger(L, decompressed_bytes.data()[i]); - lua::rawseti(L, i + 1, newTable); - } - return 1; + for (size_t i = 0; i < decompressed_bytes.size(); i++) { + lua::pushinteger(L, decompressed_bytes.data()[i]); + lua::rawseti(L, i + 1, newTable); } + return 1; } static int l_read_combined_list(lua::State* L) { From 186078a8d5b566e6c06fa42f55da5f44495acf87 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 22 Nov 2024 07:28:18 +0300 Subject: [PATCH 15/56] make httpGet non-blocking --- src/network/Network.cpp | 143 +++++++++++++++++++++++++++++++++------- src/network/Network.hpp | 10 ++- 2 files changed, 128 insertions(+), 25 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 8ecb3356..a95f6bea 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "debug/Logger.hpp" @@ -21,44 +22,127 @@ static size_t write_callback( return size * nmemb; } +struct Request { + std::string url; + OnResponse onResponse; + OnReject onReject; + long maxSize; +}; + class CurlHttp : public Http { + CURLM* multiHandle; CURL* curl; size_t totalUpload = 0; size_t totalDownload = 0; + + OnResponse onResponse; + OnReject onReject; + std::vector buffer; + std::string url; + + std::queue requests; public: - CurlHttp(CURL* curl) : curl(curl) { + CurlHttp(CURLM* multiHandle, CURL* curl) + : multiHandle(multiHandle), curl(curl) { } virtual ~CurlHttp() { curl_easy_cleanup(curl); + curl_multi_remove_handle(multiHandle, curl); + curl_multi_cleanup(multiHandle); } - void get(const std::string& url, OnResponse onResponse, OnReject onReject) - override { - std::vector buffer; + void get( + const std::string& url, + OnResponse onResponse, + OnReject onReject, + long maxSize + ) override { + Request request {url, onResponse, onReject, maxSize}; + if (url.empty()) { + processRequest(request); + } else { + requests.push(request); + } + } + + void processRequest(const Request& request) { + onResponse = request.onResponse; + onReject = request.onReject; + url = request.url; + + buffer.clear(); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); - CURLcode res = curl_easy_perform(curl); - if (res == CURLE_OK) { - long size; - if (!curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &size)) { - totalUpload += size; - } - if (!curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size)) { - totalDownload += size; - } - totalDownload += buffer.size(); - if (onResponse) { - onResponse(std::move(buffer)); - } + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); + if (request.maxSize == 0) { + curl_easy_setopt( + curl, CURLOPT_MAXFILESIZE, std::numeric_limits::max() + ); } else { - auto message = curl_easy_strerror(res); + curl_easy_setopt(curl, CURLOPT_MAXFILESIZE, request.maxSize); + } + curl_multi_add_handle(multiHandle, curl); + int running; + CURLMcode res = curl_multi_perform(multiHandle, &running); + if (res != CURLM_OK) { + auto message = curl_multi_strerror(res); logger.error() << message << " (" << url << ")"; if (onReject) { onReject(message); } + url = ""; + } + } + + void update() override { + int messagesLeft; + int running; + CURLMsg* msg; + CURLMcode res = curl_multi_perform(multiHandle, &running); + if (res != CURLM_OK) { + auto message = curl_multi_strerror(res); + logger.error() << message << " (" << url << ")"; + if (onReject) { + onReject(message); + } + curl_multi_remove_handle(multiHandle, curl); + url = ""; + return; + } + if ((msg = curl_multi_info_read(multiHandle, &messagesLeft)) != NULL) { + if(msg->msg == CURLMSG_DONE) { + curl_multi_remove_handle(multiHandle, curl); + } + int response; + curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response); + if (response == 200) { + long size; + if (!curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &size)) { + totalUpload += size; + } + if (!curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size)) { + totalDownload += size; + } + totalDownload += buffer.size(); + if (onResponse) { + onResponse(std::move(buffer)); + } + } else { + logger.error() << "response code " << response << " (" << url << ")"; + if (onReject) { + onReject(std::to_string(response).c_str()); + } + } + url = ""; + } + if (url.empty() && !requests.empty()) { + auto request = std::move(requests.front()); + requests.pop(); + processRequest(request); } } @@ -71,10 +155,16 @@ public: } static std::unique_ptr create() { - if (auto curl = curl_easy_init()) { - return std::make_unique(curl); + auto curl = curl_easy_init(); + if (curl == nullptr) { + throw std::runtime_error("could not initialzie cURL"); } - throw std::runtime_error("could not initialzie cURL"); + auto multiHandle = curl_multi_init(); + if (multiHandle == nullptr) { + curl_easy_cleanup(curl); + throw std::runtime_error("could not initialzie cURL-multi"); + } + return std::make_unique(multiHandle, curl); } }; @@ -249,9 +339,12 @@ Network::Network(std::unique_ptr http, std::unique_ptr tcp) Network::~Network() = default; void Network::httpGet( - const std::string& url, OnResponse onResponse, OnReject onReject + const std::string& url, + OnResponse onResponse, + OnReject onReject, + long maxSize ) { - http->get(url, onResponse, onReject); + http->get(url, onResponse, onReject, maxSize); } std::shared_ptr Network::connect(const std::string& address, int port) { @@ -276,6 +369,10 @@ size_t Network::getTotalDownload() const { return http->getTotalDownload() + totalDownload; } +void Network::update() { + http->update(); +} + std::unique_ptr Network::create(const NetworkSettings& settings) { auto http = CurlHttp::create(); auto tcp = std::make_unique(); diff --git a/src/network/Network.hpp b/src/network/Network.hpp index c5475834..ef5728f8 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -18,10 +18,13 @@ namespace network { virtual void get( const std::string& url, OnResponse onResponse, - OnReject onReject=nullptr + OnReject onReject=nullptr, + long maxSize=0 ) = 0; virtual size_t getTotalUpload() const = 0; virtual size_t getTotalDownload() const = 0; + + virtual void update() = 0; }; class Socket { @@ -55,7 +58,8 @@ namespace network { void httpGet( const std::string& url, OnResponse onResponse, - OnReject onReject = nullptr + OnReject onReject = nullptr, + long maxSize=0 ); std::shared_ptr connect(const std::string& address, int port); @@ -63,6 +67,8 @@ namespace network { size_t getTotalUpload() const; size_t getTotalDownload() const; + void update(); + static std::unique_ptr create(const NetworkSettings& settings); }; } From b23318a06cb573c4af3d82163cb051d60f7fe98c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 22 Nov 2024 07:30:38 +0300 Subject: [PATCH 16/56] add network.http_get, network.http_get_binary --- src/engine.cpp | 10 ++++- src/engine.hpp | 7 ++++ src/logic/scripting/lua/libs/api_lua.hpp | 1 + src/logic/scripting/lua/libs/libnetwork.cpp | 43 +++++++++++++++++++++ src/logic/scripting/lua/lua_engine.cpp | 1 + test/network/curltest.cpp | 2 +- 6 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/logic/scripting/lua/libs/libnetwork.cpp diff --git a/src/engine.cpp b/src/engine.cpp index 85a9e42b..800a20d3 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -30,6 +30,7 @@ #include "logic/EngineController.hpp" #include "logic/CommandsInterpreter.hpp" #include "logic/scripting/scripting.hpp" +#include "network/Network.hpp" #include "util/listutil.hpp" #include "util/platform.hpp" #include "window/Camera.hpp" @@ -72,7 +73,8 @@ static std::unique_ptr load_icon(const fs::path& resdir) { Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths) : settings(settings), settingsHandler(settingsHandler), paths(paths), - interpreter(std::make_unique()) + interpreter(std::make_unique()), + network(network::Network::create(settings.network)) { paths->prepare(); loadSettings(); @@ -191,6 +193,7 @@ void Engine::mainloop() { : settings.display.framerate.get() ); + network->update(); processPostRunnables(); Window::swapBuffers(); @@ -235,6 +238,7 @@ Engine::~Engine() { gui.reset(); logger.info() << "gui finished"; audio::close(); + network.reset(); scripting::close(); logger.info() << "scripting finished"; Window::terminate(); @@ -482,3 +486,7 @@ void Engine::postRunnable(const runnable& callback) { SettingsHandler& Engine::getSettingsHandler() { return settingsHandler; } + +network::Network& Engine::getNetwork() { + return *network; +} diff --git a/src/engine.hpp b/src/engine.hpp index 75b9a1b7..891606a5 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -37,6 +37,10 @@ namespace cmd { class CommandsInterpreter; } +namespace network { + class Network; +} + class initialize_error : public std::runtime_error { public: initialize_error(const std::string& message) : std::runtime_error(message) {} @@ -56,6 +60,7 @@ class Engine : public util::ObjectsKeeper { std::recursive_mutex postRunnablesMutex; std::unique_ptr controller; std::unique_ptr interpreter; + std::unique_ptr network; std::vector basePacks; uint64_t frame = 0; @@ -148,4 +153,6 @@ public: PacksManager createPacksManager(const fs::path& worldFolder); SettingsHandler& getSettingsHandler(); + + network::Network& getNetwork(); }; diff --git a/src/logic/scripting/lua/libs/api_lua.hpp b/src/logic/scripting/lua/libs/api_lua.hpp index 12df1d66..a3b2d152 100644 --- a/src/logic/scripting/lua/libs/api_lua.hpp +++ b/src/logic/scripting/lua/libs/api_lua.hpp @@ -32,6 +32,7 @@ extern const luaL_Reg inventorylib[]; extern const luaL_Reg itemlib[]; extern const luaL_Reg jsonlib[]; extern const luaL_Reg mat4lib[]; +extern const luaL_Reg networklib[]; extern const luaL_Reg packlib[]; extern const luaL_Reg particleslib[]; // gfx.particles extern const luaL_Reg playerlib[]; diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp new file mode 100644 index 00000000..48016f29 --- /dev/null +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -0,0 +1,43 @@ +#include "api_lua.hpp" + +#include "engine.hpp" +#include "network/Network.hpp" + +using namespace scripting; + +static int l_http_get(lua::State* L) { + std::string url(lua::require_lstring(L, 1)); + + lua::pushvalue(L, 2); + auto onResponse = lua::create_lambda(L); + + engine->getNetwork().httpGet(url, [onResponse](std::vector bytes) { + engine->postRunnable([=]() { + onResponse({std::string(bytes.data(), bytes.size())}); + }); + }); + return 0; +} + +static int l_http_get_binary(lua::State* L) { + std::string url(lua::require_lstring(L, 1)); + + lua::pushvalue(L, 2); + auto onResponse = lua::create_lambda(L); + + engine->getNetwork().httpGet(url, [onResponse](std::vector bytes) { + auto buffer = std::make_shared>( + reinterpret_cast(bytes.data()), bytes.size() + ); + engine->postRunnable([=]() { + onResponse({buffer}); + }); + }); + return 0; +} + +const luaL_Reg networklib[] = { + {"http_get", lua::wrap}, + {"http_get_binary", lua::wrap}, + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 93585c41..8f477710 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -65,6 +65,7 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "audio", audiolib); openlib(L, "console", consolelib); openlib(L, "player", playerlib); + openlib(L, "network", networklib); openlib(L, "entities", entitylib); openlib(L, "cameras", cameralib); diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index 50add51e..25f7c3cf 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -16,7 +16,7 @@ TEST(curltest, curltest) { auto view = std::string_view(data.data(), data.size()); auto value = json::parse(view); std::cout << value << std::endl; - } + }, [](auto){} ); if (false) { auto socket = network->connect("localhost", 8000); From 988adce975862d6195b581c290be58c0b4bca033 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 22 Nov 2024 07:42:00 +0300 Subject: [PATCH 17/56] add missing include --- src/network/Network.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index a95f6bea..76b58b04 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "debug/Logger.hpp" From e2b4239ffb00616741e95b7b7302a1713ea0cfe5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 22 Nov 2024 08:01:59 +0300 Subject: [PATCH 18/56] fix --- src/network/Network.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 76b58b04..3350ba2b 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -2,6 +2,7 @@ #pragma comment(lib, "Ws2_32.lib") +#define NOMINMAX #include #include #include From 5e063d9fe00525a60c59f91405c728f8102b211e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 23 Nov 2024 09:49:17 +0300 Subject: [PATCH 19/56] refactor Network --- src/logic/scripting/lua/libs/libnetwork.cpp | 4 +-- src/network/Network.cpp | 40 ++++++++------------- src/network/Network.hpp | 20 +++-------- test/network/curltest.cpp | 2 +- 4 files changed, 23 insertions(+), 43 deletions(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 48016f29..0add7721 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -11,7 +11,7 @@ static int l_http_get(lua::State* L) { lua::pushvalue(L, 2); auto onResponse = lua::create_lambda(L); - engine->getNetwork().httpGet(url, [onResponse](std::vector bytes) { + engine->getNetwork().get(url, [onResponse](std::vector bytes) { engine->postRunnable([=]() { onResponse({std::string(bytes.data(), bytes.size())}); }); @@ -25,7 +25,7 @@ static int l_http_get_binary(lua::State* L) { lua::pushvalue(L, 2); auto onResponse = lua::create_lambda(L); - engine->getNetwork().httpGet(url, [onResponse](std::vector bytes) { + engine->getNetwork().get(url, [onResponse](std::vector bytes) { auto buffer = std::make_shared>( reinterpret_cast(bytes.data()), bytes.size() ); diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 3350ba2b..c4e9b004 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -31,7 +31,7 @@ struct Request { long maxSize; }; -class CurlHttp : public Http { +class CurlRequests : public Requests { CURLM* multiHandle; CURL* curl; @@ -45,11 +45,11 @@ class CurlHttp : public Http { std::queue requests; public: - CurlHttp(CURLM* multiHandle, CURL* curl) + CurlRequests(CURLM* multiHandle, CURL* curl) : multiHandle(multiHandle), curl(curl) { } - virtual ~CurlHttp() { + virtual ~CurlRequests() { curl_easy_cleanup(curl); curl_multi_remove_handle(multiHandle, curl); curl_multi_cleanup(multiHandle); @@ -156,7 +156,7 @@ public: return totalDownload; } - static std::unique_ptr create() { + static std::unique_ptr create() { auto curl = curl_easy_init(); if (curl == nullptr) { throw std::runtime_error("could not initialzie cURL"); @@ -166,7 +166,7 @@ public: curl_easy_cleanup(curl); throw std::runtime_error("could not initialzie cURL-multi"); } - return std::make_unique(multiHandle, curl); + return std::make_unique(multiHandle, curl); } }; @@ -325,32 +325,23 @@ public: } }; -class SocketTcp : public Tcp { -public: - SocketTcp() {}; - - std::shared_ptr connect(const std::string& address, int port) override { - return SocketImpl::connect(address, port); - } -}; - -Network::Network(std::unique_ptr http, std::unique_ptr tcp) - : http(std::move(http)), tcp(std::move(tcp)) { +Network::Network(std::unique_ptr requests) + : requests(std::move(requests)) { } Network::~Network() = default; -void Network::httpGet( +void Network::get( const std::string& url, OnResponse onResponse, OnReject onReject, long maxSize ) { - http->get(url, onResponse, onReject, maxSize); + requests->get(url, onResponse, onReject, maxSize); } std::shared_ptr Network::connect(const std::string& address, int port) { - auto socket = tcp->connect(address, port); + auto socket = SocketImpl::connect(address, port); connections.push_back(socket); return socket; } @@ -360,7 +351,7 @@ size_t Network::getTotalUpload() const { for (const auto& socket : connections) { totalUpload += socket->getTotalUpload(); } - return http->getTotalUpload() + totalUpload; + return requests->getTotalUpload() + totalUpload; } size_t Network::getTotalDownload() const { @@ -368,15 +359,14 @@ size_t Network::getTotalDownload() const { for (const auto& socket : connections) { totalDownload += socket->getTotalDownload(); } - return http->getTotalDownload() + totalDownload; + return requests->getTotalDownload() + totalDownload; } void Network::update() { - http->update(); + requests->update(); } std::unique_ptr Network::create(const NetworkSettings& settings) { - auto http = CurlHttp::create(); - auto tcp = std::make_unique(); - return std::make_unique(std::move(http), std::move(tcp)); + auto requests = CurlRequests::create(); + return std::make_unique(std::move(requests)); } diff --git a/src/network/Network.hpp b/src/network/Network.hpp index ef5728f8..62785ad2 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -11,9 +11,9 @@ namespace network { using OnResponse = std::function)>; using OnReject = std::function; - class Http { + class Requests { public: - virtual ~Http() {} + virtual ~Requests() {} virtual void get( const std::string& url, @@ -38,24 +38,14 @@ namespace network { virtual size_t getTotalDownload() const = 0; }; - class Tcp { - public: - virtual ~Tcp() {} - - virtual std::shared_ptr connect( - const std::string& address, int port - ) = 0; - }; - class Network { - std::unique_ptr http; - std::unique_ptr tcp; + std::unique_ptr requests; std::vector> connections; public: - Network(std::unique_ptr http, std::unique_ptr tcp); + Network(std::unique_ptr requests); ~Network(); - void httpGet( + void get( const std::string& url, OnResponse onResponse, OnReject onReject = nullptr, diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index 25f7c3cf..0713376e 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -6,7 +6,7 @@ TEST(curltest, curltest) { NetworkSettings settings {}; auto network = network::Network::create(settings); - network->httpGet( + network->get( "https://raw.githubusercontent.com/MihailRis/VoxelEngine-Cpp/refs/" "heads/curl/res/content/base/blocks/lamp.json", [](std::vector data) { From 90155dd05bca2478d052235c24ecf0bb6bbf37de Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 24 Nov 2024 07:58:07 +0300 Subject: [PATCH 20/56] rename functions --- src/logic/scripting/lua/libs/libnetwork.cpp | 8 ++++---- src/network/Network.cpp | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 0add7721..edd35bcc 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -5,7 +5,7 @@ using namespace scripting; -static int l_http_get(lua::State* L) { +static int l_get(lua::State* L) { std::string url(lua::require_lstring(L, 1)); lua::pushvalue(L, 2); @@ -19,7 +19,7 @@ static int l_http_get(lua::State* L) { return 0; } -static int l_http_get_binary(lua::State* L) { +static int l_get_binary(lua::State* L) { std::string url(lua::require_lstring(L, 1)); lua::pushvalue(L, 2); @@ -37,7 +37,7 @@ static int l_http_get_binary(lua::State* L) { } const luaL_Reg networklib[] = { - {"http_get", lua::wrap}, - {"http_get_binary", lua::wrap}, + {"get", lua::wrap}, + {"get_binary", lua::wrap}, {NULL, NULL} }; diff --git a/src/network/Network.cpp b/src/network/Network.cpp index c4e9b004..aec84372 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -29,6 +29,7 @@ struct Request { OnResponse onResponse; OnReject onReject; long maxSize; + bool followLocation = false; }; class CurlRequests : public Requests { @@ -79,7 +80,7 @@ public: curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request.followLocation); if (request.maxSize == 0) { curl_easy_setopt( curl, CURLOPT_MAXFILESIZE, std::numeric_limits::max() From 18bdce52df15074903667d010a8362b362bf9b51 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 17:12:24 +0300 Subject: [PATCH 21/56] make socket non-blocking --- src/network/Network.cpp | 36 +++++++++++++++++++++++++++++++----- src/network/Network.hpp | 7 +++++-- test/network/curltest.cpp | 2 +- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index aec84372..8d258d43 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -182,6 +182,7 @@ public: #include #include #include +#include #endif #ifndef _WIN32 @@ -309,6 +310,22 @@ public: freeaddrinfo(addrinfo); throw std::runtime_error("Could not create socket"); } +#ifdef _WIN32 + u_long mode = 1; + auto result = ioctlsocket(descriptor, FIONBIO, &mode); + if (result != NO_ERROR) { + throw std::runtime_error( + "Could not set to non-blocking mode [errno=" + std::to_string(err) + + "]: " + std::string(strerror(err)) + ); + } +#else + if (fcntl(descriptor, F_SETFL, O_NONBLOCK) < 0) { + freeaddrinfo(addrinfo); + closesocket(descriptor); + throw std::runtime_error("Failed to make socket non-blocking"); + } +#endif int res = connectsocket(descriptor, addrinfo->ai_addr, addrinfo->ai_addrlen); if (res == -1) { closesocket(descriptor); @@ -341,15 +358,24 @@ void Network::get( requests->get(url, onResponse, onReject, maxSize); } -std::shared_ptr Network::connect(const std::string& address, int port) { +Socket* Network::getConnection(u64id_t id) const { + const auto& found = connections.find(id); + if (found == connections.end()) { + return nullptr; + } + return found->second.get(); +} + +u64id_t Network::connect(const std::string& address, int port) { auto socket = SocketImpl::connect(address, port); - connections.push_back(socket); - return socket; + u64id_t id = nextConnection++; + connections[id] = std::move(socket); + return id; } size_t Network::getTotalUpload() const { size_t totalUpload = 0; - for (const auto& socket : connections) { + for (const auto& [_, socket] : connections) { totalUpload += socket->getTotalUpload(); } return requests->getTotalUpload() + totalUpload; @@ -357,7 +383,7 @@ size_t Network::getTotalUpload() const { size_t Network::getTotalDownload() const { size_t totalDownload = 0; - for (const auto& socket : connections) { + for (const auto& [_, socket] : connections) { totalDownload += socket->getTotalDownload(); } return requests->getTotalDownload() + totalDownload; diff --git a/src/network/Network.hpp b/src/network/Network.hpp index 62785ad2..450b8640 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -40,7 +40,8 @@ namespace network { class Network { std::unique_ptr requests; - std::vector> connections; + std::unordered_map> connections; + u64id_t nextConnection = 1; public: Network(std::unique_ptr requests); ~Network(); @@ -52,7 +53,9 @@ namespace network { long maxSize=0 ); - std::shared_ptr connect(const std::string& address, int port); + Socket* getConnection(u64id_t id) const; + + u64id_t connect(const std::string& address, int port); size_t getTotalUpload() const; size_t getTotalDownload() const; diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index 0713376e..5ce86b7b 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -19,7 +19,7 @@ TEST(curltest, curltest) { }, [](auto){} ); if (false) { - auto socket = network->connect("localhost", 8000); + auto socket = network->getConnection(network->connect("localhost", 8000)); const char* string = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; socket->send(string, strlen(string)); char data[1024]; From 8e0a8d6a4754bdc51dc46bc9e83423c0c5ea4a8b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 17:24:52 +0300 Subject: [PATCH 22/56] fix --- src/network/Network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 8d258d43..1deee553 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -312,8 +312,8 @@ public: } #ifdef _WIN32 u_long mode = 1; - auto result = ioctlsocket(descriptor, FIONBIO, &mode); - if (result != NO_ERROR) { + auto err = ioctlsocket(descriptor, FIONBIO, &mode); + if (err != NO_ERROR) { throw std::runtime_error( "Could not set to non-blocking mode [errno=" + std::to_string(err) + "]: " + std::string(strerror(err)) From edd41705321c10cf7d6bf1e46fee711838e54c58 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 17:49:08 +0300 Subject: [PATCH 23/56] minor refactor --- src/network/Network.cpp | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 1deee553..5e4bbc04 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -8,6 +8,22 @@ #include #include +#ifdef _WIN32 +/// included in curl.h +#else +#include +#include +#include +#include +#include +#include +#include +#include + +using SOCKET = int; + +#endif + #include "debug/Logger.hpp" using namespace network; @@ -171,20 +187,6 @@ public: } }; - -#ifdef _WIN32 -/// ... -#else -#include -#include -#include -#include -#include -#include -#include -#include -#endif - #ifndef _WIN32 static inline int closesocket(int descriptor) noexcept { return close(descriptor); @@ -227,13 +229,13 @@ static std::string to_string(const addrinfo* addr) { } class SocketImpl : public Socket { - int descriptor; + SOCKET descriptor; bool open = true; addrinfo* addr; size_t totalUpload = 0; size_t totalDownload = 0; public: - SocketImpl(int descriptor, addrinfo* addr) + SocketImpl(SOCKET descriptor, addrinfo* addr) : descriptor(descriptor), addr(addr) { } @@ -303,7 +305,7 @@ public: )) { throw std::runtime_error(gai_strerror(res)); } - int descriptor = socket( + SOCKET descriptor = socket( addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol ); if (descriptor == -1) { From 5e0d5b6db226b0ca11f0dc58315f47a2f5c4e8b3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 18:10:45 +0300 Subject: [PATCH 24/56] turn on failing test to check an error message --- test/network/curltest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index 5ce86b7b..2e6402f9 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -18,7 +18,7 @@ TEST(curltest, curltest) { std::cout << value << std::endl; }, [](auto){} ); - if (false) { + if (true) { auto socket = network->getConnection(network->connect("localhost", 8000)); const char* string = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; socket->send(string, strlen(string)); From 419699bc0d3975e3ef66f5324ba7dc8628632b7a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 18:36:19 +0300 Subject: [PATCH 25/56] update error handling code --- src/network/Network.cpp | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 5e4bbc04..0d2946e4 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -21,10 +21,10 @@ #include using SOCKET = int; - -#endif +#endif // _WIN32 #include "debug/Logger.hpp" +#include "util/stringutil.hpp" using namespace network; @@ -191,6 +191,31 @@ public: static inline int closesocket(int descriptor) noexcept { return close(descriptor); } +static inline void handle_socket_error(const std::string& message) { + int err = errno; + throw std::runtime_error( + message+" [errno=" + std::to_string(err) + + "]: " + std::string(strerror(err)) + ); +} +#else +static inline void handle_socket_error() { + wchar_t* s = nullptr; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&s, + 0, + nullptr + ); + assert(s != nullptr); + std::string errorString = util::wstr2str_utf8(std::wstring(s)); + LocalFree(s); + throw std::runtime_error(message+"; "+errorString) +} #endif static inline int connectsocket( @@ -328,16 +353,12 @@ public: throw std::runtime_error("Failed to make socket non-blocking"); } #endif + int res = connectsocket(descriptor, addrinfo->ai_addr, addrinfo->ai_addrlen); if (res == -1) { closesocket(descriptor); freeaddrinfo(addrinfo); - - int err = errno; - throw std::runtime_error( - "Connect failed [errno=" + std::to_string(err) + - "]: " + std::string(strerror(err)) - ); + handle_socket_error("Connect failed"); } logger.info() << "connected to " << address << " [" << to_string(addrinfo) << ":" << port << "]"; From b999c062d6ef40305494de8866585730898eb418 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 18:46:37 +0300 Subject: [PATCH 26/56] fix --- src/network/Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 0d2946e4..76adf39a 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -199,7 +199,7 @@ static inline void handle_socket_error(const std::string& message) { ); } #else -static inline void handle_socket_error() { +static inline void handle_socket_error(const std::string& message) { wchar_t* s = nullptr; FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | From 13bdfef52ad36c045dfb684f8aaaf9e96c7de26b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 18:55:51 +0300 Subject: [PATCH 27/56] fix --- src/network/Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 76adf39a..86108352 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -214,7 +214,7 @@ static inline void handle_socket_error(const std::string& message) { assert(s != nullptr); std::string errorString = util::wstr2str_utf8(std::wstring(s)); LocalFree(s); - throw std::runtime_error(message+"; "+errorString) + throw std::runtime_error(message+"; "+errorString); } #endif From 353046b9d53a2fcad7c0fb26a844f538e380b723 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 19:26:18 +0300 Subject: [PATCH 28/56] turn off extra workflows to minimize spam --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- src/network/Network.cpp | 15 ++++++++------- test/network/curltest.cpp | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 250fd001..c6315660 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ ] jobs: build-appimage: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e4ddf55a..94895912 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ ] jobs: build-dmg: diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 86108352..d9f9c201 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -191,15 +191,15 @@ public: static inline int closesocket(int descriptor) noexcept { return close(descriptor); } -static inline void handle_socket_error(const std::string& message) { +static inline std::runtime_error handle_socket_error(const std::string& message) { int err = errno; - throw std::runtime_error( - message+" [errno=" + std::to_string(err) + - "]: " + std::string(strerror(err)) + return std::runtime_error( + message+" [errno=" + std::to_string(err) + "]: " + + std::string(strerror(err)) ); } #else -static inline void handle_socket_error(const std::string& message) { +static inline std::runtime_error handle_socket_error(const std::string& message) { wchar_t* s = nullptr; FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | @@ -214,7 +214,7 @@ static inline void handle_socket_error(const std::string& message) { assert(s != nullptr); std::string errorString = util::wstr2str_utf8(std::wstring(s)); LocalFree(s); - throw std::runtime_error(message+"; "+errorString); + return std::runtime_error(message+"; "+errorString); } #endif @@ -356,9 +356,10 @@ public: int res = connectsocket(descriptor, addrinfo->ai_addr, addrinfo->ai_addrlen); if (res == -1) { + auto error = handle_socket_error("Connect failed"); closesocket(descriptor); freeaddrinfo(addrinfo); - handle_socket_error("Connect failed"); + throw error; } logger.info() << "connected to " << address << " [" << to_string(addrinfo) << ":" << port << "]"; diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index 2e6402f9..afca9677 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -19,8 +19,8 @@ TEST(curltest, curltest) { }, [](auto){} ); if (true) { - auto socket = network->getConnection(network->connect("localhost", 8000)); - const char* string = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; + auto socket = network->getConnection(network->connect("google.com", 80)); + const char* string = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"; socket->send(string, strlen(string)); char data[1024]; From f5c4a22564c32bd99a65717b7c4f751e60285483 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 19:30:06 +0300 Subject: [PATCH 29/56] again --- .github/workflows/appimage.yml | 7 +------ .github/workflows/macos.yml | 6 +----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index c6315660..36d9fd31 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -1,11 +1,6 @@ name: C/C++ AppImage -on: - push: - branches: [ "main", "release-**"] - pull_request: - branches: [ ] - +on: [] jobs: build-appimage: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 94895912..75fbfc97 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -1,10 +1,6 @@ name: Macos Build -on: - push: - branches: [ "main", "release-**"] - pull_request: - branches: [ ] +on: [] jobs: build-dmg: From 992cfefc8e51239854ee565ff3231893284a1131 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 19:58:29 +0300 Subject: [PATCH 30/56] update error message --- src/network/Network.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index d9f9c201..5af67a71 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -200,21 +200,26 @@ static inline std::runtime_error handle_socket_error(const std::string& message) } #else static inline std::runtime_error handle_socket_error(const std::string& message) { + int errorCode = WSAGetLastError(); wchar_t* s = nullptr; - FormatMessageW( + size_t size = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, - WSAGetLastError(), + errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&s, 0, nullptr ); assert(s != nullptr); - std::string errorString = util::wstr2str_utf8(std::wstring(s)); + while (size && isspace(s[size-1])) { + s[--size] = 0; + } + auto errorString = util::wstr2str_utf8(std::wstring(s)); LocalFree(s); - return std::runtime_error(message+"; "+errorString); + return std::runtime_error(message+" [WSA error=" + + std::to_string(errorCode) + "]: "+errorString); } #endif From 85d94017c702c48fc9dc27cddf86eda2b3906dc4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 20:32:57 +0300 Subject: [PATCH 31/56] catch EINPROGRESS and WSAEWOULDBLOCK --- src/network/Network.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 5af67a71..fb80750f 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -19,10 +19,14 @@ #include #include #include +#include using SOCKET = int; #endif // _WIN32 +#include +#include + #include "debug/Logger.hpp" #include "util/stringutil.hpp" @@ -361,11 +365,24 @@ public: int res = connectsocket(descriptor, addrinfo->ai_addr, addrinfo->ai_addrlen); if (res == -1) { - auto error = handle_socket_error("Connect failed"); - closesocket(descriptor); - freeaddrinfo(addrinfo); - throw error; +# ifdef _WIN32 + if (WSAGetLastError() != WSAEWOULDBLOCK) { + auto error = handle_socket_error("Connect failed"); + closesocket(descriptor); + freeaddrinfo(addrinfo); + throw error; + } +# else + if (errno != EINPROGRESS) { + auto error = handle_socket_error("Connect failed"); + closesocket(descriptor); + freeaddrinfo(addrinfo); + throw error; + } +# endif } + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + logger.info() << "connected to " << address << " [" << to_string(addrinfo) << ":" << port << "]"; return std::make_shared(descriptor, addrinfo); From 55de4848dc4ce88821bc58461544fd035840f917 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 21:07:30 +0300 Subject: [PATCH 32/56] remove non-blocking mode --- src/network/Network.cpp | 40 +++++----------------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index fb80750f..eb50d0c5 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -346,43 +346,13 @@ public: freeaddrinfo(addrinfo); throw std::runtime_error("Could not create socket"); } -#ifdef _WIN32 - u_long mode = 1; - auto err = ioctlsocket(descriptor, FIONBIO, &mode); - if (err != NO_ERROR) { - throw std::runtime_error( - "Could not set to non-blocking mode [errno=" + std::to_string(err) + - "]: " + std::string(strerror(err)) - ); - } -#else - if (fcntl(descriptor, F_SETFL, O_NONBLOCK) < 0) { - freeaddrinfo(addrinfo); - closesocket(descriptor); - throw std::runtime_error("Failed to make socket non-blocking"); - } -#endif - int res = connectsocket(descriptor, addrinfo->ai_addr, addrinfo->ai_addrlen); - if (res == -1) { -# ifdef _WIN32 - if (WSAGetLastError() != WSAEWOULDBLOCK) { - auto error = handle_socket_error("Connect failed"); - closesocket(descriptor); - freeaddrinfo(addrinfo); - throw error; - } -# else - if (errno != EINPROGRESS) { - auto error = handle_socket_error("Connect failed"); - closesocket(descriptor); - freeaddrinfo(addrinfo); - throw error; - } -# endif + if (res < 0) { + auto error = handle_socket_error("Connect failed"); + closesocket(descriptor); + freeaddrinfo(addrinfo); + throw error; } - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - logger.info() << "connected to " << address << " [" << to_string(addrinfo) << ":" << port << "]"; return std::make_shared(descriptor, addrinfo); From aa7fa6be5ba978e0f2f419421b973b46f62d140c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 21:26:26 +0300 Subject: [PATCH 33/56] add network.__connect --- src/logic/scripting/lua/libs/libnetwork.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index edd35bcc..4b734a62 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -36,8 +36,16 @@ static int l_get_binary(lua::State* L) { return 0; } +static int l_connect(lua::State* L) { + std::string address = lua::require_string(L, 1); + int port = lua::tointeger(L, 2); + u64id_t id = engine->getNetwork().connect(address, port); + return lua::pushinteger(L, id); +} + const luaL_Reg networklib[] = { {"get", lua::wrap}, {"get_binary", lua::wrap}, + {"__connect", lua::wrap}, {NULL, NULL} }; From 5e4949f4d1c74136bc7eda70f698f117c35e0f0f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 26 Nov 2024 21:27:30 +0300 Subject: [PATCH 34/56] revert workflows --- .github/workflows/appimage.yml | 7 ++++++- .github/workflows/macos.yml | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 36d9fd31..250fd001 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -1,6 +1,11 @@ name: C/C++ AppImage -on: [] +on: + push: + branches: [ "main", "release-**"] + pull_request: + branches: [ "main" ] + jobs: build-appimage: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 75fbfc97..e4ddf55a 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -1,6 +1,10 @@ name: Macos Build -on: [] +on: + push: + branches: [ "main", "release-**"] + pull_request: + branches: [ "main" ] jobs: build-dmg: From 3933baccd215cf91dc4d2c4ab7cbbd126b6836ef Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 10:55:55 +0300 Subject: [PATCH 35/56] remove double world.lua run in 'core' --- src/logic/scripting/scripting.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 8fd9b231..f15e36e3 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -208,7 +208,6 @@ void scripting::on_world_load(LevelController* controller) { if (lua::getglobal(L, "__vc_on_world_open")) { lua::call_nothrow(L, 0, 0); } - load_script("world.lua", false); for (auto& pack : scripting::engine->getContentPacks()) { lua::emit_event(L, pack.id + ":.worldopen"); From fb0f4bff5292fd2e28dd57132d239f075b9e9afc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 12:10:59 +0300 Subject: [PATCH 36/56] complete simple connection implementation --- src/logic/scripting/lua/libs/libnetwork.cpp | 56 +++++++- src/network/Network.cpp | 139 ++++++++++++++------ src/network/Network.hpp | 18 ++- test/network/curltest.cpp | 10 -- 4 files changed, 164 insertions(+), 59 deletions(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 4b734a62..2245dc10 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -39,13 +39,67 @@ static int l_get_binary(lua::State* L) { static int l_connect(lua::State* L) { std::string address = lua::require_string(L, 1); int port = lua::tointeger(L, 2); - u64id_t id = engine->getNetwork().connect(address, port); + lua::pushvalue(L, 3); + auto callback = lua::create_lambda(L); + u64id_t id = engine->getNetwork().connect(address, port, [callback](u64id_t id) { + callback({id}); + }); return lua::pushinteger(L, id); } +static int l_send(lua::State* L) { + u64id_t id = lua::tointeger(L, 1); + auto connection = engine->getNetwork().getConnection(id); + + if (lua::istable(L, 2)) { + lua::pushvalue(L, 2); + size_t size = lua::objlen(L, 2); + util::Buffer buffer(size); + for (size_t i = 0; i < size; i++) { + lua::rawgeti(L, i + 1); + buffer[i] = lua::tointeger(L, -1); + lua::pop(L); + } + lua::pop(L); + connection->send(buffer.data(), size); + } else if (auto bytes = lua::touserdata(L, 2)) { + connection->send( + reinterpret_cast(bytes->data().data()), bytes->data().size() + ); + } + return 0; +} + +static int l_recv(lua::State* L) { + u64id_t id = lua::tointeger(L, 1); + int length = lua::tointeger(L, 2); + auto connection = engine->getNetwork().getConnection(id); + util::Buffer buffer(glm::min(length, connection->available())); + + int size = connection->recv(buffer.data(), length); + if (size == -1) { + return 0; + } + if (lua::toboolean(L, 3)) { + lua::createtable(L, size, 0); + for (size_t i = 0; i < size; i++) { + lua::pushinteger(L, buffer[i] & 0xFF); + lua::rawseti(L, i+1); + } + } else { + lua::newuserdata(L, size); + auto bytearray = lua::touserdata(L, -1); + bytearray->data().reserve(size); + std::memcpy(bytearray->data().data(), buffer.data(), size); + } + return 1; +} + const luaL_Reg networklib[] = { {"get", lua::wrap}, {"get_binary", lua::wrap}, {"__connect", lua::wrap}, + {"__send", lua::wrap}, + {"__recv", lua::wrap}, {NULL, NULL} }; diff --git a/src/network/Network.cpp b/src/network/Network.cpp index eb50d0c5..23d0ec87 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #ifdef _WIN32 /// included in curl.h @@ -24,9 +26,6 @@ using SOCKET = int; #endif // _WIN32 -#include -#include - #include "debug/Logger.hpp" #include "util/stringutil.hpp" @@ -262,36 +261,87 @@ static std::string to_string(const addrinfo* addr) { return ""; } -class SocketImpl : public Socket { +class SocketConnection : public Connection { SOCKET descriptor; bool open = true; addrinfo* addr; size_t totalUpload = 0; size_t totalDownload = 0; -public: - SocketImpl(SOCKET descriptor, addrinfo* addr) - : descriptor(descriptor), addr(addr) { - } + ConnectionState state = ConnectionState::INITIAL; + std::unique_ptr thread = nullptr; + std::vector readBatch; + util::Buffer buffer; + std::mutex mutex; - ~SocketImpl() { - closesocket(descriptor); + void connectSocket() { + state = ConnectionState::CONNECTING; + logger.info() << "connecting to " << to_string(addr); + int res = connectsocket(descriptor, addr->ai_addr, addr->ai_addrlen); + if (res < 0) { + auto error = handle_socket_error("Connect failed"); + closesocket(descriptor); + freeaddrinfo(addr); + state = ConnectionState::CLOSED; + throw error; + } + logger.info() << "connected to " << to_string(addr); + state = ConnectionState::CONNECTED; + } +public: + SocketConnection(SOCKET descriptor, addrinfo* addr) + : descriptor(descriptor), addr(addr), buffer(16'384) {} + + ~SocketConnection() { + if (state != ConnectionState::CLOSED) { + shutdown(descriptor, 2); + closesocket(descriptor); + } + thread->join(); freeaddrinfo(addr); } + void connect(runnable callback) override { + thread = std::make_unique([this, callback]() { + connectSocket(); + callback(); + while (state == ConnectionState::CONNECTED) { + int size = recvsocket(descriptor, buffer.data(), buffer.size()); + if (size == 0) { + logger.info() << "closed connection " << to_string(addr); + closesocket(descriptor); + state = ConnectionState::CLOSED; + break; + } else if (size < 0) { + logger.info() << "an error ocurred while receiving from " + << to_string(addr); + auto error = handle_socket_error("recv(...) error"); + closesocket(descriptor); + state = ConnectionState::CLOSED; + logger.error() << error.what(); + break; + } + { + std::lock_guard lock(mutex); + for (size_t i = 0; i < size; i++) { + readBatch.emplace_back(buffer[i]); + } + totalDownload += size; + } + logger.info() << "read " << size << " bytes from " << to_string(addr); + } + }); + } + int recv(char* buffer, size_t length) override { - int len = recvsocket(descriptor, buffer, length); - if (len == 0) { - int err = errno; - close(); - throw std::runtime_error( - "Read failed [errno=" + std::to_string(err) + - "]: " + std::string(strerror(err)) - ); - } else if (len == -1) { - return 0; + std::lock_guard lock(mutex); + + if (state != ConnectionState::CONNECTED && readBatch.empty()) { + return -1; } - totalDownload += len; - return len; + int size = std::min(readBatch.size(), length); + std::memcpy(buffer, readBatch.data(), size); + readBatch.erase(readBatch.begin(), readBatch.begin() + size); + return size; } int send(const char* buffer, size_t length) override { @@ -308,13 +358,17 @@ public: return len; } - void close() override { - closesocket(descriptor); - open = false; + int available() override { + std::lock_guard lock(mutex); + return readBatch.size(); } - bool isOpen() const override { - return open; + void close() override { + if (state != ConnectionState::CLOSED) { + shutdown(descriptor, 2); + closesocket(descriptor); + } + thread->join(); } size_t getTotalUpload() const override { @@ -325,8 +379,8 @@ public: return totalDownload; } - static std::shared_ptr connect( - const std::string& address, int port + static std::shared_ptr connect( + const std::string& address, int port, runnable callback ) { addrinfo hints {}; @@ -346,21 +400,18 @@ public: freeaddrinfo(addrinfo); throw std::runtime_error("Could not create socket"); } - int res = connectsocket(descriptor, addrinfo->ai_addr, addrinfo->ai_addrlen); - if (res < 0) { - auto error = handle_socket_error("Connect failed"); - closesocket(descriptor); - freeaddrinfo(addrinfo); - throw error; - } - logger.info() << "connected to " << address << " [" - << to_string(addrinfo) << ":" << port << "]"; - return std::make_shared(descriptor, addrinfo); + auto socket = std::make_shared(descriptor, addrinfo); + socket->connect(std::move(callback)); + return socket; + } + + ConnectionState getState() const override { + return state; } }; Network::Network(std::unique_ptr requests) - : requests(std::move(requests)) { +: requests(std::move(requests)) { } Network::~Network() = default; @@ -374,7 +425,7 @@ void Network::get( requests->get(url, onResponse, onReject, maxSize); } -Socket* Network::getConnection(u64id_t id) const { +Connection* Network::getConnection(u64id_t id) const { const auto& found = connections.find(id); if (found == connections.end()) { return nullptr; @@ -382,9 +433,11 @@ Socket* Network::getConnection(u64id_t id) const { return found->second.get(); } -u64id_t Network::connect(const std::string& address, int port) { - auto socket = SocketImpl::connect(address, port); +u64id_t Network::connect(const std::string& address, int port, consumer callback) { u64id_t id = nextConnection++; + auto socket = SocketConnection::connect(address, port, [id, callback]() { + callback(id); + }); connections[id] = std::move(socket); return id; } diff --git a/src/network/Network.hpp b/src/network/Network.hpp index 450b8640..f4b6ceef 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -6,6 +6,7 @@ #include "typedefs.hpp" #include "settings.hpp" #include "util/Buffer.hpp" +#include "delegates.hpp" namespace network { using OnResponse = std::function)>; @@ -27,20 +28,27 @@ namespace network { virtual void update() = 0; }; - class Socket { + enum class ConnectionState { + INITIAL, CONNECTING, CONNECTED, CLOSED + }; + + class Connection { public: + virtual void connect(runnable callback) = 0; virtual int recv(char* buffer, size_t length) = 0; virtual int send(const char* buffer, size_t length) = 0; virtual void close() = 0; - virtual bool isOpen() const = 0; + virtual int available() = 0; virtual size_t getTotalUpload() const = 0; virtual size_t getTotalDownload() const = 0; + + virtual ConnectionState getState() const = 0; }; class Network { std::unique_ptr requests; - std::unordered_map> connections; + std::unordered_map> connections; u64id_t nextConnection = 1; public: Network(std::unique_ptr requests); @@ -53,9 +61,9 @@ namespace network { long maxSize=0 ); - Socket* getConnection(u64id_t id) const; + [[nodiscard]] Connection* getConnection(u64id_t id) const; - u64id_t connect(const std::string& address, int port); + u64id_t connect(const std::string& address, int port, consumer callback); size_t getTotalUpload() const; size_t getTotalDownload() const; diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index afca9677..e365651e 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -18,16 +18,6 @@ TEST(curltest, curltest) { std::cout << value << std::endl; }, [](auto){} ); - if (true) { - auto socket = network->getConnection(network->connect("google.com", 80)); - const char* string = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"; - socket->send(string, strlen(string)); - char data[1024]; - - int len = socket->recv(data, 1024); - std::cout << len << " " << std::string(data, len) << std::endl; - } - std::cout << "upload: " << network->getTotalUpload() << " B" << std::endl; std::cout << "download: " << network->getTotalDownload() << " B" << std::endl; } From 32ba027fac44f050270a0ed76a66a4d8e6289de7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 12:12:20 +0300 Subject: [PATCH 37/56] replace getContentPacks with getAllContentPacks in global events emitting --- src/logic/scripting/scripting.cpp | 8 ++++---- src/logic/scripting/scripting_hud.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index f15e36e3..9b43cf28 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -209,21 +209,21 @@ void scripting::on_world_load(LevelController* controller) { lua::call_nothrow(L, 0, 0); } - for (auto& pack : scripting::engine->getContentPacks()) { + for (auto& pack : scripting::engine->getAllContentPacks()) { lua::emit_event(L, pack.id + ":.worldopen"); } } void scripting::on_world_tick() { auto L = lua::get_main_state(); - for (auto& pack : scripting::engine->getContentPacks()) { + for (auto& pack : scripting::engine->getAllContentPacks()) { lua::emit_event(L, pack.id + ":.worldtick"); } } void scripting::on_world_save() { auto L = lua::get_main_state(); - for (auto& pack : scripting::engine->getContentPacks()) { + for (auto& pack : scripting::engine->getAllContentPacks()) { lua::emit_event(L, pack.id + ":.worldsave"); } if (lua::getglobal(L, "__vc_on_world_save")) { @@ -233,7 +233,7 @@ void scripting::on_world_save() { void scripting::on_world_quit() { auto L = lua::get_main_state(); - for (auto& pack : scripting::engine->getContentPacks()) { + for (auto& pack : scripting::engine->getAllContentPacks()) { lua::emit_event(L, pack.id + ":.worldquit"); } if (lua::getglobal(L, "__vc_on_world_quit")) { diff --git a/src/logic/scripting/scripting_hud.cpp b/src/logic/scripting/scripting_hud.cpp index 7adb5076..355c5335 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -42,7 +42,7 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) { lua::call_nothrow(L, 0, 0); } - for (auto& pack : engine->getContentPacks()) { + for (auto& pack : engine->getAllContentPacks()) { lua::emit_event( lua::get_main_state(), pack.id + ":.hudopen", @@ -54,7 +54,7 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) { } void scripting::on_frontend_render() { - for (auto& pack : engine->getContentPacks()) { + for (auto& pack : engine->getAllContentPacks()) { lua::emit_event( lua::get_main_state(), pack.id + ":.hudrender", @@ -64,7 +64,7 @@ void scripting::on_frontend_render() { } void scripting::on_frontend_close() { - for (auto& pack : engine->getContentPacks()) { + for (auto& pack : engine->getAllContentPacks()) { lua::emit_event( lua::get_main_state(), pack.id + ":.hudclose", From 233bc3174c8751baf641571ccc7161c411c73638 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 12:51:01 +0300 Subject: [PATCH 38/56] add network.__close --- src/logic/scripting/lua/libs/libnetwork.cpp | 17 ++++++++++++++++- src/network/Network.cpp | 5 ++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 2245dc10..df60b741 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -47,10 +47,21 @@ static int l_connect(lua::State* L) { return lua::pushinteger(L, id); } + +static int l_close(lua::State* L) { + u64id_t id = lua::tointeger(L, 1); + if (auto connection = engine->getNetwork().getConnection(id)) { + connection->close(); + } + return 0; +} + static int l_send(lua::State* L) { u64id_t id = lua::tointeger(L, 1); auto connection = engine->getNetwork().getConnection(id); - + if (connection == nullptr) { + return 0; + } if (lua::istable(L, 2)) { lua::pushvalue(L, 2); size_t size = lua::objlen(L, 2); @@ -74,6 +85,9 @@ static int l_recv(lua::State* L) { u64id_t id = lua::tointeger(L, 1); int length = lua::tointeger(L, 2); auto connection = engine->getNetwork().getConnection(id); + if (connection == nullptr) { + return 0; + } util::Buffer buffer(glm::min(length, connection->available())); int size = connection->recv(buffer.data(), length); @@ -99,6 +113,7 @@ const luaL_Reg networklib[] = { {"get", lua::wrap}, {"get_binary", lua::wrap}, {"__connect", lua::wrap}, + {"__close", lua::wrap}, {"__send", lua::wrap}, {"__recv", lua::wrap}, {NULL, NULL} diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 23d0ec87..fa46b682 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -296,7 +296,9 @@ public: shutdown(descriptor, 2); closesocket(descriptor); } - thread->join(); + if (thread) { + thread->join(); + } freeaddrinfo(addr); } @@ -369,6 +371,7 @@ public: closesocket(descriptor); } thread->join(); + thread = nullptr; } size_t getTotalUpload() const override { From a72d36f53c87fd51be36668fb184cd570768ca0f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 15:38:57 +0300 Subject: [PATCH 39/56] add simple inefficient server socket implementation (WIP) --- src/logic/scripting/lua/libs/libnetwork.cpp | 17 ++- src/network/Network.cpp | 141 ++++++++++++++++++-- src/network/Network.hpp | 17 +++ 3 files changed, 164 insertions(+), 11 deletions(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index df60b741..1b3757b1 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -42,7 +42,9 @@ static int l_connect(lua::State* L) { lua::pushvalue(L, 3); auto callback = lua::create_lambda(L); u64id_t id = engine->getNetwork().connect(address, port, [callback](u64id_t id) { - callback({id}); + engine->postRunnable([=]() { + callback({id}); + }); }); return lua::pushinteger(L, id); } @@ -109,9 +111,22 @@ static int l_recv(lua::State* L) { return 1; } +static int l_open(lua::State* L) { + int port = lua::tointeger(L, 1); + lua::pushvalue(L, 2); + auto callback = lua::create_lambda(L); + u64id_t id = engine->getNetwork().openServer(port, [callback](u64id_t id) { + engine->postRunnable([=]() { + callback({id}); + }); + }); + return lua::pushinteger(L, id); +} + const luaL_Reg networklib[] = { {"get", lua::wrap}, {"get_binary", lua::wrap}, + {"__open", lua::wrap}, {"__connect", lua::wrap}, {"__close", lua::wrap}, {"__send", lua::wrap}, diff --git a/src/network/Network.cpp b/src/network/Network.cpp index fa46b682..a433860c 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -244,18 +244,26 @@ static inline int sendsocket( return send(descriptor, buf, len, flags); } +static std::string to_string(const sockaddr_in* addr) { + char ip[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &(addr->sin_addr), ip, INET_ADDRSTRLEN)) { + return std::string(ip)+":"+std::to_string(addr->sin_port); + } + return ""; +} + static std::string to_string(const addrinfo* addr) { if (addr->ai_family == AF_INET) { auto psai = reinterpret_cast(addr->ai_addr); char ip[INET_ADDRSTRLEN]; if (inet_ntop(addr->ai_family, &(psai->sin_addr), ip, INET_ADDRSTRLEN)) { - return std::string(ip); + return std::string(ip)+":"+std::to_string(psai->sin_port); } } else if (addr->ai_family == AF_INET6) { auto psai = reinterpret_cast(addr->ai_addr); char ip[INET6_ADDRSTRLEN]; if (inet_ntop(addr->ai_family, &(psai->sin6_addr), ip, INET6_ADDRSTRLEN)) { - return std::string(ip); + return std::string(ip)+":"+std::to_string(psai->sin6_port); } } return ""; @@ -265,6 +273,7 @@ class SocketConnection : public Connection { SOCKET descriptor; bool open = true; addrinfo* addr; + std::string addrString; size_t totalUpload = 0; size_t totalDownload = 0; ConnectionState state = ConnectionState::INITIAL; @@ -288,8 +297,8 @@ class SocketConnection : public Connection { state = ConnectionState::CONNECTED; } public: - SocketConnection(SOCKET descriptor, addrinfo* addr) - : descriptor(descriptor), addr(addr), buffer(16'384) {} + SocketConnection(SOCKET descriptor, addrinfo* addr, const std::string& addrString) + : descriptor(descriptor), addr(addr), addrString(addrString), buffer(16'384) {} ~SocketConnection() { if (state != ConnectionState::CLOSED) { @@ -299,7 +308,9 @@ public: if (thread) { thread->join(); } - freeaddrinfo(addr); + if (addr) { + freeaddrinfo(addr); + } } void connect(runnable callback) override { @@ -352,8 +363,8 @@ public: int err = errno; close(); throw std::runtime_error( - "Send failed [errno=" + std::to_string(err) + - "]: " + std::string(strerror(err)) + "Send failed [errno=" + std::to_string(err) + "]: " + + std::string(strerror(err)) ); } totalUpload += len; @@ -370,8 +381,10 @@ public: shutdown(descriptor, 2); closesocket(descriptor); } - thread->join(); - thread = nullptr; + if (thread) { + thread->join(); + thread = nullptr; + } } size_t getTotalUpload() const override { @@ -403,7 +416,7 @@ public: freeaddrinfo(addrinfo); throw std::runtime_error("Could not create socket"); } - auto socket = std::make_shared(descriptor, addrinfo); + auto socket = std::make_shared(descriptor, addrinfo, to_string(addrinfo)); socket->connect(std::move(callback)); return socket; } @@ -413,6 +426,101 @@ public: } }; +class SocketTcpSServer : public TcpServer { + Network* network; + SOCKET descriptor; + std::vector clients; + bool open = true; + std::unique_ptr thread = nullptr; +public: + SocketTcpSServer(Network* network, SOCKET descriptor) + : network(network), descriptor(descriptor) {} + + ~SocketTcpSServer() { + closeSocket(); + } + + void startListen(consumer handler) override { + thread = std::make_unique([this, handler]() { + while (open) { + logger.info() << "listening for connections"; + if (listen(descriptor, 2) < 0) { + close(); + break; + } + socklen_t addrlen = sizeof(sockaddr_in); + SOCKET clientDescriptor; + sockaddr_in address; + logger.info() << "accepting clients"; + if ((clientDescriptor = accept(descriptor, (sockaddr*)&address, &addrlen)) < 0) { + close(); + break; + } + logger.info() << "client connected: " << to_string(&address); + auto socket = std::make_shared( + clientDescriptor, nullptr, to_string(&address) + ); + u64id_t id = network->addConnection(socket); + clients.push_back(id); + handler(id); + } + }); + } + + void closeSocket() { + if (!open) { + return; + } + logger.info() << "closing server"; + open = false; + for (u64id_t clientid : clients) { + if (auto client = network->getConnection(clientid)) { + client->close(); + } + } + clients.clear(); + + shutdown(descriptor, 2); + closesocket(descriptor); + thread->join(); + } + + void close() override { + closeSocket(); + } + + bool isOpen() override { + return open; + } + static std::shared_ptr openServer( + Network* network, int port, consumer handler + ) { + SOCKET descriptor = socket( + AF_INET, SOCK_STREAM, 0 + ); + if (descriptor == -1) { + throw std::runtime_error("Could not create server socket"); + } + int opt = 1; + if (setsockopt(descriptor, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { + closesocket(descriptor); + throw std::runtime_error("setsockopt"); + } + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + if (bind(descriptor, (sockaddr*)&address, sizeof(address)) < 0) { + closesocket(descriptor); + throw std::runtime_error("could not bind port "+std::to_string(port)); + } + logger.info() << "opened server at port " << port; + auto server = std::make_shared(network, descriptor); + server->startListen(std::move(handler)); + return server; + } +}; + Network::Network(std::unique_ptr requests) : requests(std::move(requests)) { } @@ -445,6 +553,19 @@ u64id_t Network::connect(const std::string& address, int port, consumer return id; } +u64id_t Network::openServer(int port, consumer handler) { + u64id_t id = nextServer++; + auto server = SocketTcpSServer::openServer(this, port, handler); + servers[id] = std::move(server); + return id; +} + +u64id_t Network::addConnection(const std::shared_ptr& socket) { + u64id_t id = nextConnection++; + connections[id] = std::move(socket); + return id; +} + size_t Network::getTotalUpload() const { size_t totalUpload = 0; for (const auto& [_, socket] : connections) { diff --git a/src/network/Network.hpp b/src/network/Network.hpp index f4b6ceef..741b3ed4 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -34,6 +34,8 @@ namespace network { class Connection { public: + virtual ~Connection() {} + virtual void connect(runnable callback) = 0; virtual int recv(char* buffer, size_t length) = 0; virtual int send(const char* buffer, size_t length) = 0; @@ -46,10 +48,21 @@ namespace network { virtual ConnectionState getState() const = 0; }; + class TcpServer { + public: + virtual ~TcpServer() {} + virtual void startListen(consumer handler) = 0; + virtual void close() = 0; + virtual bool isOpen() = 0; + }; + class Network { std::unique_ptr requests; std::unordered_map> connections; u64id_t nextConnection = 1; + + std::unordered_map> servers; + u64id_t nextServer = 1; public: Network(std::unique_ptr requests); ~Network(); @@ -65,6 +78,10 @@ namespace network { u64id_t connect(const std::string& address, int port, consumer callback); + u64id_t openServer(int port, consumer handler); + + u64id_t addConnection(const std::shared_ptr& connection); + size_t getTotalUpload() const; size_t getTotalDownload() const; From 9e8addbd7ddf684412831495ea1241e012843898 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 15:55:14 +0300 Subject: [PATCH 40/56] fix msvc build --- src/network/Network.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index a433860c..f5abeb8b 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -502,7 +502,11 @@ public: throw std::runtime_error("Could not create server socket"); } int opt = 1; - if (setsockopt(descriptor, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { + int flags = SO_REUSEADDR; +# ifndef _WIN32 + flags |= SO_REUSEPORT; +# endif + if (setsockopt(descriptor, SOL_SOCKET, flags, &opt, sizeof(opt))) { closesocket(descriptor); throw std::runtime_error("setsockopt"); } From 1b04a2a61e8a5fd137301218aabd10c73a364af0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 16:07:21 +0300 Subject: [PATCH 41/56] fix msvc build again --- src/network/Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index f5abeb8b..fa09dc0e 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -506,7 +506,7 @@ public: # ifndef _WIN32 flags |= SO_REUSEPORT; # endif - if (setsockopt(descriptor, SOL_SOCKET, flags, &opt, sizeof(opt))) { + if (setsockopt(descriptor, SOL_SOCKET, flags, (const char*)&opt, sizeof(opt))) { closesocket(descriptor); throw std::runtime_error("setsockopt"); } From 847ef230924d5fc0426b46e9d64ccede2a58baff Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 16:14:20 +0300 Subject: [PATCH 42/56] add network.__closeserver --- src/logic/scripting/lua/libs/libnetwork.cpp | 9 +++++++++ src/network/Network.cpp | 14 +++++++++----- src/network/Network.hpp | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 1b3757b1..6a6c5e69 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -58,6 +58,14 @@ static int l_close(lua::State* L) { return 0; } +static int l_closeserver(lua::State* L) { + u64id_t id = lua::tointeger(L, 1); + if (auto server = engine->getNetwork().getServer(id)) { + server->close(); + } + return 0; +} + static int l_send(lua::State* L) { u64id_t id = lua::tointeger(L, 1); auto connection = engine->getNetwork().getConnection(id); @@ -127,6 +135,7 @@ const luaL_Reg networklib[] = { {"get", lua::wrap}, {"get_binary", lua::wrap}, {"__open", lua::wrap}, + {"__closeserver", lua::wrap}, {"__connect", lua::wrap}, {"__close", lua::wrap}, {"__send", lua::wrap}, diff --git a/src/network/Network.cpp b/src/network/Network.cpp index fa09dc0e..c6736ada 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -254,11 +254,7 @@ static std::string to_string(const sockaddr_in* addr) { static std::string to_string(const addrinfo* addr) { if (addr->ai_family == AF_INET) { - auto psai = reinterpret_cast(addr->ai_addr); - char ip[INET_ADDRSTRLEN]; - if (inet_ntop(addr->ai_family, &(psai->sin_addr), ip, INET_ADDRSTRLEN)) { - return std::string(ip)+":"+std::to_string(psai->sin_port); - } + return to_string(reinterpret_cast(addr->ai_addr)); } else if (addr->ai_family == AF_INET6) { auto psai = reinterpret_cast(addr->ai_addr); char ip[INET6_ADDRSTRLEN]; @@ -548,6 +544,14 @@ Connection* Network::getConnection(u64id_t id) const { return found->second.get(); } +TcpServer* Network::getServer(u64id_t id) const { + const auto& found = servers.find(id); + if (found == servers.end()) { + return nullptr; + } + return found->second.get(); +} + u64id_t Network::connect(const std::string& address, int port, consumer callback) { u64id_t id = nextConnection++; auto socket = SocketConnection::connect(address, port, [id, callback]() { diff --git a/src/network/Network.hpp b/src/network/Network.hpp index 741b3ed4..911966db 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -75,6 +75,7 @@ namespace network { ); [[nodiscard]] Connection* getConnection(u64id_t id) const; + [[nodiscard]] TcpServer* getServer(u64id_t id) const; u64id_t connect(const std::string& address, int port, consumer callback); From 34974c4bb380e75a73fd010689275ee82a238118 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 17:06:06 +0300 Subject: [PATCH 43/56] add network.tcp_connect and .tcp_open --- res/scripts/classes.lua | 25 +++++++++++++++++++++++ src/network/Network.cpp | 45 ++++++++++++++++++++++++++++------------- src/network/Network.hpp | 8 +++++++- 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index 3c23fdc4..430f27f1 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -34,3 +34,28 @@ cameras.get = function(name) wrappers[name] = wrapper return wrapper end + + +local Socket = {__index={ + send=function(self, bytes) return network.__send(self.id, bytes) end, + recv=function(self, len, usetable) return network.__recv(self.id, len, usetable) end, + close=function(self) return network.__close(self.id) end, +}} + +network.tcp_connect = function(address, port, callback) + local socket = setmetatable({id=0}, Socket) + return setmetatable({id=network.__connect(address, port, function(id) + socket.id = id + callback(socket) + end)}, Socket) +end + +local ServerSocket = {__index={ + close=function(self) return network.__closeserver(self.id) end, +}} + +network.tcp_open = function(port, handler) + return setmetatable({id=network.__open(port, function(id) + handler(setmetatable({id=id}, Socket)) + end)}, ServerSocket) +end diff --git a/src/network/Network.cpp b/src/network/Network.cpp index c6736ada..3fae398d 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #ifdef _WIN32 @@ -426,6 +425,7 @@ class SocketTcpSServer : public TcpServer { Network* network; SOCKET descriptor; std::vector clients; + std::mutex clientsMutex; bool open = true; std::unique_ptr thread = nullptr; public: @@ -457,7 +457,10 @@ public: clientDescriptor, nullptr, to_string(&address) ); u64id_t id = network->addConnection(socket); - clients.push_back(id); + { + std::lock_guard lock(clientsMutex); + clients.push_back(id); + } handler(id); } }); @@ -469,9 +472,13 @@ public: } logger.info() << "closing server"; open = false; - for (u64id_t clientid : clients) { - if (auto client = network->getConnection(clientid)) { - client->close(); + + { + std::lock_guard lock(clientsMutex); + for (u64id_t clientid : clients) { + if (auto client = network->getConnection(clientid)) { + client->close(); + } } } clients.clear(); @@ -536,7 +543,9 @@ void Network::get( requests->get(url, onResponse, onReject, maxSize); } -Connection* Network::getConnection(u64id_t id) const { +Connection* Network::getConnection(u64id_t id) { + std::lock_guard lock(connectionsMutex); + const auto& found = connections.find(id); if (found == connections.end()) { return nullptr; @@ -553,6 +562,8 @@ TcpServer* Network::getServer(u64id_t id) const { } u64id_t Network::connect(const std::string& address, int port, consumer callback) { + std::lock_guard lock(connectionsMutex); + u64id_t id = nextConnection++; auto socket = SocketConnection::connect(address, port, [id, callback]() { callback(id); @@ -569,29 +580,35 @@ u64id_t Network::openServer(int port, consumer handler) { } u64id_t Network::addConnection(const std::shared_ptr& socket) { + std::lock_guard lock(connectionsMutex); + u64id_t id = nextConnection++; connections[id] = std::move(socket); return id; } size_t Network::getTotalUpload() const { - size_t totalUpload = 0; - for (const auto& [_, socket] : connections) { - totalUpload += socket->getTotalUpload(); - } return requests->getTotalUpload() + totalUpload; } size_t Network::getTotalDownload() const { - size_t totalDownload = 0; - for (const auto& [_, socket] : connections) { - totalDownload += socket->getTotalDownload(); - } return requests->getTotalDownload() + totalDownload; } void Network::update() { requests->update(); + + totalDownload = 0; + totalUpload = 0; + { + std::lock_guard lock(connectionsMutex); + for (const auto& [_, socket] : connections) { + totalDownload += socket->getTotalDownload(); + } + for (const auto& [_, socket] : connections) { + totalUpload += socket->getTotalUpload(); + } + } } std::unique_ptr Network::create(const NetworkSettings& settings) { diff --git a/src/network/Network.hpp b/src/network/Network.hpp index 911966db..a6ccb864 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "typedefs.hpp" #include "settings.hpp" @@ -58,11 +59,16 @@ namespace network { class Network { std::unique_ptr requests; + std::unordered_map> connections; + std::mutex connectionsMutex {}; u64id_t nextConnection = 1; std::unordered_map> servers; u64id_t nextServer = 1; + + size_t totalDownload = 0; + size_t totalUpload = 0; public: Network(std::unique_ptr requests); ~Network(); @@ -74,7 +80,7 @@ namespace network { long maxSize=0 ); - [[nodiscard]] Connection* getConnection(u64id_t id) const; + [[nodiscard]] Connection* getConnection(u64id_t id); [[nodiscard]] TcpServer* getServer(u64id_t id) const; u64id_t connect(const std::string& address, int port, consumer callback); From 951d2fd1f8d163c30f0148a12a794c6f140472bf Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 17:46:09 +0300 Subject: [PATCH 44/56] add socket:is_alive, :is_connected, serversocket:is_open --- res/scripts/classes.lua | 7 +++-- src/logic/scripting/lua/libs/libnetwork.cpp | 31 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index 430f27f1..255594c4 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -37,9 +37,11 @@ end local Socket = {__index={ - send=function(self, bytes) return network.__send(self.id, bytes) end, - recv=function(self, len, usetable) return network.__recv(self.id, len, usetable) end, + send=function(self, ...) return network.__send(self.id, ...) end, + recv=function(self, ...) return network.__recv(self.id, ...) end, close=function(self) return network.__close(self.id) end, + is_alive=function(self) return network.__is_alive(self.id) end, + is_connected=function(self) return network.__is_connected(self.id) end, }} network.tcp_connect = function(address, port, callback) @@ -52,6 +54,7 @@ end local ServerSocket = {__index={ close=function(self) return network.__closeserver(self.id) end, + is_open=function(self) return network.__is_serveropen(self.id) end, }} network.tcp_open = function(port, handler) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 6a6c5e69..a2b49cc1 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -131,6 +131,34 @@ static int l_open(lua::State* L) { return lua::pushinteger(L, id); } +static int l_is_alive(lua::State* L) { + u64id_t id = lua::tointeger(L, 1); + if (auto connection = engine->getNetwork().getConnection(id)) { + return lua::pushboolean( + L, connection->getState() != network::ConnectionState::CLOSED + ); + } + return lua::pushboolean(L, false); +} + +static int l_is_connected(lua::State* L) { + u64id_t id = lua::tointeger(L, 1); + if (auto connection = engine->getNetwork().getConnection(id)) { + return lua::pushboolean( + L, connection->getState() == network::ConnectionState::CONNECTED + ); + } + return lua::pushboolean(L, false); +} + +static int l_is_serveropen(lua::State* L) { + u64id_t id = lua::tointeger(L, 1); + if (auto server = engine->getNetwork().getServer(id)) { + return lua::pushboolean(L, server->isOpen()); + } + return lua::pushboolean(L, false); +} + const luaL_Reg networklib[] = { {"get", lua::wrap}, {"get_binary", lua::wrap}, @@ -140,5 +168,8 @@ const luaL_Reg networklib[] = { {"__close", lua::wrap}, {"__send", lua::wrap}, {"__recv", lua::wrap}, + {"__is_alive", lua::wrap}, + {"__is_connected", lua::wrap}, + {"__is_serveropen", lua::wrap}, {NULL, NULL} }; From af5902f98e95004ecd9e17145b43f1575a3e9b5e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 17:50:53 +0300 Subject: [PATCH 45/56] fix network.tcp_connect --- res/scripts/classes.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/scripts/classes.lua b/res/scripts/classes.lua index 255594c4..200f305f 100644 --- a/res/scripts/classes.lua +++ b/res/scripts/classes.lua @@ -46,10 +46,10 @@ local Socket = {__index={ network.tcp_connect = function(address, port, callback) local socket = setmetatable({id=0}, Socket) - return setmetatable({id=network.__connect(address, port, function(id) - socket.id = id + socket.id = network.__connect(address, port, function(id) callback(socket) - end)}, Socket) + end) + return socket end local ServerSocket = {__index={ From 495dd5f8f2b0eafb11bab168e0b039df17b94d17 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 17:53:23 +0300 Subject: [PATCH 46/56] add localhost 'network' library test --- res/scripts/world.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/res/scripts/world.lua b/res/scripts/world.lua index 964100e7..48773a06 100644 --- a/res/scripts/world.lua +++ b/res/scripts/world.lua @@ -1,3 +1,25 @@ -- use for engine development tests -- must be empty in release -- must not be modified by content-packs + +local server = network.tcp_open(65343, function (socket) + print("connected client", socket.id) + socket:send(utf8.tobytes("Hello, World!")) + socket:close() +end) +print("server", server.id) + +local socket = network.tcp_connect('localhost', 65343, function (socket) + print("connected", socket.id) +end) + +function on_world_tick() + local result = socket:recv(128) + if result and #result > 0 then + print("received from server:", utf8.tostring(result)) + end +end + +function on_world_quit() + server:close() +end From 7e0c95d126c8a430488dd7d6f6de02c333172b56 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 19:54:17 +0300 Subject: [PATCH 47/56] prevent uncaught exception in thread --- src/network/Network.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 3fae398d..ffd4881b 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -286,7 +286,8 @@ class SocketConnection : public Connection { closesocket(descriptor); freeaddrinfo(addr); state = ConnectionState::CLOSED; - throw error; + logger.error() << error.what(); + return; } logger.info() << "connected to " << to_string(addr); state = ConnectionState::CONNECTED; @@ -311,7 +312,9 @@ public: void connect(runnable callback) override { thread = std::make_unique([this, callback]() { connectSocket(); - callback(); + if (state == ConnectionState::CONNECTED) { + callback(); + } while (state == ConnectionState::CONNECTED) { int size = recvsocket(descriptor, buffer.data(), buffer.size()); if (size == 0) { From 6af6eda78e3c9ff8cb3302e9051f8698f0aeff4e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 20:24:09 +0300 Subject: [PATCH 48/56] refactor Network --- src/network/Network.cpp | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index ffd4881b..60d9d39d 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -246,7 +246,7 @@ static inline int sendsocket( static std::string to_string(const sockaddr_in* addr) { char ip[INET_ADDRSTRLEN]; if (inet_ntop(AF_INET, &(addr->sin_addr), ip, INET_ADDRSTRLEN)) { - return std::string(ip)+":"+std::to_string(addr->sin_port); + return std::string(ip)+":"+std::to_string(htons(addr->sin_port)); } return ""; } @@ -258,7 +258,7 @@ static std::string to_string(const addrinfo* addr) { auto psai = reinterpret_cast(addr->ai_addr); char ip[INET6_ADDRSTRLEN]; if (inet_ntop(addr->ai_family, &(psai->sin6_addr), ip, INET6_ADDRSTRLEN)) { - return std::string(ip)+":"+std::to_string(psai->sin6_port); + return std::string(ip)+":"+std::to_string(htons(psai->sin6_port)); } } return ""; @@ -267,8 +267,7 @@ static std::string to_string(const addrinfo* addr) { class SocketConnection : public Connection { SOCKET descriptor; bool open = true; - addrinfo* addr; - std::string addrString; + sockaddr_in addr; size_t totalUpload = 0; size_t totalDownload = 0; ConnectionState state = ConnectionState::INITIAL; @@ -279,22 +278,21 @@ class SocketConnection : public Connection { void connectSocket() { state = ConnectionState::CONNECTING; - logger.info() << "connecting to " << to_string(addr); - int res = connectsocket(descriptor, addr->ai_addr, addr->ai_addrlen); + logger.info() << "connecting to " << to_string(&addr); + int res = connectsocket(descriptor, (const sockaddr*)&addr, sizeof(sockaddr_in)); if (res < 0) { auto error = handle_socket_error("Connect failed"); closesocket(descriptor); - freeaddrinfo(addr); state = ConnectionState::CLOSED; logger.error() << error.what(); return; } - logger.info() << "connected to " << to_string(addr); + logger.info() << "connected to " << to_string(&addr); state = ConnectionState::CONNECTED; } public: - SocketConnection(SOCKET descriptor, addrinfo* addr, const std::string& addrString) - : descriptor(descriptor), addr(addr), addrString(addrString), buffer(16'384) {} + SocketConnection(SOCKET descriptor, sockaddr_in addr) + : descriptor(descriptor), addr(std::move(addr)), buffer(16'384) {} ~SocketConnection() { if (state != ConnectionState::CLOSED) { @@ -304,9 +302,6 @@ public: if (thread) { thread->join(); } - if (addr) { - freeaddrinfo(addr); - } } void connect(runnable callback) override { @@ -318,13 +313,13 @@ public: while (state == ConnectionState::CONNECTED) { int size = recvsocket(descriptor, buffer.data(), buffer.size()); if (size == 0) { - logger.info() << "closed connection " << to_string(addr); + logger.info() << "closed connection with " << to_string(&addr); closesocket(descriptor); state = ConnectionState::CLOSED; break; } else if (size < 0) { logger.info() << "an error ocurred while receiving from " - << to_string(addr); + << to_string(&addr); auto error = handle_socket_error("recv(...) error"); closesocket(descriptor); state = ConnectionState::CLOSED; @@ -338,7 +333,7 @@ public: } totalDownload += size; } - logger.info() << "read " << size << " bytes from " << to_string(addr); + logger.info() << "read " << size << " bytes from " << to_string(&addr); } }); } @@ -403,18 +398,21 @@ public: addrinfo* addrinfo; if (int res = getaddrinfo( - address.c_str(), std::to_string(port).c_str(), &hints, &addrinfo + address.c_str(), nullptr, &hints, &addrinfo )) { throw std::runtime_error(gai_strerror(res)); } - SOCKET descriptor = socket( - addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol - ); + + sockaddr_in serverAddress = *(sockaddr_in*)addrinfo->ai_addr; + freeaddrinfo(addrinfo); + serverAddress.sin_port = htons(port); + + SOCKET descriptor = socket(AF_INET, SOCK_STREAM, 0); if (descriptor == -1) { freeaddrinfo(addrinfo); throw std::runtime_error("Could not create socket"); } - auto socket = std::make_shared(descriptor, addrinfo, to_string(addrinfo)); + auto socket = std::make_shared(descriptor, std::move(serverAddress)); socket->connect(std::move(callback)); return socket; } @@ -457,7 +455,7 @@ public: } logger.info() << "client connected: " << to_string(&address); auto socket = std::make_shared( - clientDescriptor, nullptr, to_string(&address) + clientDescriptor, address ); u64id_t id = network->addConnection(socket); { From 43a838b1a525edcff05c8a007867d17bade4c507 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 20:31:05 +0300 Subject: [PATCH 49/56] fix WSAEAFNOSUPPORT (10047) --- src/network/Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 60d9d39d..f9379286 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -393,7 +393,7 @@ public: ) { addrinfo hints {}; - hints.ai_family = AF_UNSPEC; + hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; addrinfo* addrinfo; From 79e311dd912b70b850c20d23cac55df75929eb7c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 20:35:36 +0300 Subject: [PATCH 50/56] fix curl cleanup --- src/network/Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index f9379286..5b0c4975 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -69,8 +69,8 @@ public: } virtual ~CurlRequests() { - curl_easy_cleanup(curl); curl_multi_remove_handle(multiHandle, curl); + curl_easy_cleanup(curl); curl_multi_cleanup(multiHandle); } From 6b8ee77ff5ba54f9e4eac62657543406d9838722 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 20:46:40 +0300 Subject: [PATCH 51/56] fix server shutdown with winsocks --- src/network/Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 5b0c4975..bc471fb2 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -449,7 +449,7 @@ public: SOCKET clientDescriptor; sockaddr_in address; logger.info() << "accepting clients"; - if ((clientDescriptor = accept(descriptor, (sockaddr*)&address, &addrlen)) < 0) { + if ((clientDescriptor = accept(descriptor, (sockaddr*)&address, &addrlen)) == -1) { close(); break; } From 3b3407aef6131a135769ff6ed9f97479f41df527 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 21:18:11 +0300 Subject: [PATCH 52/56] remove unused function --- src/network/Network.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/network/Network.cpp b/src/network/Network.cpp index bc471fb2..15e7bdc2 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -251,19 +251,6 @@ static std::string to_string(const sockaddr_in* addr) { return ""; } -static std::string to_string(const addrinfo* addr) { - if (addr->ai_family == AF_INET) { - return to_string(reinterpret_cast(addr->ai_addr)); - } else if (addr->ai_family == AF_INET6) { - auto psai = reinterpret_cast(addr->ai_addr); - char ip[INET6_ADDRSTRLEN]; - if (inet_ntop(addr->ai_family, &(psai->sin6_addr), ip, INET6_ADDRSTRLEN)) { - return std::string(ip)+":"+std::to_string(htons(psai->sin6_port)); - } - } - return ""; -} - class SocketConnection : public Connection { SOCKET descriptor; bool open = true; From 637dfa66c2bdf707f03e22c8fb86dab7734e1645 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 21:33:20 +0300 Subject: [PATCH 53/56] add 'null' support to json parser --- src/coders/json.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coders/json.cpp b/src/coders/json.cpp index f6b27f51..8164371b 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -241,6 +241,8 @@ dv::value Parser::parseValue() { return INFINITY; } else if (literal == "nan") { return NAN; + } else if (literal == "null") { + return nullptr; } throw error("invalid keyword " + literal); } From 6e90568d2a162443e9b7d5ae8f9b8e8ff00ba87b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 27 Nov 2024 21:40:48 +0300 Subject: [PATCH 54/56] add util::escape 'escapeUnicode' option --- src/coders/json.cpp | 2 +- src/util/stringutil.cpp | 10 +++++++--- src/util/stringutil.hpp | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 8164371b..017f7d26 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -73,7 +73,7 @@ void stringifyValue( break; } case value_type::string: - ss << util::escape(value.asString()); + ss << util::escape(value.asString(), !nice); break; case value_type::number: ss << std::setprecision(15) << value.asNumber(); diff --git a/src/util/stringutil.cpp b/src/util/stringutil.cpp index d1148da5..588da4a4 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -7,7 +7,7 @@ #include #include -std::string util::escape(std::string_view s) { +std::string util::escape(std::string_view s, bool escapeUnicode) { std::stringstream ss; ss << '"'; size_t pos = 0; @@ -39,8 +39,12 @@ std::string util::escape(std::string_view s) { if (c & 0x80) { uint cpsize; int codepoint = decode_utf8(cpsize, s.data() + pos); + if (escapeUnicode) { + ss << "\\u" << std::hex << codepoint; + } else { + ss << std::string(s.data() + pos, cpsize); + } pos += cpsize-1; - ss << "\\u" << std::hex << codepoint; break; } if (c < ' ') { @@ -57,7 +61,7 @@ std::string util::escape(std::string_view s) { } std::string util::quote(const std::string& s) { - return escape(s); + return escape(s, false); } std::wstring util::lfill(std::wstring s, uint length, wchar_t c) { diff --git a/src/util/stringutil.hpp b/src/util/stringutil.hpp index edd0df17..ed3061f5 100644 --- a/src/util/stringutil.hpp +++ b/src/util/stringutil.hpp @@ -8,7 +8,7 @@ namespace util { /// @brief Function used for string serialization in text formats - std::string escape(std::string_view s); + std::string escape(std::string_view s, bool escapeUnicode=true); /// @brief Function used for error messages std::string quote(const std::string& s); From 46628263d1554792efcb36be404a50f8b01c005a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 28 Nov 2024 10:21:16 +0300 Subject: [PATCH 55/56] add network.get_total_upload, .get_total_download --- src/logic/scripting/lua/libs/libnetwork.cpp | 10 ++++++ src/network/Network.cpp | 37 +++++++++++++++------ src/network/Network.hpp | 4 +-- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index a2b49cc1..a2d48b5b 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -159,9 +159,19 @@ static int l_is_serveropen(lua::State* L) { return lua::pushboolean(L, false); } +static int l_get_total_upload(lua::State* L) { + return lua::pushinteger(L, engine->getNetwork().getTotalUpload()); +} + +static int l_get_total_download(lua::State* L) { + return lua::pushinteger(L, engine->getNetwork().getTotalDownload()); +} + const luaL_Reg networklib[] = { {"get", lua::wrap}, {"get_binary", lua::wrap}, + {"get_total_upload", lua::wrap}, + {"get_total_download", lua::wrap}, {"__open", lua::wrap}, {"__closeserver", lua::wrap}, {"__connect", lua::wrap}, diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 15e7bdc2..f0ebd3ca 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -367,12 +367,16 @@ public: } } - size_t getTotalUpload() const override { - return totalUpload; + size_t pullUpload() override { + size_t size = totalUpload; + totalUpload = 0; + return size; } - size_t getTotalDownload() const override { - return totalDownload; + size_t pullDownload() override { + size_t size = totalDownload; + totalDownload = 0; + return size; } static std::shared_ptr connect( @@ -586,15 +590,28 @@ size_t Network::getTotalDownload() const { void Network::update() { requests->update(); - totalDownload = 0; - totalUpload = 0; { std::lock_guard lock(connectionsMutex); - for (const auto& [_, socket] : connections) { - totalDownload += socket->getTotalDownload(); + auto socketiter = connections.begin(); + while (socketiter != connections.end()) { + auto socket = socketiter->second.get(); + totalDownload += socket->pullDownload(); + totalUpload += socket->pullUpload(); + if (socket->available() == 0 && + socket->getState() == ConnectionState::CLOSED) { + socketiter = connections.erase(socketiter); + continue; + } + ++socketiter; } - for (const auto& [_, socket] : connections) { - totalUpload += socket->getTotalUpload(); + auto serveriter = servers.begin(); + while (serveriter != servers.end()) { + auto server = serveriter->second.get(); + if (!server->isOpen()) { + serveriter = servers.erase(serveriter); + continue; + } + ++serveriter; } } } diff --git a/src/network/Network.hpp b/src/network/Network.hpp index a6ccb864..756784f4 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -43,8 +43,8 @@ namespace network { virtual void close() = 0; virtual int available() = 0; - virtual size_t getTotalUpload() const = 0; - virtual size_t getTotalDownload() const = 0; + virtual size_t pullUpload() = 0; + virtual size_t pullDownload() = 0; virtual ConnectionState getState() const = 0; }; From 7e566a51a3222030cb5ff5c3f7f7ca6259d0e73b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 28 Nov 2024 10:21:50 +0300 Subject: [PATCH 56/56] remove test script --- res/scripts/world.lua | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/res/scripts/world.lua b/res/scripts/world.lua index 48773a06..964100e7 100644 --- a/res/scripts/world.lua +++ b/res/scripts/world.lua @@ -1,25 +1,3 @@ -- use for engine development tests -- must be empty in release -- must not be modified by content-packs - -local server = network.tcp_open(65343, function (socket) - print("connected client", socket.id) - socket:send(utf8.tobytes("Hello, World!")) - socket:close() -end) -print("server", server.id) - -local socket = network.tcp_connect('localhost', 65343, function (socket) - print("connected", socket.id) -end) - -function on_world_tick() - local result = socket:recv(128) - if result and #result > 0 then - print("received from server:", utf8.tostring(result)) - end -end - -function on_world_quit() - server:close() -end