diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1548c4d..d432744 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -145,6 +145,11 @@ Functions like `malloc`, `calloc`, and `free` are forbidden in C++ code. Speaking of class layout, [*the rule of zero*](https://en.cppreference.com/w/cpp/language/rule_of_three.html) is preferred +### Code documenting + +Comments in code are preferred, but not always necessary. There are some basic rules of proper code documentation, +which can be found [here](https://developer.lsst.io/cpp/api-docs.html). + --- If you find code that doesn’t follow these guidelines, [open an issue](https://github.com/kbrddestroyer/AxonEngine/issues/new) with the `refactoring` label. diff --git a/cmake/build-utils.cmake b/cmake/build-utils.cmake index b4104e7..67222fb 100644 --- a/cmake/build-utils.cmake +++ b/cmake/build-utils.cmake @@ -10,7 +10,7 @@ function(add_all_subdirs) endforeach() endfunction() -function(list_all_tests name_pattern) +function(list_all_tests_separate name_pattern) file (GLOB_RECURSE TEST_SOURCES ${name_pattern}) foreach (TEST_SRC ${TEST_SOURCES}) @@ -31,3 +31,20 @@ function(list_all_tests name_pattern) message ("Loaded test sequence: ${FILENAME}") endforeach () endfunction() + +function(list_all_tests name_pattern) + file (GLOB_RECURSE TEST_SOURCES ${name_pattern}) + get_filename_component(FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME_WE) + string (TOUPPER ${FOLDER} FOLDER) + + add_executable(TEST_${FOLDER}_TARGET ${TEST_SOURCES} "entry.cpp") + add_test(NAME TEST_${FOLDER} COMMAND $) + target_link_libraries(TEST_${FOLDER}_TARGET PRIVATE ${ARGN}) + + add_custom_command(TARGET TEST_${FOLDER}_TARGET POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy -t $ $ + COMMAND_EXPAND_LISTS + ) + + message ("Loaded test folder: TEST_${FOLDER}") +endfunction() \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index e69de29..65afe60 100644 --- a/examples/README.md +++ b/examples/README.md @@ -0,0 +1,2 @@ +# Examples + diff --git a/examples/basics/basic-synapse/client.cpp b/examples/basics/basic-synapse/client.cpp index ab8060f..f7d5ff3 100644 --- a/examples/basics/basic-synapse/client.cpp +++ b/examples/basics/basic-synapse/client.cpp @@ -1,12 +1,12 @@ /* -* This example shows basic connection, using Axon internal tools -* Synapse provides connection between two points +* This example shows basic client connection, using Axon internal tools. +* Synapse provides connection between two points, this one is created in client mode. * -* This file defined basic client logic, with AxonMessage forming and sending with Synapse tool +* This file defined basic client logic. It creates Message package and sends it to server. Also, it is capable of +* server messages handling, displaying response packages. */ -#include -#include +#include #include #include @@ -34,12 +34,12 @@ void onMessageReceived(const Networking::SynapseMessageReceivedEvent& event) int main() { - Networking::ConnectionInfo connection = { "localhost", 10423 }; - - Networking::AsyncSynapse clientConnection(connection); + Networking::AsyncSynapse< + Networking::BerkeleyAxonNetworkController + > clientConnection("localhost", 10423); clientConnection.getEventManager().subscribe(onMessageReceived); - clientConnection.start(); + time_t startTimestamp = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); Networking::AxonMessage msg(nullptr, 0); @@ -50,12 +50,15 @@ int main() { std::this_thread::sleep_for(std::chrono::seconds(1)); - std::stringstream sstream; + std::stringstream stream; - sstream << + stream << "Sending message on " << std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) - startTimestamp; - Networking::AxonMessage message_(const_cast(sstream.str().c_str()), sstream.str().length() + 1, 0, Networking::TAG_FLAGS::VALIDATE); + + std::cout << "Sending: " << stream.str() << std::endl; + + Networking::AxonMessage message_(const_cast(stream.str().c_str()), stream.str().length() + 2, 0, Networking::TAG_FLAGS::VALIDATE); clientConnection.send(message_); } diff --git a/examples/basics/basic-synapse/server.cpp b/examples/basics/basic-synapse/server.cpp index 8d9e39a..bcfd370 100644 --- a/examples/basics/basic-synapse/server.cpp +++ b/examples/basics/basic-synapse/server.cpp @@ -1,11 +1,11 @@ /* -* This example shows basic connection, using Axon internal tools -* Synapse provides connection between two points +* This example shows basic server, using Axon internal tools. +* Synapse provides connection between two points, this one is created in server mode. * -* This file defines basic server logic, with message handling using Synapse event system +* This file shows basic server logic and message handling using asynchronous Synapse with event system. */ -#include +#include #include #include @@ -31,7 +31,9 @@ void onMessageReceived(const Networking::SynapseMessageReceivedEvent& event) int main() { - Networking::Synapse serverConnection(10423); + Networking::Synapse< + Networking::BerkeleyAxonNetworkController + > serverConnection(10423); serverConnection.getEventManager().subscribe(onMessageReceived); std::cout << "Starting Synapse connection" << std::endl; diff --git a/include/common_macro.h b/include/common_macro.h index 58d0259..6caad64 100644 --- a/include/common_macro.h +++ b/include/common_macro.h @@ -1,3 +1,7 @@ +/** + * Defines some useful macro, mostly for C code and backends. + */ + #ifndef AXONENGINE_COMMON_MACRO_H #define AXONENGINE_COMMON_MACRO_H diff --git a/include/custom_utility.h b/include/custom_utility.h index a62b49c..74abb28 100644 --- a/include/custom_utility.h +++ b/include/custom_utility.h @@ -1,3 +1,7 @@ +/** + * Contains utility types, functions, etc. + */ + #ifndef CUSTOM_UTILITY_H #define CUSTOM_UTILITY_H @@ -6,8 +10,18 @@ #else #include #endif -// Define alias for uint64_t to be used instead of size_t -// as size_t may have different size on different machines (i.e. x86 vs x64) + +#include "common_macro.h" + +/** +* Define alias for uint64_t to be used instead of size_t +* as size_t may have different size on different machines (i.e. x86 vs x64) +*/ typedef uint64_t size64_t; +struct Socket +{ + SOCKET_T socket; + SOCKADDR_IN_T conn; +}; #endif //CUSTOM_UTILITY_H diff --git a/include/netconfig.h b/include/netconfig.h index c407114..49ec335 100644 --- a/include/netconfig.h +++ b/include/netconfig.h @@ -1,4 +1,4 @@ #pragma once #define SYNAPSE_PAYLOAD_SIZE_MAX 16 -#define SYNAPSE_MESSAGE_SIZE_MAX SYNAPSE_PAYLOAD_SIZE_MAX + 16 + 64 +#define SYNAPSE_MESSAGE_SIZE_MAX (SYNAPSE_PAYLOAD_SIZE_MAX + 16 + 64) diff --git a/libraries/AxonUtility.h b/libraries/AxonUtility.h index 2d793fb..a75497f 100644 --- a/libraries/AxonUtility.h +++ b/libraries/AxonUtility.h @@ -3,16 +3,19 @@ #include -#if defined(MSVC) || defined(__GNUC__) || defined(__clang__) +#if defined(MSVC) || defined(__GNUC__) || defined(__clang__) || defined(WINDOWS_PLATFORM) #define GETTER [[nodiscard("Ignoring getter return value is not allowed. For god's sake, have some respect!")]] #else #define GETTER #endif #if defined(WINDOWS_PLATFORM) -#define GETTER #if defined(AXON_LIB) +#if defined(AXON_STATIC) +#define AXON_DECLSPEC +#else #define AXON_DECLSPEC __declspec(dllexport) +#endif #else #define AXON_DECLSPEC __declspec(dllimport) #endif @@ -30,3 +33,4 @@ #define WGETTER GETTER_WNE #endif + diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 5e2c8ef..d9e3ece 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -5,5 +5,6 @@ message(STATUS "Searching for build config in ${V_GLOB}") set (CMAKE_POSITION_INDEPENDENT_CODE ON) set (COMMON_SRC "AxonUtility.h" PARENT_SCOPE) +set (LIBS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) add_all_subdirs() \ No newline at end of file diff --git a/libraries/backends/CMakeLists.txt b/libraries/backends/CMakeLists.txt index 495936a..d166ae7 100644 --- a/libraries/backends/CMakeLists.txt +++ b/libraries/backends/CMakeLists.txt @@ -1,7 +1,8 @@ -project ("AxonEngine.libraries") +project ("AxonEngine.libraries.backends") file(GLOB_RECURSE SOURCES "*.c" "*.h" "*.cpp" "*.hpp") add_library(backends STATIC ${SOURCES}) -target_include_directories(backends PUBLIC "${CMAKE_HOME_DIRECTORY}/libraries") +target_include_directories(backends PUBLIC ${LIBS_DIR}) target_include_directories(backends PUBLIC "${CMAKE_HOME_DIRECTORY}/common") +target_compile_definitions(backends PUBLIC AXON_LIB) diff --git a/libraries/backends/backend.cpp b/libraries/backends/backend.cpp index be45f92..f56ca54 100644 --- a/libraries/backends/backend.cpp +++ b/libraries/backends/backend.cpp @@ -30,12 +30,12 @@ template <> uint8_t initialize_server(Socket& socket, uint32_t port) return create_udp_server(&socket.conn, &socket.socket, port); } -template <> uint8_t initialize_client(Socket& socket, const char* hostname, uint32_t port) +template <> uint8_t initialize_client(Socket& socket, const char* hostname, const uint32_t port) { return connect_tcp_client(&socket.conn, &socket.socket, hostname, port); } -template <> uint8_t initialize_client(Socket& socket, const char* hostname, uint32_t port) +template <> uint8_t initialize_client(Socket& socket, const char* hostname, const uint32_t port) { return connect_udp_client(&socket.conn, &socket.socket, hostname, port); } diff --git a/libraries/backends/backend.hpp b/libraries/backends/backend.hpp index d8bc5c5..99fc06e 100644 --- a/libraries/backends/backend.hpp +++ b/libraries/backends/backend.hpp @@ -1,27 +1,23 @@ +/** + * backend library. + * Contains functions for protocol-independent socket functions usage. Works with Berkeley sockets. + */ + #pragma once #ifdef __cplusplus extern "C" { #endif -#if defined(USE_BERKELEY) -#include "berkeley/basic_networking.h" -#else - /* DEFAULT */ -#endif - #include "berkeley/basic_networking.h" +#include "custom_utility.h" #ifdef __cplusplus } #endif -template int32_t send_message(const Socket& socket, const void* message, size_t size); - -template int32_t recv_message(Socket& socket, void* buffer, size_t size_allocated); - -template uint8_t initialize_server(Socket& socket, uint32_t port); - -template uint8_t initialize_client(Socket& socket, const char* hostname, uint32_t port); - +template int32_t send_message(const Socket& socket, const void* message, size_t size) = delete; +template int32_t recv_message(Socket& socket, void* buffer, size_t size_allocated) = delete; +template uint8_t initialize_server(Socket& socket, uint32_t port) = delete; +template uint8_t initialize_client(Socket& socket, const char* hostname, uint32_t port) = delete; template void finalize(Socket& socket); template <> int32_t send_message(const Socket&, const void*, size_t); @@ -33,6 +29,8 @@ template <> int32_t recv_message(Socket&, void*, size_t); template <> uint8_t initialize_server(Socket&, uint32_t); template <> uint8_t initialize_server(Socket&, uint32_t); +template <> uint8_t initialize_client(Socket&, const char*, uint32_t); +template <> uint8_t initialize_client(Socket&, const char*, uint32_t); template <> void finalize(Socket &); template <> void finalize(Socket &); diff --git a/libraries/backends/berkeley/basic_networking.h b/libraries/backends/berkeley/basic_networking.h index dc61d0d..c101f74 100644 --- a/libraries/backends/berkeley/basic_networking.h +++ b/libraries/backends/berkeley/basic_networking.h @@ -1,4 +1,4 @@ -/* +/** * Can be used as template for any other backends * * Defines some low-level socket logic. Direct usage is not recommended @@ -12,12 +12,6 @@ #include "udp/udp_connection.h" #include "tcp/tcp_connection.h" -struct Socket -{ - SOCKET_T socket; - SOCKADDR_IN_T conn; -}; - #endif /* basic_networking.h */ diff --git a/libraries/backends/berkeley/master_include.h b/libraries/backends/berkeley/master_include.h index 04e3815..6d6885e 100644 --- a/libraries/backends/berkeley/master_include.h +++ b/libraries/backends/berkeley/master_include.h @@ -1,11 +1,21 @@ +/** + * Header, that aggregates some includes for every other berkeley backend source file + */ + #ifndef MASTER_INCLUDE_H #define MASTER_INCLUDE_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include - -/* common */ - #include #include + +#ifdef __cplusplus +} +#endif + #endif diff --git a/libraries/backends/berkeley/tcp/tcp_connection.c b/libraries/backends/berkeley/tcp/tcp_connection.c index a8976c1..eeaa98f 100644 --- a/libraries/backends/berkeley/tcp/tcp_connection.c +++ b/libraries/backends/berkeley/tcp/tcp_connection.c @@ -1,6 +1,27 @@ #include "tcp_connection.h" #include +/** + * Accept client connection to server + * @param server server socket + * @param [out] c_addr client information + * @return + */ +SOCKET_T accept_incoming(const SOCKET_T server, const SOCKADDR_IN_T *c_addr) +{ + SOCKLEN_T addr_s = sizeof(*c_addr); + return accept(server, (SOCKADDR_T*) c_addr, &addr_s); +} + +/** +* Connects to the remote host via TCP. Uses `getaddrinfo`. +* @param [out] server server SOCKADDR_IN_T structure +* @param client bound client socket +* @param hostname ip/hostname of a target node +* @param port port of a target node +* @returns 0 +* @returns ERR_CODE (defined in basic_networking.h) +*/ uint8_t connect_tcp_client(SOCKADDR_IN_T* server, SOCKET_T* client, const char* hostname, uint32_t port) { SOCKET_HEAD_INIT @@ -8,9 +29,7 @@ uint8_t connect_tcp_client(SOCKADDR_IN_T* server, SOCKET_T* client, const char* if (!CHECK_VALID(*client = socket(AF_INET, SOCK_STREAM, 0))) return ERR_INVALID; - ADDRINFO_T hints; - - memset(&hints, 0, sizeof(hints)); + ADDRINFO_T hints = {0}; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; @@ -39,7 +58,14 @@ uint8_t connect_tcp_client(SOCKADDR_IN_T* server, SOCKET_T* client, const char* return SUCCESS; } - +/** +* Initializes server socket with TCP protocol. +* @param [out] server server information (SOCKADDR_IN_T structure) +* @param server_socket bound server socket +* @param port port to create server onto +* @returns 0 +* @returns ERR_CODE (defined in basic_networking.h) +*/ uint8_t create_tcp_server(SOCKADDR_IN_T* server, SOCKET_T* server_socket, uint32_t port) { SOCKET_HEAD_INIT @@ -64,23 +90,35 @@ uint8_t create_tcp_server(SOCKADDR_IN_T* server, SOCKET_T* server_socket, uint32 return SUCCESS; } -int32_t send_tcp_message(const void* message, size_t size, SOCKET_T from) +/** +* Sends data over TCP +* @param message data to send, sequence of bytes +* @param size size of message, bytes. Most of the time can be strlen(message), but we cannot be sure in case, when message is serialized data +* @param from source socket +* @returns send() function result +*/ +int32_t send_tcp_message(const void* message, const size_t size, const SOCKET_T from) { return send(from, message, size, 0); } -int32_t recv_tcp_message(void* const message, size_t max_size, SOCKET_T c_sock) +/** +* Handles message receiving over TCP +* @param message data buffer, must be either a static array or pre-allocated +* @param max_size max size of bytes that can be written in buffer +* @param c_sock client socket to receive message from +* @returns c_sock result +*/ +int32_t recv_tcp_message(void* const message, const size_t max_size, const SOCKET_T c_sock) { return recv(c_sock, message, max_size, 0); } -SOCKET_T accept_incoming(SOCKET_T server, SOCKADDR_IN_T* c_addr) -{ - SOCKLEN_T addr_s = sizeof(*c_addr); - return accept(server, (SOCKADDR_T*) c_addr, &addr_s); -} - -void finalize_tcp(SOCKET_T socket) +/** +* Closes socket, on Windows machines also cleanups WSA +* @param socket socket to close +*/ +void finalize_tcp(const SOCKET_T socket) { CLOSESOCKET(socket); #if defined(WIN32) diff --git a/libraries/backends/berkeley/tcp/tcp_connection.h b/libraries/backends/berkeley/tcp/tcp_connection.h index b356127..1e21db5 100644 --- a/libraries/backends/berkeley/tcp/tcp_connection.h +++ b/libraries/backends/berkeley/tcp/tcp_connection.h @@ -5,53 +5,12 @@ #include #pragma region TCP_CONNECTION - -/** -* Connects to the remote host via TCP. Uses getaddrinfo. -* @param server -* @param client -* @param char* hostname -* @param port -* @return 0 or ERR_CODE (defined in basic_networking.h) -*/ -uint8_t connect_tcp_client(SOCKADDR_IN_T*, SOCKET_T*, const char*, uint32_t); - -/** -* Initializes server socket with TCP protocol. -* @param server -* @param server_socket -* @param port -* @returns 0 -* @returns ERR_CODE (defined in basic_networking.h) -*/ +uint8_t connect_tcp_client(SOCKADDR_IN_T *, SOCKET_T *, const char *, uint32_t); uint8_t create_tcp_server(SOCKADDR_IN_T*, SOCKET_T*, uint32_t); - -/** -* Sends data over UDP -* @param char* message - data to send, sequence of bytes -* @param size - size of message, bytes. Most of the time can be strlen(message), but we cannot be sure in case, when message is serialized data -* @param from - source -* @param to - destination -* @returns sendto result -*/ int32_t send_tcp_message(const void*, size_t, SOCKET_T); - -/** -* Handles message receiving over UDP -* @param buffer - data buffer -* @param max_size - max size of bytes that can be written in buffer -* @returns recvfrom result -*/ -int32_t recv_tcp_message(void* const, size_t, SOCKET_T); - -SOCKET_T accept_incoming(SOCKET_T, SOCKADDR_IN_T*); - -/** -* Closes socket -* @param socket -*/ +int32_t recv_tcp_message(void*, size_t, SOCKET_T); +SOCKET_T accept_incoming(SOCKET_T, const SOCKADDR_IN_T*); void finalize_tcp(SOCKET_T); - #pragma endregion #endif diff --git a/libraries/backends/berkeley/udp/udp_connection.c b/libraries/backends/berkeley/udp/udp_connection.c index 9d7b120..80ff997 100644 --- a/libraries/backends/berkeley/udp/udp_connection.c +++ b/libraries/backends/berkeley/udp/udp_connection.c @@ -5,6 +5,15 @@ #pragma region UDP_UTILS +/** +* Connects to the remote host via UDP. Uses `getaddrinfo`. +* @param [out] server server info (SOCKADDR_IN_T structure) +* @param client client socket +* @param hostname ip/hostname of a node to connect to +* @param port port of a node to connect to +* @returns 0 +* @returns ERR_CODE (defined in basic_networking.h) +*/ uint8_t connect_udp_client(SOCKADDR_IN_T* server, SOCKET_T* client, const char* hostname, uint32_t port) { SOCKET_HEAD_INIT @@ -12,9 +21,7 @@ uint8_t connect_udp_client(SOCKADDR_IN_T* server, SOCKET_T* client, const char* if (!CHECK_VALID(*client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))) return ERR_INVALID; - ADDRINFO_T hints; - - memset(&hints, 0, sizeof(hints)); + ADDRINFO_T hints = {0}; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; @@ -38,7 +45,14 @@ uint8_t connect_udp_client(SOCKADDR_IN_T* server, SOCKET_T* client, const char* return SUCCESS; } - +/** +* Initializes server socket with UDP protocol. +* @param [out] server server info (SOCKADDR_IN_T structure) +* @param [out] server_socket server socket that will be initialized +* @param port port to create server onto +* @returns 0 +* @returns ERR_CODE (defined in basic_networking.h) +*/ uint8_t create_udp_server(SOCKADDR_IN_T* server, SOCKET_T* server_socket, uint32_t port) { SOCKET_HEAD_INIT @@ -58,17 +72,37 @@ uint8_t create_udp_server(SOCKADDR_IN_T* server, SOCKET_T* server_socket, uint32 return SUCCESS; } +/** +* Sends data over UDP +* @param message data to send, sequence of bytes +* @param size size of message, bytes. Most of the time can be strlen(message), but we cannot be sure in case, when message is serialized data +* @param from source +* @param to destination +* @return sendto function result +*/ int32_t send_udp_message(const void* message, const size_t size, const SOCKET_T from, const SOCKADDR_IN_T* to) { return sendto(from, message, size, 0, (SOCKADDR_T*)to, sizeof(*to)); } +/** +* Handles message receiving over UDP +* @param message data buffer +* @param max_size max size of bytes that can be written in buffer +* @param to destination node +* @param from client information +* @return recvfrom function result +*/ int32_t recv_udp_message(void* const message, size_t max_size, SOCKET_T to, SOCKADDR_IN_T* from) { - SOCKLEN_T len = (SOCKLEN_T) sizeof(*from); - return recvfrom(to, message, max_size, 0, (SOCKADDR_T*)from, &len); + SOCKLEN_T len = sizeof(*from); + return recvfrom(to, message, max_size, 0, (SOCKADDR_T *) from, &len); } +/** +* Closes socket +* @param socket +*/ void finalize_udp(SOCKET_T socket) { CLOSESOCKET(socket); diff --git a/libraries/backends/berkeley/udp/udp_connection.h b/libraries/backends/berkeley/udp/udp_connection.h index ec00928..3a9dafc 100644 --- a/libraries/backends/berkeley/udp/udp_connection.h +++ b/libraries/backends/berkeley/udp/udp_connection.h @@ -4,58 +4,10 @@ #include #include -#pragma region UDP_UTILS -#pragma region CLIENT_UTILS - -/** -* Connects to the remote host via UDP. Uses getaddrinfo. -* @param server -* @param client -* @param hostname -* @param port -* @return 0 or ERR_CODE (defined in basic_networking.h) -*/ uint8_t connect_udp_client(SOCKADDR_IN_T*, SOCKET_T*, const char*, uint32_t); - -#pragma endregion /* CLIENT UTILITY FUNCTIONS */ - -#pragma region SERVER_UTILS - -/** -* Initializes server socket with UDP protocol. -* @param server -* @param server_socket -* @param port -* @returns 0 -* @returns ERR_CODE (defined in basic_networking.h) -*/ uint8_t create_udp_server(SOCKADDR_IN_T*, SOCKET_T*, uint32_t); - -#pragma endregion /* SERVER UTILITY FUNCTIONS */ - -/** -* Sends data over UDP -* @param message - data to send, sequence of bytes -* @param size - size of message, bytes. Most of the time can be strlen(message), but we cannot be sure in case, when message is serialized data -* @param from - source -* @param to - destination -* @return sendto result -*/ int32_t send_udp_message(const void*, size_t, SOCKET_T, const SOCKADDR_IN_T*); - -/** -* Handles message receiving over UDP -* @param buffer - data buffer -* @param max_size - max size of bytes that can be written in buffer -* @return recvfrom result -*/ int32_t recv_udp_message(void*, size_t, SOCKET_T, SOCKADDR_IN_T*); - -/** -* Closes socket -* @param socket -*/ void finalize_udp(SOCKET_T); -#pragma endregion /* UDP UTILS */ #endif diff --git a/libraries/events/AxonEvent.cpp b/libraries/events/AxonEvent.cpp index fcbd0cd..5abb61a 100644 --- a/libraries/events/AxonEvent.cpp +++ b/libraries/events/AxonEvent.cpp @@ -3,7 +3,7 @@ EventSystem::AxonEventManager::~AxonEventManager() { - for (std::pair>> calls : subscribers) + for (auto & calls : subscribers) { calls.second.clear(); } @@ -12,17 +12,17 @@ EventSystem::AxonEventManager::~AxonEventManager() void EventSystem::AxonEventManager::invoke(AxonEvent* event) { - std::type_index type = std::type_index(typeid(*event)); - std::vector> calls = subscribers[type]; + const auto type = std::type_index(typeid(*event)); + calls_c calls = subscribers[type]; - for (const std::function& callback : calls) + for (const auto & callback : calls) { - callback(event); + callback.second(event); } } -EventSystem::GlobalEventManager& EventSystem::GlobalEventManager::Instance() +EventSystem::GlobalEventManager& EventSystem::GlobalEventManager::Instance() noexcept { - static EventSystem::GlobalEventManager instance; + static GlobalEventManager instance; return instance; } diff --git a/libraries/events/AxonEvent.hpp b/libraries/events/AxonEvent.hpp index eac15aa..d5e20e3 100644 --- a/libraries/events/AxonEvent.hpp +++ b/libraries/events/AxonEvent.hpp @@ -4,39 +4,76 @@ #include #include +#include namespace EventSystem { + /** + * Base class for any AxonEvent. Provides convenient interface (or will do some day). + */ class AxonEvent { public: virtual ~AxonEvent() = default; - }; + }; + + /** + * EventManager class is capable of dispatching triggered events.\n + * Usage: + *
+ * EventManager could be created literally anywhere. + * Callback method can be attached with subscribe() method. It will be dispatched when + * invoke() message is called. + *
+ *
+ * IMPORTANT NOTE: + *
+ * Please, make sure that you unsubscribe class method in destructor. EventManager contains class method reference and will try to call it, even if object is nonexistent. This is undefined behaviour and most likely will cause a segmentation fault on Unix systems and SEH exception on Windows. + * + * @note see GlobalEventManager + * @note see client-server example + */ class AxonEventManager { + protected: + typedef uint64_t desc_t; + typedef std::type_index primary_key_t; + typedef std::function event_call_t; + typedef std::unordered_map calls_c; public: AxonEventManager() = default; virtual ~AxonEventManager(); template - void subscribe(std::function); - + void subscribe(std::function); template - void subscribe(void (C::* method)(const T&), C* instance); - - void invoke(AxonEvent*); + void subscribe(void (C::* callback)(const T&), C *instance); + + template + void unsubscribe(std::function); + + template + void unsubscribe(void (C::* callback)(const T&), C *instance); + + void invoke(AxonEvent *); + protected: + template + desc_t generateDescriptor(std::function); + + template + desc_t generateDescriptor(void (C::* callback)(const T&), C *instance); private: - std::unordered_map>> subscribers; - - friend class GlobalEventManager; + std::unordered_map subscribers; }; - class GlobalEventManager : public AxonEventManager + /** + * GlobalEventManager cannot be constructed, only accessed via Instance() method + */ + class GlobalEventManager final : public AxonEventManager { - private: GlobalEventManager() = default; public: - static GlobalEventManager& Instance(); + static GlobalEventManager& Instance() noexcept; }; } diff --git a/libraries/events/AxonEvent.ipp b/libraries/events/AxonEvent.ipp index 621e263..ce219e9 100644 --- a/libraries/events/AxonEvent.ipp +++ b/libraries/events/AxonEvent.ipp @@ -1,11 +1,13 @@ #pragma once template -inline void EventSystem::AxonEventManager::subscribe(std::function callback) +void EventSystem::AxonEventManager::subscribe(std::function callback) { - std::type_index type = std::type_index(typeid(T)); + const auto type = std::type_index(typeid(T)); - subscribers[type].push_back( + desc_t desc = generateDescriptor(callback); + + subscribers[type][desc] = ( [callback](AxonEvent* base_event) { if (T* event = dynamic_cast(base_event)) { @@ -16,11 +18,12 @@ inline void EventSystem::AxonEventManager::subscribe(std::function -inline void EventSystem::AxonEventManager::subscribe(void(C::* callback)(const T&), C* instance) +void EventSystem::AxonEventManager::subscribe(void(C::* callback)(const T&), C* instance) { - std::type_index type = std::type_index(typeid(T)); + const auto type = std::type_index(typeid(T)); + desc_t desc = generateDescriptor(callback, instance); - subscribers[type].push_back( + subscribers[type][desc] = ( [callback, instance](AxonEvent* base_event) { if (T* event = dynamic_cast(base_event)) { @@ -29,3 +32,30 @@ inline void EventSystem::AxonEventManager::subscribe(void(C::* callback)(const T } ); } + +template +void EventSystem::AxonEventManager::unsubscribe(std::function callback) { + const auto type = std::type_index(typeid(T)); + desc_t desc = generateDescriptor(callback); + + subscribers[type].erase(desc); +} + +template +void EventSystem::AxonEventManager::unsubscribe(void (C::* callback)(const T&), C *instance) { + const auto type = std::type_index(typeid(T)); + desc_t desc = generateDescriptor(callback, instance); + + subscribers[type].erase(desc); +} + +template +uint64_t EventSystem::AxonEventManager::generateDescriptor(std::function callback) { + const auto fPtr = callback.template target(); + return std::hash() (*(void**) &fPtr); +} + +template +uint64_t EventSystem::AxonEventManager::generateDescriptor(void (C::* callback)(const T&), C *instance) { + return std::hash() (instance); +} diff --git a/libraries/events/CMakeLists.txt b/libraries/events/CMakeLists.txt index c9c055b..c60ecbf 100644 --- a/libraries/events/CMakeLists.txt +++ b/libraries/events/CMakeLists.txt @@ -1,7 +1,7 @@ -project ("AxonEngine.libraries") +project ("AxonEngine.libraries.events") file(GLOB_RECURSE SOURCES "*.c" "*.h" "*.cpp" "*.hpp" "*.ipp") add_library(events STATIC ${SOURCES}) -set (LIB_HOME ${CMAKE_HOME_DIRECTORY}/libraries) -target_include_directories(events PUBLIC "${LIB_HOME}") +target_include_directories(events PUBLIC "${LIBS_DIR}") +target_compile_definitions(events PUBLIC AXON_LIB) diff --git a/libraries/networking/message/AxonBinaryStream.cpp b/libraries/messages/AxonBinaryStream.cpp similarity index 94% rename from libraries/networking/message/AxonBinaryStream.cpp rename to libraries/messages/AxonBinaryStream.cpp index 4dfaeaa..35b6981 100644 --- a/libraries/networking/message/AxonBinaryStream.cpp +++ b/libraries/messages/AxonBinaryStream.cpp @@ -64,7 +64,7 @@ namespace Networking { return *this; } - void Networking::AxonBinaryStreamBase::append(const char* data, std::size_t dataSize) + void AxonBinaryStreamBase::append(const char* data, std::size_t dataSize) { char* containerBufferPointer = new char[containerSize + dataSize]; memcpy(containerBufferPointer, containerPtr, containerSize); @@ -74,7 +74,7 @@ namespace Networking { containerSize += dataSize; } - void Networking::AxonBinaryStreamBase::clear() { + void AxonBinaryStreamBase::clear() { delete[] containerPtr; containerSize = 0; } diff --git a/libraries/networking/message/AxonBinaryStream.hpp b/libraries/messages/AxonBinaryStream.hpp similarity index 67% rename from libraries/networking/message/AxonBinaryStream.hpp rename to libraries/messages/AxonBinaryStream.hpp index 89c3f0e..25e5edb 100644 --- a/libraries/networking/message/AxonBinaryStream.hpp +++ b/libraries/messages/AxonBinaryStream.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include "AxonUtility.h" namespace Networking { class AXON_DECLSPEC AxonBinaryStreamBase { @@ -16,11 +16,16 @@ namespace Networking { /* API */ void append(const char*, size_t); - inline const char* data() const noexcept { return containerPtr; } - inline size_t size() const noexcept { return containerSize; } + GETTER const char* data() const noexcept { return containerPtr; } + GETTER size_t size() const noexcept { return containerSize; } void clear(); - private: + protected: char* containerPtr = nullptr; size_t containerSize = 0; }; + + class AXON_DECLSPEC AxonBinaryStream : public AxonBinaryStreamBase { + public: + AxonBinaryStream() = default; + }; } diff --git a/libraries/networking/message/AxonMessage.cpp b/libraries/messages/AxonMessage.cpp similarity index 62% rename from libraries/networking/message/AxonMessage.cpp rename to libraries/messages/AxonMessage.cpp index df10bab..1b47fef 100644 --- a/libraries/networking/message/AxonMessage.cpp +++ b/libraries/messages/AxonMessage.cpp @@ -20,24 +20,18 @@ Networking::SerializedAxonMessage::SerializedAxonMessage(const char* bits, size6 } } -Networking::SerializedAxonMessage::SerializedAxonMessage(const SerializedAxonMessage &message) : - size(message.size), - offset(message.offset) +Networking::SerializedAxonMessage::SerializedAxonMessage(const SerializedAxonMessage &message) { - if (size > 0) - { - bytes = new char[size]; - memcpy(const_cast(bytes), message.bytes, size); - } + this->copy(message); } -Networking::SerializedAxonMessage::SerializedAxonMessage(SerializedAxonMessage &&message) noexcept : - size(std::exchange(message.size, 0)), - bytes(std::exchange(message.bytes, nullptr)) -{} +Networking::SerializedAxonMessage::SerializedAxonMessage(SerializedAxonMessage &&message) noexcept +{ + this->move(message); +} Networking::SerializedAxonMessage::~SerializedAxonMessage() { - if (bytes && owning) + if (bytes) delete[] bytes; } @@ -50,14 +44,7 @@ Networking::SerializedAxonMessage::operator=(const SerializedAxonMessage &messag if (&message == this) return *this; - size = message.size; - - if (size > 0) - { - bytes = new char[size]; - memcpy(const_cast(bytes), message.bytes, size); - } - + this->copy(message); return * this; } @@ -66,12 +53,24 @@ Networking::SerializedAxonMessage::operator=(SerializedAxonMessage &&message) no if (&message == this) return *this; - std::exchange(size, message.size); - std::exchange(bytes, message.bytes); - + this->move(message); return * this; } +void Networking::SerializedAxonMessage::copy(const Networking::SerializedAxonMessage &message) { + size = message.size; + if (size > 0) + { + bytes = new char[size]; + memcpy(const_cast(bytes), message.bytes, size); + } +} + +void Networking::SerializedAxonMessage::move(Networking::SerializedAxonMessage &message) noexcept { + size = std::exchange(message.size, 0); + bytes = std::exchange(message.bytes, nullptr); +} + #pragma endregion #pragma region AxonMessage @@ -91,18 +90,23 @@ Networking::AxonMessage::AxonMessage(const void* message, size64_t size, uint8_t Networking::AxonMessage::AxonMessage(const SerializedAxonMessage &serialized) { TAG_T tag; - deserialize( serialized.bytes, serialized.size, - &this->message, + reinterpret_cast(&this->message), &this->size, &tag ); - decompressTag(tag, &partID, &flags, &this->uniqueID); } +Networking::AxonMessage::AxonMessage(const AxonMessage &message, const uint8_t additionalFlags) : + flags(additionalFlags), + uniqueID(message.uniqueID) +{ + addFlag(ACKNOWLEDGE); +} + Networking::AxonMessage::AxonMessage(const AxonMessage& message) : size(message.size), partID(message.partID), @@ -116,24 +120,74 @@ Networking::AxonMessage::AxonMessage(const AxonMessage& message) : memcpy(this->message, message.getMessage(), this->size); } -Networking::AxonMessage::AxonMessage(Networking::AxonMessage &message, size64_t size, uint8_t partID, uint8_t flags, uint64_t uniqueID, size64_t offset) : +Networking::AxonMessage::AxonMessage(AxonMessage &&other) noexcept : + size(std::exchange(other.size, 0)), + message(std::exchange(other.message, nullptr)), + partID(std::exchange(other.partID, 0)), + flags(std::exchange(other.flags, UNDEFINED)), + uniqueID(std::exchange(other.uniqueID, 0)) +{} + +Networking::AxonMessage::AxonMessage(const AxonMessage &message, size64_t size, uint8_t partID, uint8_t flags, uint64_t uniqueID, size64_t offset) : size(size), - message(message.message), partID(partID), flags(flags), - uniqueID(uniqueID), - offset(offset) + uniqueID(uniqueID) { - message.owning = false; + if (!message.message || size == 0) + return; + + this->message = new char[size]; + memcpy(this->message, message.message + offset, size); } Networking::AxonMessage::~AxonMessage() { - if (this->message && owning) - delete[] static_cast( message ); + if (this->message) + delete[] message; message = nullptr; } +Networking::AxonMessage & Networking::AxonMessage::operator=(const AxonMessage &other) { + if (&other == this) { + return *this; + } + + if (this->message) { + delete[] this->message; + } + + size = other.size; + partID = other.partID; + flags = other.flags; + uniqueID = other.uniqueID; + + if (other.message && other.size > 0) { + message = new char[size]; + memcpy(message, other.message, size); + } + + return *this; +} + +Networking::AxonMessage & Networking::AxonMessage::operator=(AxonMessage &&other) noexcept { + if (&other == this) { + return *this; + } + + if (this->message) { + delete[] message; + } + + size = std::exchange(other.size, 0); + message = std::exchange(other.message, nullptr); + partID = std::exchange(other.partID, 0); + flags = std::exchange(other.flags, UNDEFINED); + uniqueID = std::exchange(other.uniqueID, 0); + + return *this; +} + Networking::SerializedAxonMessage Networking::AxonMessage::getSerialized() const noexcept { return SerializedAxonMessage(*this); } @@ -161,34 +215,23 @@ Networking::AxonMessage::UniqueAxonMessagePtr Networking::AxonMessage::split(con return std::make_unique(*this, left, partID + 1, flags ^ PARTIAL, uniqueID, size); } -void Networking::AxonMessage::append(const Networking::AxonMessage &other) { - if (!other.getMessage() || other.size == 0 || other.partID == partID + 1) +void Networking::AxonMessage::append(const AxonMessage &other) { + if (!other.getMessage() || other.size == 0 || other.partID != partID + 1) return; char* tempBuffer = new char[other.size + size]; - - // assert((uintptr_t) &size < (uintptr_t) tempBuffer || (uintptr_t) &size > (uintptr_t) tempBuffer + other.size + size); - if (message) { memcpy(tempBuffer, getMessage(), size); - if (owning) - delete[] static_cast(message); + delete[] message; message = nullptr; } - memcpy((static_cast(tempBuffer) + size), other.getMessage(), other.size); + memcpy(tempBuffer + size, other.getMessage(), other.size); message = tempBuffer; size += other.size; partID = other.partID; } -Networking::AxonMessage::AxonMessage(const AxonMessage &message, uint8_t additionalFlags) : - flags(additionalFlags), - uniqueID(message.uniqueID) -{ - addFlag(ACKNOWLEDGE); -} - #pragma endregion /* AxonMessage.cpp */ diff --git a/libraries/networking/message/AxonMessage.hpp b/libraries/messages/AxonMessage.hpp similarity index 53% rename from libraries/networking/message/AxonMessage.hpp rename to libraries/messages/AxonMessage.hpp index d4303b5..4faf190 100644 --- a/libraries/networking/message/AxonMessage.hpp +++ b/libraries/messages/AxonMessage.hpp @@ -1,15 +1,17 @@ /* AxonMessage.hpp */ +// TODO: Code styling, remove all shit pls + #pragma once -#include +#include "serialization/serialization.hpp" #include #include namespace Networking { - /* - * MESSAGE TAG STRUCTURE: + /** + * MESSAGE TAG STRUCTURE: * * 8 bits of optional message data (such as partialID) * 8 bits of TAG_FLAGS @@ -22,18 +24,22 @@ namespace Networking */ enum TAG_FLAGS { - UNDEFINED = 0, /// default - VALIDATE = 1, /// "how copy?" - ACKNOWLEDGE = 1 << 1, /// response package, "copy that!" - PARTIAL = 1 << 2, /// set if message bits are part of a partial delivery - ALL = UNDEFINED | VALIDATE | ACKNOWLEDGE | PARTIAL + UNDEFINED = 0, ///< default + VALIDATE = 1, ///< "how copy?" + ACKNOWLEDGE = 1 << 1, ///< response package, "copy that!" + PARTIAL = 1 << 2, ///< set if message bits are part of a partial delivery + NETOBJ_INI = 1 << 3, + NETOBJ_REPL = 1 << 4, + TO_ALL = 1 << 5, + SERV_STORE = 1 << 6, + + ALL = UNDEFINED | VALIDATE | ACKNOWLEDGE | PARTIAL | NETOBJ_INI | NETOBJ_REPL | TO_ALL | SERV_STORE }; - static_assert(ALL == 0b111, "Flag set is incorrect. Check ALL value or manually edit this assert"); - + static_assert(ALL == 0b1111111, "Flag set is incorrect. Check ALL value or manually edit this assert"); class AxonMessage; - /* + /** * Class, optimized for queued storage, bit split */ class AXON_DECLSPEC SerializedAxonMessage { @@ -46,60 +52,74 @@ namespace Networking ~SerializedAxonMessage(); - GETTER size_t getSize() const { return size; } - GETTER const char* getBits() const { return bytes; } + WGETTER( size_t getSize() ) { return size; } + WGETTER ( const char* getBits() ) { return bytes; } SerializedAxonMessage& operator=(const SerializedAxonMessage&); SerializedAxonMessage& operator=(SerializedAxonMessage&&) noexcept; protected: static TAG_T compressTag(uint8_t, uint8_t, uint16_t); + + void copy(const SerializedAxonMessage&); + void move(SerializedAxonMessage&) noexcept; private: size64_t size = 0; - uint16_t uniqueID = 0; - uintptr_t offset = 0; - bool owning = true; const char* bytes = nullptr; friend class AxonMessage; }; - /* - * AxonMessage is a low-level interface for data storage, serialization and network sharing + /** + * AxonMessage is a low-level interface for data storage, serialization and network sharing. + * + * Can be created in several ways. See constructors */ class AXON_DECLSPEC AxonMessage { public: typedef std::unique_ptr UniqueAxonMessagePtr; AxonMessage() = default; + /** * Create new message from actual data (send mode) + * @param message data pointer + * @param size message size, bytes + * @param partID optional parameter + * @param flags optional parameter, flag set */ - AxonMessage(const void*, size64_t, uint8_t = 0, uint8_t = 0); + AxonMessage(const void* message, size64_t size, uint8_t partID=0, uint8_t flags=0); /** - * Create new message from serialized message structure (preferred) + * Create new message from serialized. + * @param serialized SerializedAxonMessage object, containing compressed data */ - explicit AxonMessage(const SerializedAxonMessage&); + explicit AxonMessage(const SerializedAxonMessage &serialized); /** * Create acknowledge message with empty buffer + * @param message message to create acknowledge message from (will copy uniqueID) + * @param additionalFlags flags to add */ - AxonMessage(const AxonMessage&, uint8_t); + AxonMessage(const AxonMessage &message, uint8_t additionalFlags); - AxonMessage(const AxonMessage&); - AxonMessage(AxonMessage&, size64_t, uint8_t, uint8_t, uint64_t, size64_t); + AxonMessage(const AxonMessage &); + AxonMessage(AxonMessage &&) noexcept; + + AxonMessage(const AxonMessage&, size64_t, uint8_t, uint8_t, uint64_t, size64_t); ~AxonMessage(); + AxonMessage & operator= (const AxonMessage &); + AxonMessage & operator= (AxonMessage &&) noexcept; + WGETTER(SerializedAxonMessage getSerialized()); - WGETTER(void* getMessage()) { return (message) ? static_cast(static_cast(message) + offset) : message; } + WGETTER(void* getMessage()) { return message; } WGETTER(size64_t getSize()) { return size; } WGETTER(uint16_t ID()) { return uniqueID; } - GETTER uint8_t getFlags() const { return flags; } - GETTER uint8_t getPartID() const { return partID; } - GETTER bool hasFlag(const TAG_FLAGS flag) const { return flags & flag; } + WGETTER(uint8_t getFlags()) { return flags; } + WGETTER(uint8_t getPartID()) { return partID; } + WGETTER(bool hasFlag(const TAG_FLAGS flag)) { return flags & flag; } - // Mostly for debugging - GETTER std::bitset<8> getFlagSet() const noexcept { return { flags }; } + WGETTER(std::bitset<8> getFlagSet()) { return flags; } void setPartID(const uint8_t id) { this->partID = id; } @@ -112,20 +132,16 @@ namespace Networking protected: static uint16_t generateUniqueID() { static uint16_t uniqueID = 0; - if (uniqueID == INT16_MAX - 1) uniqueID = 0; - return uniqueID++; } - void decompressTag(TAG_T, uint8_t*, uint8_t*, uint16_t*); + static void decompressTag(TAG_T, uint8_t*, uint8_t*, uint16_t*); private: size64_t size = 0; - void* message = nullptr; - bool owning = true; + char * message = nullptr; uint8_t partID = 0; uint8_t flags = UNDEFINED; - size64_t offset = 0; uint16_t uniqueID = generateUniqueID(); }; } diff --git a/libraries/messages/CMakeLists.txt b/libraries/messages/CMakeLists.txt new file mode 100644 index 0000000..d87e9f2 --- /dev/null +++ b/libraries/messages/CMakeLists.txt @@ -0,0 +1,15 @@ +project ("AxonEngine.libraries.messages") + +file(GLOB_RECURSE SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/*.c" + "${CMAKE_CURRENT_SOURCE_DIR}/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/*.ipp" +) + +add_library(messages STATIC ${SOURCES}) + +target_compile_definitions(messages PUBLIC AXON_LIB) +target_link_libraries(messages events serialization backends) +target_include_directories(messages PUBLIC "${LIBS_DIR}") diff --git a/libraries/networking/AxonLibrary.hpp b/libraries/networking/AxonLibrary.hpp new file mode 100644 index 0000000..31e2bff --- /dev/null +++ b/libraries/networking/AxonLibrary.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "messages/AxonBinaryStream.hpp" +#include "networking/synapse/utils/SynapseEvents.hpp" +#include "networking/synapse/utils/SynapseUtility.hpp" +#include +#include +#include +#include "networking/synapse/utils/SynapseEvents.hpp" +#include + +#include diff --git a/libraries/networking/AxonNetwork.hpp b/libraries/networking/AxonNetwork.hpp index 735ed18..637d059 100644 --- a/libraries/networking/AxonNetwork.hpp +++ b/libraries/networking/AxonNetwork.hpp @@ -1,14 +1,25 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include +#include + +#include namespace Networking { + class AXON_DECLSPEC AxonNetworkManager { + public: + AxonNetworkManager() = default; + + template + void initialize(); + + virtual uint64_t getUniqueIDInNetwork() = 0; + private: + std::unique_ptr synapse; + }; } +#include "AxonNetwork.ipp" + /* AxonNetwork.hpp */ diff --git a/libraries/networking/AxonNetwork.ipp b/libraries/networking/AxonNetwork.ipp new file mode 100644 index 0000000..e69de29 diff --git a/libraries/networking/AxonNetworkObject.cpp b/libraries/networking/AxonNetworkObject.cpp new file mode 100644 index 0000000..e7fe7d1 --- /dev/null +++ b/libraries/networking/AxonNetworkObject.cpp @@ -0,0 +1,46 @@ +#include "AxonNetworkObject.hpp" +#include + +Networking::AxonNetworkObject::AxonNetworkObject(SynapseInterface * synapse) : + synapse(synapse) { + if (synapse) { + synapse->getEventManager().subscribe< + AxonNetworkObject, SynapseMessageReceivedEvent + >(&AxonNetworkObject::onIDResolved, this); + resolveNetworkID(); + } +} + +Networking::AxonNetworkObject::~AxonNetworkObject() { + synapse->getEventManager().unsubscribe< + AxonNetworkObject, SynapseMessageReceivedEvent + >(&AxonNetworkObject::onIDResolved, this); +} + +void Networking::AxonNetworkObject::resolveNetworkID() { + this->clientID = MessageProcessor::RequestUniqueIDProto::generateID(); + + MessageProcessor::RequestUniqueIDProto request = { + clientID, 0 + }; + + AxonMessage msg(&request, sizeof(request), 0, NETOBJ_INI); + synapse->send(msg); +} + +void Networking::AxonNetworkObject::onIDResolved(const SynapseMessageReceivedEvent &event) { + const AxonMessage & message = event.getMessage(); + + if (!message.hasFlag(NETOBJ_REPL)) return; + + MessageProcessor::RequestUniqueIDProto repl = * static_cast < MessageProcessor::RequestUniqueIDProto * >( message.getMessage() ); + if (repl.clientSideID != this->clientID) return; + + this->serverID = repl.serverSideID; + + synapse->getEventManager().unsubscribe< + AxonNetworkObject, SynapseMessageReceivedEvent + >(&AxonNetworkObject::onIDResolved, this); +} + + diff --git a/libraries/networking/AxonNetworkObject.hpp b/libraries/networking/AxonNetworkObject.hpp new file mode 100644 index 0000000..a71cc31 --- /dev/null +++ b/libraries/networking/AxonNetworkObject.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +#include + + +namespace Networking { + class AXON_DECLSPEC AxonNetworkObject { + public: + AxonNetworkObject() = delete; + AxonNetworkObject( SynapseInterface * ); + virtual ~AxonNetworkObject(); + + WGETTER( bool ready() ) { return serverID != 0; } + WGETTER( uint64_t id() ) { return serverID; } + protected: + void resolveNetworkID(); + void onIDResolved(const SynapseMessageReceivedEvent&); + private: + uint64_t serverID = 0; + uint32_t clientID = 0; + protected: + SynapseInterface * synapse = nullptr; + }; +} diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index 6d715e5..65aaad4 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -1,4 +1,5 @@ -project ("AxonEngine.libraries") +project ("AxonEngine.libraries.networking" LANGUAGES C CXX) +SET (CMAKE_CXX_STANDARD 17) file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.c" @@ -8,11 +9,15 @@ file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.ipp" ) -add_library(networking SHARED ${SOURCES}) -set (LIB_HOME ${CMAKE_HOME_DIRECTORY}/libraries) +if (AXON_STATIC) + add_library(networking STATIC ${SOURCES}) + add_compile_definitions(AXON_STATIC) +else() + add_library(networking SHARED ${SOURCES}) +endif() target_compile_definitions(networking PUBLIC AXON_LIB) -target_link_libraries(networking events serialization backends) +target_link_libraries(networking events serialization backends messages) -target_include_directories(networking PUBLIC "${LIB_HOME}") -target_include_directories(networking PUBLIC "${LIB_HOME}/networking") +target_include_directories(networking PUBLIC "${LIBS_DIR}") +target_include_directories(networking PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/libraries/networking/networking-core/BasicSynapse.hpp b/libraries/networking/networking-core/BasicSynapse.hpp deleted file mode 100644 index cd49bb2..0000000 --- a/libraries/networking/networking-core/BasicSynapse.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/** - * BasicSynapse is a class that contains backend calls and basic message handling. It has no advanced functionality - * such as event dispatching, thread managing, etc. - * - * It was created to separate low-level implementations from high level Synapse interface. - * This class still can be used to create custom Synapse. Just override onMessageReceived and update methods. - */ - -#pragma once -#include - - -namespace Networking { - /** - * Basic connection handler - * Specifies one-to-one connection for client and one-to-many for server. - * - * @tparam conn connection mode (TCP|UDP) - * @tparam mode synapse mode (CLIENT|SERVER) - */ - template class AXON_DECLSPEC BasicSynapse - { - public: - /** Default creation is restricted */ - BasicSynapse() = delete; - /** Initializes Synapse in server mode */ - explicit BasicSynapse(uint32_t); - /** Initialize Synapse in client mode */ - explicit BasicSynapse(const ConnectionInfo&); - - virtual ~BasicSynapse(); - - GETTER bool alive() const { return isAlive.load(); }; - virtual void kill() { isAlive.store(false); } - - virtual void start(); - virtual void send(AxonMessage&); - virtual void sendTo(AxonMessage&, const SOCKADDR_IN_T*); - virtual void sendTo(const SerializedAxonMessage&, const SOCKADDR_IN_T*) const; - - // This function should be instanced for each connection type - virtual void listen() {} - virtual void update() {} - virtual void onMessageReceived(const AxonMessage&, SOCKADDR_IN_T*) {}; - - void processIncomingMessage(SerializedAxonMessage, SOCKADDR_IN_T*); - protected: - std::atomic isAlive = false; // TODO: move this from here - ConnectionInfo connectionInfo; - Socket socketInfo; - }; -} - -#include "BasicSynapse.ipp" - -// BasicSynapse.hpp diff --git a/libraries/networking/networking-core/BasicSynapse.ipp b/libraries/networking/networking-core/BasicSynapse.ipp deleted file mode 100644 index 0e67634..0000000 --- a/libraries/networking/networking-core/BasicSynapse.ipp +++ /dev/null @@ -1,195 +0,0 @@ -#pragma once - - -namespace Networking { - template<> - void BasicSynapse::listen(); - template<> - void BasicSynapse::listen(); - template<> - void BasicSynapse::listen(); - template<> - void BasicSynapse::listen(); -} - -#pragma region BASIC_SYNAPSE - -#pragma region CONTRUCTING - -template -Networking::BasicSynapse::BasicSynapse(uint32_t port) -{ - connectionInfo.port = port; - - socketInfo = {}; - - uint8_t result = initialize_server(socketInfo, port); - - if (result != SUCCESS) { - throw AxonNetworkingInternalError(result); - } -} - -template -Networking::BasicSynapse::BasicSynapse(const ConnectionInfo& connection) -{ - connectionInfo.hostname = connection.hostname; - connectionInfo.port = connection.port; - - socketInfo = {}; - - uint8_t result = initialize_client(socketInfo, connection.hostname.c_str(), connection.port); - - if (result != SUCCESS) { - throw AxonNetworkingInternalError(result); - } -} - -template -Networking::BasicSynapse::~BasicSynapse() -{ - isAlive = false; - finalize_udp(socketInfo.socket); -} - -#pragma endregion - -template -void Networking::BasicSynapse::start() -{ - isAlive.store(true); - listen(); -} - -template -void Networking::BasicSynapse::send(AxonMessage& message) -{ - sendTo(message, &socketInfo.conn); -} - -template -void Networking::BasicSynapse::sendTo(AxonMessage& message, const SOCKADDR_IN_T* dest) -{ - sendTo(message.getSerialized(), dest); -} - -template -void Networking::BasicSynapse::sendTo(const SerializedAxonMessage& serialized, const SOCKADDR_IN_T* dest) const -{ - send_message({ socketInfo.socket, *dest }, serialized.getBits(), serialized.getSize()); -} - -template -void Networking::BasicSynapse::processIncomingMessage(SerializedAxonMessage message, SOCKADDR_IN_T *host) { - onMessageReceived(AxonMessage(message), host); -} - -#pragma region SERVER_FUNCTIONS - -template<> -inline void Networking::BasicSynapse::listen() -{ - SOCKADDR_IN_T host = {}; - - fd_set master; - FD_ZERO(&master); - FD_SET(socketInfo.socket, &master); - SOCKET_T maxSocket = socketInfo.socket; - - while (isAlive) - { - timeval tv = { 0, 10000 }; - fd_set reads = master; - if (select(maxSocket + 1, &reads, nullptr, nullptr, &tv) <= 0) { - continue; - } - for (SOCKET_T connectionSock = 1; connectionSock <= maxSocket; connectionSock++) { - if (FD_ISSET(connectionSock, &reads)){ - if (connectionSock == socketInfo.socket) { - sockaddr_storage storage = {}; - SOCKET_T client = accept_incoming(socketInfo.socket, reinterpret_cast(&storage)); - - if (!CHECK_VALID(client)) { - continue; - } - - FD_SET(client, &master); - if (client > maxSocket) { - maxSocket = client; - } - } - else { - char buffer[SYNAPSE_MESSAGE_SIZE_MAX] = {}; - const int32_t size = recv_message(socketInfo, buffer, 256); - - if (size < 0) { - FD_CLR(connectionSock, &master); - CLOSESOCKET(connectionSock); - continue; - } - - processIncomingMessage(SerializedAxonMessage(buffer, size), &socketInfo.conn); - } - } - } - update(); - } -} - -template<> -inline void Networking::BasicSynapse::listen() -{ - SOCKADDR_IN_T host = {}; - - while (isAlive) { - char buffer[SYNAPSE_MESSAGE_SIZE_MAX] = {}; - int32_t size = recv_message(socketInfo, buffer, SYNAPSE_MESSAGE_SIZE_MAX); - if (size > 0) { - processIncomingMessage(SerializedAxonMessage(buffer, size), &socketInfo.conn); - } - - update(); - } -} - -#pragma endregion - -#pragma region CLIENT_FUNCTIONS - -template<> -inline void Networking::BasicSynapse::listen() { - // Todo: async support - SOCKADDR_IN_T host = socketInfo.conn; - SOCKET_T client = socketInfo.socket; - - while (isAlive) - { - char buffer[SYNAPSE_MESSAGE_SIZE_MAX] = {}; - const int32_t size = recv_tcp_message(reinterpret_cast(buffer), 256, client); - if (size > 0) - { - processIncomingMessage(SerializedAxonMessage(buffer, size), &host); - } - - update(); - } -} - -template<> -inline void Networking::BasicSynapse::listen() { - SOCKADDR_IN_T host = {}; - - while (isAlive) { - char buffer[SYNAPSE_MESSAGE_SIZE_MAX] = {}; - int32_t size = recv_udp_message(reinterpret_cast (buffer), SYNAPSE_MESSAGE_SIZE_MAX, socketInfo.socket, - &host); - if (size > 0) { - processIncomingMessage(SerializedAxonMessage(buffer, size), &host); - } - - update(); - } -} - -#pragma endregion -#pragma endregion diff --git a/libraries/networking/networking-core/Synapse.hpp b/libraries/networking/networking-core/Synapse.hpp deleted file mode 100644 index 021a495..0000000 --- a/libraries/networking/networking-core/Synapse.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once -#include -#include - -#include - -namespace Networking -{ - /** - * Advanced connection handler with event system - * - * @tparam conn connection mode (TCP|UDP) - * @tparam mode synapse mode (CLIENT|SERVER) - */ - template - class AXON_DECLSPEC Synapse : public BasicSynapse { - public: -#pragma region CONSTRUCTING - /** Initializes Synapse in server mode */ - explicit Synapse(uint32_t port) : BasicSynapse(port) {} - /** Initialize Synapse in client mode */ - explicit Synapse(const ConnectionInfo &info) : BasicSynapse(info) {} - - ~Synapse() override = default; -#pragma endregion - -#pragma region INTERFACE - - void update() override; - void onMessageReceived(const AxonMessage&, SOCKADDR_IN_T*) override; - - EventSystem::AxonEventManager& getEventManager() { return events; } - void sendTo(AxonMessage&, const SOCKADDR_IN_T*) override; - void sendPooled(const AxonMessage&, const SOCKADDR_IN_T* = nullptr) const; -#pragma endregion - protected: - EventSystem::AxonEventManager events; - - std::vector pendingValidation; - std::unique_ptr pool = std::make_unique(); - std::unique_ptr mmap = std::make_unique(); - }; - - /** - * Synapse with listen() function in separated thread. - * - * @tparam conn connection mode (TCP|UDP) - * @tparam mode synapse mode (CLIENT|SERVER) - */ - template - class AXON_DECLSPEC AsyncSynapse final : public Synapse - { - public: - /** Initializes Synapse in server mode */ - explicit AsyncSynapse(uint32_t port) : Synapse(port) {} - - /** Initialize Synapse in client mode */ - explicit AsyncSynapse(const ConnectionInfo& info) : Synapse(info) {} - - ~AsyncSynapse() override; - - void start() override; - void kill() override; - private: - std::thread proc; - }; -} - -#include "Synapse.ipp" - -/* Synapse.hpp */ diff --git a/libraries/networking/networking-core/Synapse.ipp b/libraries/networking/networking-core/Synapse.ipp deleted file mode 100644 index f9a26d3..0000000 --- a/libraries/networking/networking-core/Synapse.ipp +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -#include - - -#pragma region SYNAPSE - -template -void Networking::Synapse::update() { - MessagePoolNodePtr pNode = pool->pop(); - if (!pNode.get()) - return; - - this->sendTo(pNode->message, &pNode->destination); -} - -template -void Networking::Synapse::sendTo(AxonMessage &message, const SOCKADDR_IN_T *dest) { - Networking::AxonMessage::UniqueAxonMessagePtr ptr = message.split(SYNAPSE_PAYLOAD_SIZE_MAX); - if (ptr) - { - sendPooled(*ptr.get(), dest); - } - - if (message.hasFlag(VALIDATE)) - { - pendingValidation.push_back(message.ID()); - } - - BasicSynapse::sendTo(message, dest); -} - -template -void Networking::Synapse::sendPooled(const AxonMessage& message, const SOCKADDR_IN_T* dest) const { - if (!dest) - dest = &this->socketInfo.conn; - pool->push( { message, *dest } ); -} - -template -void Networking::Synapse::onMessageReceived(const AxonMessage& message, SOCKADDR_IN_T* from) -{ - if (message.hasFlag(VALIDATE)) - { - sendPooled(AxonMessage(message, 0), from); - } - if (message.hasFlag(ACKNOWLEDGE) && !message.hasFlag(PARTIAL)) - { - pendingValidation.erase(std::find(pendingValidation.begin(), pendingValidation.end(), message.ID())); - } - if (mmap->contains(message.ID()) || message.hasFlag(PARTIAL)) - { - mmap->append(message); - - if (!message.hasFlag(PARTIAL)) - { - // Fini - std::shared_ptr res = mmap->collapse(message.ID()); - if (!res) - return; - - SynapseMessageReceivedEvent event_ = SynapseMessageReceivedEvent(*res, from); - events.invoke(&event_); - } - return; - } - - SynapseMessageReceivedEvent event_ = SynapseMessageReceivedEvent(message, from); - events.invoke(&event_); -} - -#pragma endregion - -#pragma region ASYNC_SYNAPS - -template -void Networking::AsyncSynapse::kill() -{ - if (!this->isAlive) - return; - - this->isAlive = false; - proc.join(); -} - -template -Networking::AsyncSynapse::~AsyncSynapse() -{ - kill(); -} - -template -void Networking::AsyncSynapse::start() -{ - this->isAlive = true; - proc = std::thread(&AsyncSynapse::listen, this); -} - -#pragma endregion diff --git a/libraries/networking/synapse/BasicSynapse.hpp b/libraries/networking/synapse/BasicSynapse.hpp new file mode 100644 index 0000000..8f94d7e --- /dev/null +++ b/libraries/networking/synapse/BasicSynapse.hpp @@ -0,0 +1,61 @@ +/** + * BasicSynapse is a class that contains backend calls and basic message handling. It has no advanced functionality + * such as event dispatching, thread managing, etc. + * + * It was created to separate low-level implementations from high level Synapse interface. + * This class still can be used to create custom Synapse. Just override onMessageReceived and update methods. + */ + +#pragma once +#include +#include "networking/synapse/utils/SynapseUtility.hpp" +#include "messages/AxonMessage.hpp" +#include "networking/synapse/utils/SynapseEvents.hpp" +#include "SynapseInterface.hpp" +#include + + +namespace Networking { + /** + * Basic connection handler + * Specifies one-to-one connection for client and one-to-many for server. + * + * @tparam NetworkController derived from AxonNetworkControllerBase class + */ + template + class AXON_DECLSPEC BasicSynapse : public SynapseInterface + { + static_assert(std::is_base_of()); + static_assert(!std::is_abstract()); + public: + /** Default creation is restricted */ + BasicSynapse() = delete; + /** Initializes Synapse in server mode */ + explicit BasicSynapse(uint32_t); + /** Initialize Synapse in client mode */ + explicit BasicSynapse(const ConnectionInfo&); + + explicit BasicSynapse(const BasicSynapse&) = delete; + BasicSynapse(BasicSynapse&&) noexcept = delete; + BasicSynapse& operator=(const BasicSynapse&) = delete; + BasicSynapse& operator=(BasicSynapse&&) noexcept = delete; + + ~BasicSynapse() override; + + void kill() override { this->networkController->kill(); } + void start() override; + void send(AxonMessage&) override; + void sendTo(AxonMessage&, const Socket&) override; + + void listen() override; + void update() override {} + void onMessageReceived(const AxonMessage&, const Socket&) override {}; + + void processIncomingMessage(const SerializedAxonMessage&, const Socket&) override; + protected: + std::unique_ptr networkController; + }; +} + +#include "BasicSynapse.ipp" +// BasicSynapse.hpp diff --git a/libraries/networking/synapse/BasicSynapse.ipp b/libraries/networking/synapse/BasicSynapse.ipp new file mode 100644 index 0000000..d23ad63 --- /dev/null +++ b/libraries/networking/synapse/BasicSynapse.ipp @@ -0,0 +1,54 @@ +#pragma once +#include "networking/synapse/utils/SynapseUtility.hpp" + +#pragma region BASIC_SYNAPSE +#pragma region CONTRUCTING + +template +Networking::BasicSynapse::BasicSynapse(uint32_t port) : + networkController(std::make_unique(this, port)) +{} + +template +Networking::BasicSynapse::BasicSynapse(const ConnectionInfo& connection) : + networkController(std::make_unique(this, connection)) +{} + +template +Networking::BasicSynapse::~BasicSynapse() +{ + networkController->kill(); +} + +#pragma endregion + +template +void Networking::BasicSynapse::start() +{ + networkController->start(); + listen(); +} + +template +void Networking::BasicSynapse::send(AxonMessage& message) +{ + networkController->send(message); +} + +template +void Networking::BasicSynapse::sendTo(AxonMessage& message, const Socket& dest) +{ + networkController->sendTo(message, dest); +} + +template +void Networking::BasicSynapse::processIncomingMessage(const SerializedAxonMessage &message, const Socket& from) { + onMessageReceived(AxonMessage(message), from); +} + +template +void Networking::BasicSynapse::listen() { + networkController->listen(); +} + +#pragma endregion \ No newline at end of file diff --git a/libraries/networking/synapse/Synapse.hpp b/libraries/networking/synapse/Synapse.hpp new file mode 100644 index 0000000..a526ae3 --- /dev/null +++ b/libraries/networking/synapse/Synapse.hpp @@ -0,0 +1,71 @@ +#pragma once +#include "BasicSynapse.hpp" +#include "networking/utility/MessagePool.hpp" + +#include +#include "AxonUtility.h" +#include "messages/AxonMessage.hpp" + +#include "networking/synapse/utils/SynapseUtility.hpp" +#include + +namespace Networking +{ + /** + * Advanced connection handler with event system + * + * @tparam controller derived from AxonNetworkControllerBase class + */ + template + class AXON_DECLSPEC Synapse : public BasicSynapse { + public: +#pragma region CONSTRUCTING + /** Initializes Synapse in server mode */ + explicit Synapse(uint32_t port) : BasicSynapse(port) {} + /** Initialize Synapse in client mode */ + Synapse (const char *hostname, const uint32_t port) : BasicSynapse({hostname, port}) {} + + ~Synapse() override = default; +#pragma endregion + +#pragma region INTERFACE + void update() override; + void onMessageReceived(const AxonMessage&, const Socket&) override; + + void sendTo(AxonMessage&, const Socket&) override; + void sendPooled(const AxonMessage&, const Socket &) const; + + GETTER bool alive() { return this->networkController->isAlive(); } +#pragma endregion + protected: + ADD_MODULE(MessageProcessor, msgproc, this); + friend MessageProcessor; + }; + + /** + * Synapse with listen() function in separated thread. + * + * @tparam controller derived from AxonNetworkControllerBase class + */ + template + class AXON_DECLSPEC AsyncSynapse final : public Synapse + { + public: + /** Initializes Synapse in server mode */ + explicit AsyncSynapse(uint32_t port) : Synapse(port) {} + + /** Initialize Synapse in client mode */ + explicit AsyncSynapse (const char *hostname, const uint32_t port) : Synapse(hostname, port) {} + + ~AsyncSynapse() override; + + void start() override; + void kill() override; + private: + std::thread proc; + }; +} + +#include "Synapse.ipp" + +/* Synapse.hpp */ diff --git a/libraries/networking/synapse/Synapse.ipp b/libraries/networking/synapse/Synapse.ipp new file mode 100644 index 0000000..8dd9250 --- /dev/null +++ b/libraries/networking/synapse/Synapse.ipp @@ -0,0 +1,70 @@ +#pragma once +#include + + +#pragma region SYNAPSE + +template +void Networking::Synapse::update() { + MessagePoolNodePtr pNode = this->msgproc->processPool(); + if (!pNode.get()) + return; + + this->sendTo(pNode->message, pNode->destination); +} + +template +void Networking::Synapse::sendTo(AxonMessage &message, const Socket &dest) { + const AxonMessage::UniqueAxonMessagePtr ptr = message.split(SYNAPSE_PAYLOAD_SIZE_MAX); + if (ptr) + { + sendPooled(*ptr.get(), dest); + } + + if (message.hasFlag(VALIDATE)) + { + this->msgproc->awaitValidation(message.ID()); + } + + BasicSynapse::sendTo(message, dest); +} + +template +void Networking::Synapse::sendPooled(const AxonMessage& message, const Socket &dest) const { + this->msgproc->poolMessage( { message, dest } ); +} + +template +void Networking::Synapse::onMessageReceived(const AxonMessage& message, const Socket &from) +{ + this->msgproc->process(message, from); +} + +#pragma endregion + +#pragma region ASYNC_SYNAPSE + +template +void Networking::AsyncSynapse::kill() +{ + if (!this->networkController->isAlive()) + return; + + this->networkController->kill(); + proc.join(); +} + +template +Networking::AsyncSynapse::~AsyncSynapse() +{ + kill(); +} + +template +void Networking::AsyncSynapse::start() +{ + this->networkController->start(); + proc = std::thread(&AsyncSynapse::listen, this); +} + +#pragma endregion diff --git a/libraries/networking/synapse/SynapseInterface.hpp b/libraries/networking/synapse/SynapseInterface.hpp new file mode 100644 index 0000000..843cd95 --- /dev/null +++ b/libraries/networking/synapse/SynapseInterface.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include "networking/synapse/utils/SynapseUtility.hpp" +#include "networking/synapse/utils/SynapseEvents.hpp" + + +namespace Networking { + class AXON_DECLSPEC SynapseInterface { + public: + SynapseInterface() = default; + virtual ~SynapseInterface() = default; + + virtual void kill() = 0; + + virtual void start() = 0; + virtual void send(AxonMessage &) = 0; + virtual void sendTo(AxonMessage&, const Socket&) = 0; + + EventSystem::AxonEventManager& getEventManager() { return events; } + virtual void listen() = 0; + virtual void update() = 0; + virtual void onMessageReceived(const AxonMessage&, const Socket&) = 0; + virtual void processIncomingMessage(const SerializedAxonMessage&, const Socket&) = 0; + protected: + EventSystem::AxonEventManager events; + }; +} \ No newline at end of file diff --git a/libraries/networking/synapse/netcontroller/AxonNetworkController.hpp b/libraries/networking/synapse/netcontroller/AxonNetworkController.hpp new file mode 100644 index 0000000..64b2332 --- /dev/null +++ b/libraries/networking/synapse/netcontroller/AxonNetworkController.hpp @@ -0,0 +1,59 @@ +#pragma once +#include + +#include + +#include +#include +#include + +namespace Networking { + typedef Socket NetworkNodeInfo; + + class AXON_DECLSPEC AxonNetworkControllerBase { + public: + AxonNetworkControllerBase() = delete; + + explicit AxonNetworkControllerBase(SynapseInterface *); + + virtual ~AxonNetworkControllerBase() { kill(); }; + + GETTER bool isAlive() { return alive.load(); } + + virtual void start(); + void kill(); + + virtual void listen() = 0; + virtual void send(AxonMessage&) = 0; + virtual void sendTo(AxonMessage&, const NetworkNodeInfo&) = 0; + protected: + virtual void sendSerializedTo(const SerializedAxonMessage&, const NetworkNodeInfo&) = 0; + private: + std::atomic alive = false; + protected: + SynapseInterface *owningSynapse; + }; + + template + class AXON_DECLSPEC BerkeleyAxonNetworkController : public AxonNetworkControllerBase { + public: + BerkeleyAxonNetworkController() = delete; + + explicit BerkeleyAxonNetworkController(SynapseInterface * , uint32_t); + explicit BerkeleyAxonNetworkController(SynapseInterface * , ConnectionInfo); + + ~BerkeleyAxonNetworkController() override; + + void listen() override {}; + void send(AxonMessage&) override; + void sendTo(AxonMessage&, const NetworkNodeInfo&) override; + protected: + void sendSerializedTo(const SerializedAxonMessage&, const NetworkNodeInfo&) override; + private: + ConnectionInfo meta; + NetworkNodeInfo connection; // Client-only feature, server will store nothing + }; +} + + +#include "AxonNetworkController.ipp" diff --git a/libraries/networking/synapse/netcontroller/AxonNetworkController.ipp b/libraries/networking/synapse/netcontroller/AxonNetworkController.ipp new file mode 100644 index 0000000..18c3d5f --- /dev/null +++ b/libraries/networking/synapse/netcontroller/AxonNetworkController.ipp @@ -0,0 +1,171 @@ +#pragma once +#include + + +namespace Networking { + inline void AxonNetworkControllerBase::start() { + this->alive.store(true); + } + + inline void AxonNetworkControllerBase::kill() { + this->alive.store(false); + } + + inline AxonNetworkControllerBase::AxonNetworkControllerBase(SynapseInterface *owner) : + owningSynapse(owner) {} + + template + BerkeleyAxonNetworkController::BerkeleyAxonNetworkController(SynapseInterface *owner, uint32_t port) : + meta({ "", port }), + connection(), + AxonNetworkControllerBase(owner) + { + static_assert(mode == SynapseMode::SERVER); + uint8_t ret = initialize_server ( connection, port ); + if ( ret != SUCCESS ) { + throw AxonNetworkingInternalError(ret); + } + } + + template + BerkeleyAxonNetworkController::BerkeleyAxonNetworkController(SynapseInterface *owner, const ConnectionInfo info) : + meta(info), + connection(), + AxonNetworkControllerBase(owner) + { + static_assert(mode == SynapseMode::CLIENT); + const uint8_t ret = initialize_client ( connection, info.hostname.c_str(), info.port ); + if ( ret != SUCCESS ) { + throw AxonNetworkingInternalError(ret); + } + } + + template + BerkeleyAxonNetworkController::~BerkeleyAxonNetworkController() { + this->kill(); + finalize(connection); + } + + template + void BerkeleyAxonNetworkController::send(AxonMessage &message) { + this->sendTo(message, this->connection); + } + + template + void BerkeleyAxonNetworkController::sendTo(AxonMessage &message, const NetworkNodeInfo &dest) { + this->sendSerializedTo(message.getSerialized(), dest); + } + + template + void BerkeleyAxonNetworkController::sendSerializedTo(const SerializedAxonMessage &serialized, + const NetworkNodeInfo &dest) { + send_message(dest, serialized.getBits(), serialized.getSize()); + } + + +#pragma region SERVER_FUNCTIONS + + template<> + inline void BerkeleyAxonNetworkController::listen() + { + fd_set master; + FD_ZERO(&master); + FD_SET(connection.socket, &master); + SOCKET_T maxSocket = connection.socket; + + while (this->isAlive()) + { + timeval tv = { 0, 10000 }; + fd_set reads = master; + if (select(maxSocket + 1, &reads, nullptr, nullptr, &tv) <= 0) { + continue; + } + for (SOCKET_T connectionSock = 1; connectionSock <= maxSocket; connectionSock++) { + if (FD_ISSET(connectionSock, &reads)){ + if (connectionSock == connection.socket) { + sockaddr_storage storage = {}; + SOCKET_T client = accept_incoming(connection.socket, reinterpret_cast(&storage)); + + if (!CHECK_VALID(client)) { + continue; + } + + FD_SET(client, &master); + if (client > maxSocket) { + maxSocket = client; + } + } + else { + char buffer[SYNAPSE_MESSAGE_SIZE_MAX] = {}; + Socket socket = { connectionSock, {} }; + const int32_t size = recv_message(socket, buffer, 256); + if (size > 0) + { + owningSynapse->processIncomingMessage(SerializedAxonMessage(buffer, size), socket); + } + else if (size < 0) + { + FD_CLR(connectionSock, &master); + CLOSESOCKET(connectionSock); + continue; + } + } + } + } + owningSynapse->update(); + } + } + + template<> + inline void BerkeleyAxonNetworkController::listen() + { + while (this->isAlive()) { + char buffer[SYNAPSE_MESSAGE_SIZE_MAX] = {}; + int32_t size = recv_message(connection, buffer, SYNAPSE_MESSAGE_SIZE_MAX); + if (size > 0) { + owningSynapse->processIncomingMessage(SerializedAxonMessage(buffer, size), connection); + } + + owningSynapse->update(); + } + } + +#pragma endregion + +#pragma region CLIENT_FUNCTIONS + + template<> + inline void BerkeleyAxonNetworkController::listen() { + SOCKET_T client = connection.socket; + + while (this->isAlive()) + { + char buffer[SYNAPSE_MESSAGE_SIZE_MAX] = {}; + const int32_t size = recv_tcp_message(reinterpret_cast(buffer), 256, client); + if (size > 0) + { + owningSynapse->processIncomingMessage(SerializedAxonMessage(buffer, size), connection); + } + + owningSynapse->update(); + } + } + + template<> + inline void BerkeleyAxonNetworkController::listen() { + SOCKADDR_IN_T host = {}; + + while (this->isAlive()) { + char buffer[SYNAPSE_MESSAGE_SIZE_MAX] = {}; + const int32_t size = recv_udp_message(buffer, SYNAPSE_MESSAGE_SIZE_MAX, connection.socket, + &host); + if (size > 0) { + owningSynapse->processIncomingMessage(SerializedAxonMessage(buffer, size), connection); + } + + owningSynapse->update(); + } + } + +#pragma endregion +} \ No newline at end of file diff --git a/libraries/networking/networking-core/SynapseEvents.cpp b/libraries/networking/synapse/utils/SynapseEvents.cpp similarity index 100% rename from libraries/networking/networking-core/SynapseEvents.cpp rename to libraries/networking/synapse/utils/SynapseEvents.cpp diff --git a/libraries/networking/networking-core/SynapseEvents.hpp b/libraries/networking/synapse/utils/SynapseEvents.hpp similarity index 56% rename from libraries/networking/networking-core/SynapseEvents.hpp rename to libraries/networking/synapse/utils/SynapseEvents.hpp index f942a33..f9d0b08 100644 --- a/libraries/networking/networking-core/SynapseEvents.hpp +++ b/libraries/networking/synapse/utils/SynapseEvents.hpp @@ -1,7 +1,9 @@ #pragma once -#include -#include -#include +#include "events/AxonEvent.hpp" +#include "messages/AxonMessage.hpp" +#include "AxonUtility.h" + +struct Socket; namespace Networking { /** @@ -10,15 +12,14 @@ namespace Networking { class AXON_DECLSPEC SynapseMessageReceivedEvent final : public EventSystem::AxonEvent { public: - SynapseMessageReceivedEvent(const AxonMessage& message, SOCKADDR_IN_T* from) : AxonEvent(), message(message) - { - this->from = from; - } + SynapseMessageReceivedEvent(const AxonMessage& message, const Socket &from) : + AxonEvent(), message(message), from(from) + {} GETTER const AxonMessage& getMessage() const { return message; } - GETTER SOCKADDR_IN_T* getFrom() const { return from; } + GETTER const Socket& getFrom() const { return from; } private: - const AxonMessage& message; - SOCKADDR_IN_T* from; + const AxonMessage& message; + const Socket& from; }; } diff --git a/libraries/networking/networking-core/SynapseUtility.cpp b/libraries/networking/synapse/utils/SynapseUtility.cpp similarity index 90% rename from libraries/networking/networking-core/SynapseUtility.cpp rename to libraries/networking/synapse/utils/SynapseUtility.cpp index 6fbc48b..1dff993 100644 --- a/libraries/networking/networking-core/SynapseUtility.cpp +++ b/libraries/networking/synapse/utils/SynapseUtility.cpp @@ -1,5 +1,5 @@ #include "SynapseUtility.hpp" -#include +#include "common_macro.h" namespace Networking { const char * AxonNetworkingInternalError::what() const noexcept { @@ -10,7 +10,7 @@ namespace Networking { #if defined(WINDOWS_PLATFORM) return "C WSAStartup function failed."; #else - return "That shouln't be here. Check your code"; + return "That shouldn't be here. Check your code"; #endif case ERR_INVALID: return "Got invalid socket value"; diff --git a/libraries/networking/networking-core/SynapseUtility.hpp b/libraries/networking/synapse/utils/SynapseUtility.hpp similarity index 90% rename from libraries/networking/networking-core/SynapseUtility.hpp rename to libraries/networking/synapse/utils/SynapseUtility.hpp index a01455f..715bff7 100644 --- a/libraries/networking/networking-core/SynapseUtility.hpp +++ b/libraries/networking/synapse/utils/SynapseUtility.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include "backends/backend.hpp" +#include "AxonUtility.h" namespace Networking { /** @@ -43,7 +43,7 @@ namespace Networking { const uint8_t err; public: explicit AxonNetworkingInternalError(const uint8_t err = 0) : err(err) {} - GETTER constexpr uint8_t code() const { return err; } + GETTER constexpr uint8_t code() const { return err; } GETTER const char* what() const noexcept override; }; } diff --git a/libraries/networking/utility/AxonNetworkPointer.hpp b/libraries/networking/utility/AxonNetworkPointer.hpp new file mode 100644 index 0000000..9662d0f --- /dev/null +++ b/libraries/networking/utility/AxonNetworkPointer.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace Networking { + template + class AXON_DECLSPEC AxonNetworkPtr : public AxonNetworkObject { + static_assert(!std::is_abstract::value); + static_assert( + std::is_trivially_copyable::value, + "Type must be trivially copyable to be compatible with Axon Network Pointer"); + public: + AxonNetworkPtr() = delete; + AxonNetworkPtr(SynapseInterface *, T *); + ~AxonNetworkPtr() override = default; + + GETTER T *get() const { return ptr; } + void set(T); + T &operator*() const; + T *operator->() const; + protected: + virtual void onValueChanged(); + virtual void dispatchValueChangeEvent(); + AxonMessage toMessage() const; + private: + T *ptr = nullptr; + uint8_t flags = 0; + bool shouldUpdateSelf = false; + EventSystem::AxonEventManager events; + }; +} + +#include "AxonNetworkPointer.ipp" diff --git a/libraries/networking/utility/AxonNetworkPointer.ipp b/libraries/networking/utility/AxonNetworkPointer.ipp new file mode 100644 index 0000000..6de6329 --- /dev/null +++ b/libraries/networking/utility/AxonNetworkPointer.ipp @@ -0,0 +1,50 @@ +#pragma once +#include +#include "MessageProcessor.hpp" + +namespace Networking { + template + AxonNetworkPtr::AxonNetworkPtr(SynapseInterface * synapse, T * ptrTo) : + AxonNetworkObject(synapse), + ptr(ptrTo) + {} + + template + void AxonNetworkPtr::set(T val) { + *ptr = val; + + dispatchValueChangeEvent(); + } + + template + T &AxonNetworkPtr::operator*() const { + return *ptr; + } + + template + T *AxonNetworkPtr::operator->() const { + return ptr; + } + + template + void AxonNetworkPtr::onValueChanged() { + + } + + template + void AxonNetworkPtr::dispatchValueChangeEvent() { + if (!ready()) { + shouldUpdateSelf = true; + return; + } + AxonMessage _tmp = this->toMessage(); + synapse->send(_tmp); + } + + template + AxonMessage AxonNetworkPtr::toMessage() const { + return AxonMessage( + ptr, sizeof( * ptr ), 0, flags + ); + } +} // Networking \ No newline at end of file diff --git a/libraries/networking/utility/MessagePool.hpp b/libraries/networking/utility/MessagePool.hpp index 366cfa7..1a4d21f 100644 --- a/libraries/networking/utility/MessagePool.hpp +++ b/libraries/networking/utility/MessagePool.hpp @@ -1,5 +1,6 @@ #pragma once -#include +#include "messages/AxonMessage.hpp" +#include #include #include //#include @@ -13,7 +14,7 @@ namespace Networking { struct AXON_DECLSPEC MessagePoolNode { AxonMessage message; - SOCKADDR_IN_T destination; + Socket destination; }; typedef std::shared_ptr MessagePoolNodePtr; @@ -36,7 +37,7 @@ namespace Networking { class MessageMapComparator { public: - bool operator() ( const AxonMessagePtr& l, const AxonMessagePtr& r ) { return l->getPartID() < r->getPartID(); } + bool operator() ( const AxonMessagePtr& l, const AxonMessagePtr& r ) const { return l->getPartID() < r->getPartID(); } }; typedef std::priority_queue, MessageMapComparator> MessageMapNode; public: @@ -48,7 +49,7 @@ namespace Networking { auto it = messagePool.find(message.ID()); if (it != messagePool.end()) { - if (message.getPartID() == it->second.top()->getPartID() + 1) + if (!it->second.empty() && message.getPartID() == it->second.top()->getPartID() + 1) { it->second.top()->append(message); return it->second.top(); @@ -80,7 +81,6 @@ namespace Networking { bool contains(uint16_t id) { return messagePool.find(id) != messagePool.end(); } - protected: private: std::map messagePool; }; diff --git a/libraries/networking/utility/MessageProcessor.cpp b/libraries/networking/utility/MessageProcessor.cpp new file mode 100644 index 0000000..6a6512a --- /dev/null +++ b/libraries/networking/utility/MessageProcessor.cpp @@ -0,0 +1,55 @@ +#include "MessageProcessor.hpp" +#include + +#include + +namespace Networking { + MessageProcessor::MessageProcessor(SynapseInterface *owner) : owner(owner) { + } + + void MessageProcessor::process(const AxonMessage &message, const Socket &from) { + if (message.hasFlag(VALIDATE)) + { + AxonMessage reply(message, 0); + owner->sendTo(reply, from); + } + if (message.hasFlag(NETOBJ_INI)) { + RequestUniqueIDProto proto = * static_cast( message.getMessage() ); + + assert (!message.hasFlag(VALIDATE)); + assert (message.getSize() == sizeof(RequestUniqueIDProto)); + + proto.serverSideID = getObjectID(); + + AxonMessage reply(&proto, sizeof(proto), 0, NETOBJ_REPL); + + owner->sendTo(reply, from); + } + if (message.hasFlag(ACKNOWLEDGE) && !message.hasFlag(PARTIAL)) + { + if (const auto it = std::find( + pendingValidation.begin(), + pendingValidation.end(), message.ID()); it != pendingValidation.end()) + pendingValidation.erase(it); + } + if (mmap->contains(message.ID()) || message.hasFlag(PARTIAL)) + { + mmap->append(message); + + if (!message.hasFlag(PARTIAL)) + { + const std::shared_ptr res = mmap->collapse(message.ID()); + if (!res) + return; + + SynapseMessageReceivedEvent event_(*res, from); + this->owner->getEventManager().invoke(&event_); + } + + return; + } + + SynapseMessageReceivedEvent event_(message, from); + this->owner->getEventManager().invoke(&event_); + } +} // Networking \ No newline at end of file diff --git a/libraries/networking/utility/MessageProcessor.hpp b/libraries/networking/utility/MessageProcessor.hpp new file mode 100644 index 0000000..dfcfbb5 --- /dev/null +++ b/libraries/networking/utility/MessageProcessor.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +namespace Networking { + +#define ADD_MODULE(TYPENAME, NAME, ...) \ + std::unique_ptr NAME = std::make_unique(__VA_ARGS__) + +class AXON_DECLSPEC MessageProcessor { +public: + struct RequestUniqueIDProto { + uint32_t clientSideID = 0; + uint64_t serverSideID = 0; + + static uint32_t generateID() { + static uint32_t uniqueID = 0; + return ++uniqueID; + } + }; + + MessageProcessor() = delete; + + explicit MessageProcessor(SynapseInterface *); + + MessageProcessor & operator= (const MessageProcessor &) = delete; + MessageProcessor & operator= (MessageProcessor&&) = delete; + + void process(const AxonMessage &, const Socket &); + void awaitValidation(uint64_t id) { pendingValidation.push_back(id); } + + GETTER MessagePoolNodePtr processPool() const { return pool->pop(); } + void poolMessage(const MessagePoolNode &node) const { pool->push(node); } +protected: + GETTER static uint64_t getObjectID() { + static uint64_t lastID = 0; + return ++lastID; + } +private: + SynapseInterface *owner; + + std::vector pendingValidation; + ADD_MODULE(MessageMapBase, mmap); + ADD_MODULE(MessagePoolBase, pool); +}; + +} // Networking diff --git a/tests/basic_checks/CMakeLists.txt b/tests/basic_checks/CMakeLists.txt deleted file mode 100644 index abf4287..0000000 --- a/tests/basic_checks/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -project ("AxonEngine.tests.basic_checks") -include ("CTest") - -# TEST FILES -enable_testing() - -list_all_tests("test_*.cxx" networking) - -include_directories("../../libraries") diff --git a/tests/basic_checks/test_event_system.cxx b/tests/basic_checks/test_event_system.cxx deleted file mode 100644 index b7ce10f..0000000 --- a/tests/basic_checks/test_event_system.cxx +++ /dev/null @@ -1,52 +0,0 @@ -#include - -#include -#include - - -class TestEvent : public EventSystem::AxonEvent -{ -private: - uint8_t data; - bool isChecked = false; -public: - TestEvent() = delete; - - explicit TestEvent(uint8_t data = 0) - { - this->data = data; - } - - inline uint8_t get_data() const { return data; } -}; - - -void callback(const TestEvent& event) -{ - uint8_t data = event.get_data(); - assert(data == 3); -} - -void callback2(const TestEvent& event) -{ - uint8_t data = event.get_data(); - assert(data == 3); -} - -int main() -{ - /* Local events */ - TestEvent event(3); - - EventSystem::AxonEventManager manager; - - manager.subscribe(callback); - manager.invoke(&event); - - /* Global event manager */ - EventSystem::GlobalEventManager& g_manager = EventSystem::GlobalEventManager::Instance(); - g_manager.subscribe(callback); - g_manager.subscribe(callback2); - - g_manager.invoke(&event); -} diff --git a/tests/gtest/CMakeLists.txt b/tests/gtest/CMakeLists.txt index 90f6a04..a5ae41c 100644 --- a/tests/gtest/CMakeLists.txt +++ b/tests/gtest/CMakeLists.txt @@ -4,6 +4,7 @@ project ("AxonEngine.tests.gtest") find_package(GTest REQUIRED) enable_testing() -set (LIST_LIBRARIES networking GTest::gtest GTest::gtest_main) +set (LIST_LIBRARIES GTest::gtest GTest::gtest_main networking) list_all_tests("test_*.cpp" ${LIST_LIBRARIES}) include_directories("../../libraries") +include_directories("../utils/testutils") \ No newline at end of file diff --git a/tests/gtest/FakeNetworkController/FakeNetworkController.hpp b/tests/gtest/FakeNetworkController/FakeNetworkController.hpp new file mode 100644 index 0000000..0eec207 --- /dev/null +++ b/tests/gtest/FakeNetworkController/FakeNetworkController.hpp @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace Networking { + class AxonNetworkControllerBase; +} + +namespace TestUtils { + class FakeNetworkController; + + class FakeNetwork { + FakeNetwork() = default; + public: + static FakeNetwork & Instance () { + static FakeNetwork gInstance; + return gInstance; + } + + uint32_t create(const std::string &, uint32_t); + uint32_t connect(const std::string &, uint32_t); + + void bind(uint32_t, FakeNetworkController *); + + void sendto(const Networking::SerializedAxonMessage&, uint32_t, uint32_t); + private: + static uint32_t getDesc() { + static uint32_t desc = 0; + return ++desc; + } + + std::map pool = {}; + std::map> nodes = {}; + }; + + class FakeNetworkController : public Networking::AxonNetworkControllerBase { + public: + FakeNetworkController() = delete; + + explicit FakeNetworkController(Networking::SynapseInterface * owner, uint32_t); + explicit FakeNetworkController(Networking::SynapseInterface * owner, const Networking::ConnectionInfo&); + + void listen() override; + void send(Networking::AxonMessage&) override; + void sendTo(Networking::AxonMessage&, const Networking::NetworkNodeInfo&) override; + + GETTER Networking::SynapseInterface * owner() const { return owningSynapse; } + protected: + void sendSerializedTo(const Networking::SerializedAxonMessage&, const Networking::NetworkNodeInfo&) override; + + uint32_t connectedNode; + uint32_t self; + FakeNetwork &instance; + }; +} + +#include "FakeNetworkController.ipp" diff --git a/tests/gtest/FakeNetworkController/FakeNetworkController.ipp b/tests/gtest/FakeNetworkController/FakeNetworkController.ipp new file mode 100644 index 0000000..e13649a --- /dev/null +++ b/tests/gtest/FakeNetworkController/FakeNetworkController.ipp @@ -0,0 +1,54 @@ +#pragma once + +inline void TestUtils::FakeNetwork::sendto(const Networking::SerializedAxonMessage &serialized, uint32_t socket, uint32_t from) { + pool[socket]->owner()->processIncomingMessage(serialized, { static_cast( from ), {} }); +} + +inline uint32_t TestUtils::FakeNetwork::create( + const std::string &hostname, + uint32_t port + ) { + uint32_t desc = getDesc(); + this->nodes[hostname][port] = desc; + return desc; +} + +inline uint32_t TestUtils::FakeNetwork::connect(const std::string &hostname, uint32_t port) { + return this->nodes[hostname][port]; +} + +inline void TestUtils::FakeNetwork::bind(uint32_t desc, FakeNetworkController *controller) { + pool[desc] = controller; +} + +inline void TestUtils::FakeNetworkController::listen() {} + +inline void TestUtils::FakeNetworkController::send(Networking::AxonMessage &message) { + this->sendTo(message, {static_cast(connectedNode), {}}); +} + +inline void TestUtils::FakeNetworkController::sendTo(Networking::AxonMessage &message, const Networking::NetworkNodeInfo &node) { + this->sendSerializedTo(message.getSerialized(), node); +} + +inline void TestUtils::FakeNetworkController::sendSerializedTo(const Networking::SerializedAxonMessage &serialized, + const Networking::NetworkNodeInfo &node) { + instance.sendto(serialized, node.socket, self); +} + +inline TestUtils::FakeNetworkController::FakeNetworkController(Networking::SynapseInterface *owner, uint32_t port) : + AxonNetworkControllerBase(owner), + instance(FakeNetwork::Instance()) + { + connectedNode = self = instance.create("test-nodes-fake-host", port); + instance.bind(connectedNode, this); + } + +inline TestUtils::FakeNetworkController::FakeNetworkController(Networking::SynapseInterface *owner, + const Networking::ConnectionInfo &info) : + AxonNetworkControllerBase(owner), + instance(FakeNetwork::Instance()) { + self = instance.create("test-nodes-fake-clients", info.port); + connectedNode = instance.connect(info.hostname, info.port); + instance.bind(self, this); +} diff --git a/tests/gtest/entry.cpp b/tests/gtest/entry.cpp new file mode 100644 index 0000000..217bcfc --- /dev/null +++ b/tests/gtest/entry.cpp @@ -0,0 +1,8 @@ +#include + + +int main(int argc, char* argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tests/gtest/test_data_buffer.cpp b/tests/gtest/test_data_buffer.cpp index b7ccb99..b902dc3 100644 --- a/tests/gtest/test_data_buffer.cpp +++ b/tests/gtest/test_data_buffer.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include TEST(TEST_BINARY_STREAM, TEST_COMMON) @@ -20,9 +20,3 @@ TEST(TEST_BINARY_STREAM, TEST_COMMON) ASSERT_NE(newStream.size(), 0); ASSERT_NE(newStream.data(), nullptr); } - -int main(int argc, char* argv[]) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file diff --git a/tests/gtest/test_event_system.cpp b/tests/gtest/test_event_system.cpp new file mode 100644 index 0000000..b4e993f --- /dev/null +++ b/tests/gtest/test_event_system.cpp @@ -0,0 +1,146 @@ +#include + +#include + +class TestEvent : public EventSystem::AxonEvent +{ +public: + TestEvent() = delete; + + explicit TestEvent(uint8_t data = 0) + { + this->data = data; + } + + uint8_t get_data() const { return data; } +private: + uint8_t data; + bool isChecked = false; +}; + + +void callback(const TestEvent& event) +{ + uint8_t data = event.get_data(); + assert(data == 3); +} + +void callback2(const TestEvent& event) +{ + uint8_t data = event.get_data(); + assert(data == 3); +} + +TEST(TEST_EVENT_SYSTEM, TEST_EVENT_SYSTEM_GENERAL) +{ + /* Local events */ + TestEvent event(3); + + EventSystem::AxonEventManager manager; + + manager.subscribe(callback); + manager.invoke(&event); + + /* Global event manager */ + EventSystem::GlobalEventManager& g_manager = EventSystem::GlobalEventManager::Instance(); + g_manager.subscribe(callback); + g_manager.subscribe(callback2); + + g_manager.invoke(&event); +} + +TEST(TEST_EVENT_SYSTEM, TEST_EVENT_SYSTEM_W_CLASS_OBJ) +{ + /* Local events */ + TestEvent event(3); + EventSystem::GlobalEventManager& g_manager = EventSystem::GlobalEventManager::Instance(); + + class TestEventCheckClass { + public: + TestEventCheckClass() { + EventSystem::GlobalEventManager::Instance().subscribe< + TestEventCheckClass, TestEvent + >(&TestEventCheckClass::callback, this); + } + ~TestEventCheckClass() { + EventSystem::GlobalEventManager::Instance().unsubscribe< + TestEventCheckClass, TestEvent + >(&TestEventCheckClass::callback, this); + } + + void callback(const TestEvent &) { + call = true; + } + + bool check() const { return call; } + private: + bool call = false; + } instance; + + g_manager.invoke(&event); + ASSERT_TRUE(instance.check()); +} + +TEST(TEST_EVENT_SYSTEM, TEST_UNSUBSCRIBE_CLASS_MEMBER) +{ + /* Local events */ + TestEvent event(3); + EventSystem::GlobalEventManager& g_manager = EventSystem::GlobalEventManager::Instance(); + + class TestEventCheckClass { + public: + TestEventCheckClass() { + EventSystem::GlobalEventManager::Instance().subscribe< + TestEventCheckClass, TestEvent + >(&TestEventCheckClass::callback, this); + } + + ~TestEventCheckClass() { + EventSystem::GlobalEventManager::Instance().unsubscribe< + TestEventCheckClass, TestEvent + >(&TestEventCheckClass::callback, this); + } + + void callback(const TestEvent &) { + call = true; + } + + bool check() const { return call; } + + void reset() { call = false; } + private: + bool call = false; + } instance1, instance2; + + g_manager.invoke(&event); + ASSERT_TRUE(instance1.check()); + ASSERT_TRUE(instance2.check()); + instance1.reset(); + instance2.reset(); + ASSERT_FALSE(instance1.check()); + ASSERT_FALSE(instance2.check()); + g_manager.unsubscribe< + TestEventCheckClass, TestEvent + >(&TestEventCheckClass::callback, &instance1); + g_manager.invoke(&event); + ASSERT_FALSE(instance1.check()); + ASSERT_TRUE(instance2.check()); + g_manager.unsubscribe< + TestEventCheckClass, TestEvent + >(&TestEventCheckClass::callback, &instance2); +} + +void callback3(const TestEvent& event) +{ + throw 1; +} + +TEST(TEST_EVENT_SYSTEM, TEST_UNSUBSCRIBE_FUNCTION) { + TestEvent event(3); + EventSystem::AxonEventManager g_manager; + + g_manager.subscribe(callback3); + g_manager.unsubscribe(callback3); + + g_manager.invoke(&event); +} diff --git a/tests/gtest/test_message_pool.cpp b/tests/gtest/test_message_pool.cpp index d5e0799..2ec4b16 100644 --- a/tests/gtest/test_message_pool.cpp +++ b/tests/gtest/test_message_pool.cpp @@ -1,5 +1,5 @@ #include -#include +#include TEST(TEST_MESSAGE_POOL, TEST_REGULAR) @@ -23,10 +23,3 @@ TEST(TEST_MESSAGE_POOL, TEST_REGULAR) ASSERT_FALSE(pool.getPoolSize()); ASSERT_NE(pNode1.get(), pNode2.get()); } - - -int main(int argc, char* argv[]) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file diff --git a/tests/gtest/test_network_pointer.cpp b/tests/gtest/test_network_pointer.cpp new file mode 100644 index 0000000..62cf471 --- /dev/null +++ b/tests/gtest/test_network_pointer.cpp @@ -0,0 +1,16 @@ +#include +#include +#include "FakeNetworkController/FakeNetworkController.hpp" +#include "utility/AxonNetworkPointer.hpp" + +TEST(TEST_NETWORK_POINTER, TEST_GET_SERVER_ID) { + Networking::Synapse server(10432); + Networking::Synapse client("test-nodes-fake-host", 10432); + + server.start(); + client.start(); + uint8_t data = 0; + Networking::AxonNetworkPtr ptr(&client, &data); + + ASSERT_TRUE(ptr.ready()); +} diff --git a/tests/gtest/test_networking.cpp b/tests/gtest/test_networking.cpp index 399bd8d..04dd0e4 100644 --- a/tests/gtest/test_networking.cpp +++ b/tests/gtest/test_networking.cpp @@ -4,9 +4,3 @@ TEST(TESTS_BACKEND, TEST_BACKEND_GENERAL) { } - -int main(int argc, char* argv[]) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/gtest/test_serialization.cpp b/tests/gtest/test_serialization.cpp index ff1c9f8..183cc0d 100644 --- a/tests/gtest/test_serialization.cpp +++ b/tests/gtest/test_serialization.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include "messages/AxonMessage.hpp" #include @@ -102,29 +102,24 @@ TEST(TEST_SERIALIZATION, TEST_MESSAGE_TAG) { TEST(TEST_SERIALIZATION, TEST_MESSAGE_SPLIT) { const char* message = "Hello World!"; - Networking::AxonMessage message_( const_cast ( message ), strlen(message) + 1, 0); + auto * message_ = new Networking::AxonMessage( const_cast ( message ), strlen(message) + 1, 0); - ASSERT_STREQ(static_cast(message_.getMessage()), "Hello World!"); - Networking::AxonMessage::UniqueAxonMessagePtr ptr = message_.split(5); + ASSERT_STREQ(static_cast(message_->getMessage()), "Hello World!"); + Networking::AxonMessage::UniqueAxonMessagePtr ptr = message_->split(5); ASSERT_TRUE(ptr.get()); ASSERT_EQ(ptr->getSize(), 8); - ASSERT_EQ(message_.getSize(), 5); + ASSERT_EQ(message_->getSize(), 5); - ASSERT_STREQ(static_cast(message_.getMessage()), "Hello World!"); + ASSERT_STREQ(static_cast(message_->getMessage()), "Hello World!"); - ASSERT_TRUE(message_.hasFlag(Networking::PARTIAL)); + ASSERT_TRUE(message_->hasFlag(Networking::PARTIAL)); ASSERT_FALSE(ptr->hasFlag(Networking::PARTIAL)); - ASSERT_EQ(message_.ID(), ptr->ID()); - ASSERT_EQ(message_.getPartID() + 1, ptr->getPartID()); + ASSERT_EQ(message_->ID(), ptr->ID()); + ASSERT_EQ(message_->getPartID() + 1, ptr->getPartID()); - message_.~AxonMessage(); - ASSERT_STREQ(static_cast(ptr->getMessage()), " World!"); -} + delete message_; -int main(int argc, char* argv[]) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + ASSERT_STREQ(static_cast(ptr->getMessage()), " World!"); } diff --git a/tests/gtest/test_synapse.cpp b/tests/gtest/test_synapse.cpp new file mode 100644 index 0000000..e62c669 --- /dev/null +++ b/tests/gtest/test_synapse.cpp @@ -0,0 +1,63 @@ +#include +#include +#include "FakeNetworkController/FakeNetworkController.hpp" +#include + +bool hasVisited = false; + +namespace { + void onMessageReceived(const Networking::SynapseMessageReceivedEvent& event) { + ASSERT_STREQ(static_cast(event.getMessage().getMessage()), "Hello World!"); + hasVisited = true; + } + + void onMessageReceivedWithUniqueID(const Networking::SynapseMessageReceivedEvent& event) { + static uint64_t lastTaken = 0; + + if (!event.getMessage().hasFlag(Networking::NETOBJ_REPL)) return; + + ASSERT_EQ(event.getMessage().getSize(), sizeof(Networking::MessageProcessor::RequestUniqueIDProto)); + + Networking::MessageProcessor::RequestUniqueIDProto repl = * static_cast< Networking::MessageProcessor::RequestUniqueIDProto * > ( event.getMessage().getMessage() ); + ASSERT_NE( repl.serverSideID, lastTaken ); + lastTaken = repl.serverSideID; + hasVisited = true; + } +} + +TEST(TEST_SYNAPSE, TEST_FAKE_NETWORK) { + Networking::Synapse< + TestUtils::FakeNetworkController + > server(10435); + Networking::Synapse< + TestUtils::FakeNetworkController + > client("test-nodes-fake-host", 10435); + + server.getEventManager().subscribe(onMessageReceived); + server.start(); + client.start(); + + Networking::AxonMessage msg("Hello World!", strlen("Hello World!") + 1); + client.send(msg); + ASSERT_TRUE(hasVisited); + hasVisited = false; +} + +TEST(TEST_SYNAPSE, TEST_GET_SERVER_ID) { + Networking::Synapse server(10432); + Networking::Synapse client("test-nodes-fake-host", 10432); + client.getEventManager().subscribe(onMessageReceivedWithUniqueID); + + server.start(); + client.start(); + + Networking::MessageProcessor::RequestUniqueIDProto repl = { 0, 0 }; + Networking::AxonMessage message( &repl, sizeof(repl), 0, Networking::NETOBJ_INI ); + + client.send(message); + client.send(message); + client.send(message); + + ASSERT_TRUE(hasVisited); +} + diff --git a/tests/performance/CMakeLists.txt b/tests/performance/CMakeLists.txt new file mode 100644 index 0000000..b66e6aa --- /dev/null +++ b/tests/performance/CMakeLists.txt @@ -0,0 +1,7 @@ +project ("AxonEngine.tests.performance") + +set (LIST_LIBRARIES networking testutils) +list_all_tests_separate("perf_*.cpp" ${LIST_LIBRARIES}) +include_directories("../../libraries") +include_directories("../utils/testutils") + diff --git a/tests/performance/perf_message_api.cpp b/tests/performance/perf_message_api.cpp new file mode 100644 index 0000000..9a80763 --- /dev/null +++ b/tests/performance/perf_message_api.cpp @@ -0,0 +1,22 @@ +#include "messages/AxonMessage.hpp" +#include + +int main() { + ENABLE_MEMGUARD + + Networking::AxonMessage msg1(nullptr, 0); + Networking::AxonMessage msg2("Hello World", 12, 0, 1); + + Networking::SerializedAxonMessage serializedAxonMessage1 = msg1.getSerialized(); + Networking::SerializedAxonMessage serializedAxonMessage2(msg2); + + Networking::AxonMessage cpyMsg1(msg1); + Networking::AxonMessage cpyMsg2 = msg2; + + Networking::SerializedAxonMessage serializedAxonMessageCpy1(serializedAxonMessage1); + Networking::SerializedAxonMessage serializedAxonMessageCpy2 = serializedAxonMessage2; + + msg1.split(2); +} + +MEMGUARD_REPORT diff --git a/tests/performance/perf_serialization.cpp b/tests/performance/perf_serialization.cpp new file mode 100644 index 0000000..306af34 --- /dev/null +++ b/tests/performance/perf_serialization.cpp @@ -0,0 +1,35 @@ +#include + +#include +#include + +#include +#include +#include + +int main () { + ENABLE_MEMGUARD + constexpr char buffer[] = "Hello World! Some big data here is awaiting serialization"; + + const std::chrono::time_point start = std::chrono::system_clock::now(); + + size64_t total; + const char* result = serialize(buffer, sizeof(buffer), 2, &total); + + const std::chrono::time_point end = std::chrono::system_clock::now(); + DISABLE_MEMGUARD + std::cout << + std::left << std::setw(50) << "Execution time: " << + std::chrono::duration_cast ( end - start ).count() << "ns" << std::endl << + std::setw(50) << "Serialization allocated " << total << " bytes of memory" + << std::endl; + + for (size64_t i = 0; i < total; i++) { + std::cout << std::setw(15) << std::bitset<8>( result[i] ) << std::setw(5) << result[i] << std::setw(5) << static_cast(result[i]) << std::endl; + } + ENABLE_MEMGUARD + free((void*) result); + return 0; +} + +MEMGUARD_REPORT \ No newline at end of file diff --git a/tests/performance/perf_synapse.cpp b/tests/performance/perf_synapse.cpp new file mode 100644 index 0000000..16af340 --- /dev/null +++ b/tests/performance/perf_synapse.cpp @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +int main() { + Networking::AsyncSynapse< + Networking::BerkeleyAxonNetworkController + > server(10432); + Networking::AsyncSynapse< + Networking::BerkeleyAxonNetworkController + > client("localhost", 10432); + server.start(); + client.start(); + + ENABLE_MEMGUARD + + for (uint8_t i = 0; i < 4; i++) + { + DISABLE_MEMGUARD + std::this_thread::sleep_for(std::chrono::milliseconds (100)); + std::stringstream sstream; + sstream << + "Sending message on " << i; + Networking::AxonMessage message_(const_cast(sstream.str().c_str()), sstream.str().length() + 1, 0, Networking::TAG_FLAGS::VALIDATE); + ENABLE_MEMGUARD + client.send(message_); + } + + client.kill(); + server.kill(); +} + +MEMGUARD_REPORT diff --git a/tests/utils/CMakeLists.txt b/tests/utils/CMakeLists.txt new file mode 100644 index 0000000..fe10b99 --- /dev/null +++ b/tests/utils/CMakeLists.txt @@ -0,0 +1 @@ +add_all_subdirs() \ No newline at end of file diff --git a/tests/utils/testutils/CMakeLists.txt b/tests/utils/testutils/CMakeLists.txt new file mode 100644 index 0000000..1a4b506 --- /dev/null +++ b/tests/utils/testutils/CMakeLists.txt @@ -0,0 +1,12 @@ +file(GLOB_RECURSE SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/*.c" + "${CMAKE_CURRENT_SOURCE_DIR}/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/*.ipp" +) + +add_library(testutils STATIC ${SOURCES}) +set (LIB_HOME ${CMAKE_HOME_DIRECTORY}/test/utils/testutils) + +target_include_directories(testutils PUBLIC "${LIB_HOME}") diff --git a/tests/utils/testutils/MemoryGuard/MemoryGuard.cpp b/tests/utils/testutils/MemoryGuard/MemoryGuard.cpp new file mode 100644 index 0000000..b9983e2 --- /dev/null +++ b/tests/utils/testutils/MemoryGuard/MemoryGuard.cpp @@ -0,0 +1,78 @@ +#include "MemoryGuard.hpp" + +#if defined(__unix) || defined(__unix__) +extern "C" void init_hooks(); +extern void* (*real_malloc) (size_t); +extern void* (*real_calloc) (size_t, size_t); +extern void* (*real_realloc) (void*, size_t); +extern void (*real_free) (void*); + +void add_to_guard(void *ptr, size_t size) { + Testing::MemoryGuard::instance()->insert(ptr, size); +} + +void remove_from_guard(void *ptr) { + Testing::MemoryGuard::instance()->pop(ptr); +} + +namespace Testing { + MemoryGuard::MemoryGuard() { + init_hooks(); + } + + MemoryGuard::~MemoryGuard() { + while (head) { + MemoryNode *dummy = head; + head = head->next; + real_free(dummy); + } + } + + void MemoryGuard::insert(void *ptr, size_t size) { + if (!isEnabled) return; + + MemoryNode *node = reinterpret_cast(real_malloc(sizeof(MemoryNode))); + node->ptr = ptr; + node->size = size; + node->next = head; + head = node; + } + + void MemoryGuard::pop(void *ptr) { + if (!head) return; + if (head->ptr == ptr) { + // Remove if already on head + MemoryNode* dummy = head; + head = head->next; + real_free(dummy); + return; + } + + MemoryNode *node = head; + while (node->next && node->next->ptr != ptr) node = node->next; + + if (!node->next) return; // Not found + + // Remove + MemoryNode* dummy = node->next; + node->next = node->next->next; + real_free(dummy); + } + + size_t MemoryGuard::total() const { + size_t total = 0; + MemoryNode *node = head; + while (node) { + total += node->size; + node = node->next; + } + + return total; + } + + MemoryGuard *MemoryGuard::instance() { + static MemoryGuard g_guard; + return &g_guard; + } +} // Testing +#endif diff --git a/tests/utils/testutils/MemoryGuard/MemoryGuard.hpp b/tests/utils/testutils/MemoryGuard/MemoryGuard.hpp new file mode 100644 index 0000000..7d8d61e --- /dev/null +++ b/tests/utils/testutils/MemoryGuard/MemoryGuard.hpp @@ -0,0 +1,65 @@ +#pragma once +#if defined(__unix) || defined(__unix__) +#include +#include +#include + +extern "C" void add_to_guard(void*, size_t); +extern "C" void remove_from_guard(void*); + +namespace Testing { + struct MemoryNode { + MemoryNode *next; + + void *ptr; + size_t size; + }; + + class MemoryGuard { + public: + MemoryGuard(); + MemoryGuard(const MemoryGuard&) = delete; + MemoryGuard& operator=(const MemoryGuard&) = delete; + ~MemoryGuard(); + + void insert(void*, size_t); + void pop(void*); + [[nodiscard]] size_t total() const; + void setEnabled(bool enabled) { this->isEnabled = enabled; } + static MemoryGuard* instance(); + + [[nodiscard]] MemoryNode *node() const noexcept { return head; } + private: + MemoryNode *head = nullptr; + bool isEnabled = false; + }; +} // Testing + +#define ENABLE_MEMGUARD Testing::MemoryGuard::instance()->setEnabled(true); +#define DISABLE_MEMGUARD Testing::MemoryGuard::instance()->setEnabled(false); + +#define MEMGUARD_REPORT \ +__attribute__((destructor)) \ +void memguard_report() { \ + DISABLE_MEMGUARD \ + std::cerr.flush(); \ + std::cout.flush(); \ + Testing::MemoryGuard *g_guard = Testing::MemoryGuard::instance(); \ + Testing::MemoryNode *head = g_guard->node(); \ + if (head) { \ + while (head) { \ + std::cerr << "0x" << reinterpret_cast(head->ptr) << " " << \ + head->size << std::endl; \ + head = head->next; \ + } \ + std::cerr << "Total leaked memory: " << g_guard->total() << std::endl; \ + } \ +} +#else +#pragma message( __FILE__ " MemoryGuard is not supported on current platform" ) +#pragma message( "MemoryGuard is disabled" ) + +#define ENABLE_MEMGUARD +#define DISABLE_MEMGUARD +#define MEMGUARD_REPORT +#endif diff --git a/tests/utils/testutils/MemoryGuard/memutils.c b/tests/utils/testutils/MemoryGuard/memutils.c new file mode 100644 index 0000000..a28f47c --- /dev/null +++ b/tests/utils/testutils/MemoryGuard/memutils.c @@ -0,0 +1,44 @@ +#include "memutils.h" + +#if defined(__unix) || defined(__unix__) +extern void add_to_guard(void*, size_t); +extern void remove_from_guard(void*); + +void init_hooks() { + if (!real_malloc) + real_malloc = dlsym(RTLD_NEXT, "malloc"); + if (!real_calloc) + real_calloc = dlsym(RTLD_NEXT, "calloc"); + if (!real_realloc) + real_realloc = dlsym(RTLD_NEXT, "realloc"); + if (!real_free) + real_free = dlsym(RTLD_NEXT, "free"); +} + +void* malloc(size_t size) { + if (!real_malloc) init_hooks(); + void *ptr = real_malloc(size); + add_to_guard(ptr, size); + return ptr; +} + +void* calloc(size_t nmem, size_t size) { + if (!real_calloc) init_hooks(); + void *ptr = real_calloc(nmem, size); + add_to_guard(ptr, nmem * size); + return ptr; +} + +void* realloc(void *ptr, size_t size) { + if (!real_realloc) init_hooks(); + ptr = real_realloc(ptr, size); + add_to_guard(ptr, size); + return ptr; +} + +void free(void *ptr) { + if (!real_free) init_hooks(); + remove_from_guard(ptr); + real_free(ptr); +} +#endif diff --git a/tests/utils/testutils/MemoryGuard/memutils.h b/tests/utils/testutils/MemoryGuard/memutils.h new file mode 100644 index 0000000..ada1343 --- /dev/null +++ b/tests/utils/testutils/MemoryGuard/memutils.h @@ -0,0 +1,19 @@ +#ifndef AXON_MEMUTILS_H +#define AXON_MEMUTILS_H +#if defined(__unix) || defined(__unix__) +#include +#include + +// Hooks to "real" functions + +void* (*real_malloc) (size_t) = NULL; +void* (*real_calloc) (size_t, size_t) = NULL; +void* (*real_realloc) (void*, size_t) = NULL; +void (*real_free) (void*) = NULL; + +void *malloc(size_t size); +void *calloc(size_t nmem, size_t size); +void *realloc(void *ptr, size_t size); +void free(void *ptr); +#endif +#endif \ No newline at end of file