diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5dfcfc72..90b29df7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,3 +20,4 @@ target_sources( add_subdirectory(Client) add_subdirectory(ECS) +add_subdirectory(Nitwork) diff --git a/src/Nitwork.hpp b/src/Nitwork.hpp deleted file mode 100644 index b0d3d963..00000000 --- a/src/Nitwork.hpp +++ /dev/null @@ -1,238 +0,0 @@ -/* -** EPITECH PROJECT, 2023 -** R-Bus -** File description: -** Nitwork library, a portable network library -*/ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAX_ACTIONS 16 -#define MAX_PLAYERS 4 -#define HEADER_SIZE sizeof(int) * 4 -#define TICKS_PER_SECOND 128 - -namespace Nitwork { - namespace Actions { - extern "C" - { - // Parent structs - typedef int idsReceived_t; - typedef int id_t; - typedef int nbAction_t; - - struct header_s { - char magick1; - idsReceived_t ids_received; - id_t last_id_received; - id_t id; - nbAction_t nb_action; - char magick2; - } __attribute__((packed)); - - typedef char actionType_t; - - struct action_s { - actionType_t magick; - } __attribute__((packed)); - // Init action structs - - struct msgInit_s { - actionType_t magick; // type of the action - } __attribute__((packed)); - - struct msgReady_s { - actionType_t magick; // type of the action - } __attribute__((packed)); - } - } - class Nitwork { - public: - Nitwork() - : _context(), - _socket(_context) {} - - Nitwork &getInstance() { - static Nitwork instance; - - return instance; - } - - // start the server - int Start(int port = 4242) { - try { - _endpoint = asio::ip::udp::endpoint(asio::ip::udp::v4(), port); - _socket.open(asio::ip::udp::v4()); - _socket.bind(_endpoint); - ContinuousReception(); - return true; - } catch (std::exception &e) { - std::cerr << e.what() << std::endl; - return false; - } - } - int Stop() { - _context.stop(); - _inputThread.join(); - _clockThread.join(); - for (auto &thread : _outputThreads) { - thread.join(); - } - } - void ContinuousReception() { - _inputThread = std::thread([this]() { - _context.run(); - - }); - } - // Method which handle clock and unlock client threads each n ticks - void ClockThread(int tick) { - _clockThread = std::thread([this]() { - while (true) { - std::this_thread::sleep_for(std::chrono::milliseconds(1000 / TICKS_PER_SECOND)); - _queueCondVar.notify_all(); - } - }); - } - void ClientsDatasHandler() { - _outputThreads = std::array(); - for (int i = 0; i < MAX_PLAYERS; i++) { - _outputThreads[i] = std::thread([this]() { - std::unique_lock lock(_queueMutex); - while (true) { - _queueCondVar.wait(lock); - // handle actions and send it to the client - } - }); - } - } - template - void sendDatasToEndpoint(asio::ip::udp::endpoint &endpoint, T &datas) { - _socket.send_to(asio::buffer(&datas, sizeof(T)), endpoint); - } - - template - void sendDatasToAll(T &datas) { - for (auto &endpoint : _endpoints) { - sendDatasToEndpoint(endpoint, datas); - } - } - template - void handleBodyDatas(const struct Actions::header_s &header, const std::function &handler, B body, const asio::error_code& error, const std::size_t bytes_received) { - if (error) { - std::cerr << "Error: " << error.message() << std::endl; - return; - } else if (bytes_received != sizeof(B)) { - std::cerr << "Error: body not received" << std::endl; - return; - } - _actions.emplace_back(body); - _actionsHandler.emplace_back(handler); - } - template - void handleBody(const Actions::header_s &header, const std::function &handler) { - B body; - - _socket.async_receive_from(asio::buffer(&body, sizeof(B)), _endpoint, boost::bind(&Nitwork::handleBodyDatas, this, header, handler, body, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); - } - void handleBodyActionDatas(const Actions::action_s &action, const Actions::header_s &header, const asio::error_code& error, const std::size_t bytes_received) { - if (error) { - std::cerr << "Error: " << error.message() << std::endl; - return; - } else if (bytes_received != sizeof(Actions::actionType_t)) { - std::cerr << "Error: body not received" << std::endl; - return; - } - auto it = _actionsHandlers.find(action.magick); - if (it == _actionsHandlers.end()) { - std::cerr << "Error: action not found" << std::endl; - return; - } - it->second.first(header, it->second.second); - } - void handleBodyAction(const Actions::header_s &header) { - Actions::action_s action; - - _socket.async_receive_from(asio::buffer(&action, sizeof(Actions::action_s)), _endpoint, boost::bind(&Nitwork::handleBodyActionDatas, this, action, header, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); - } - void readDataFromEndpoint(asio::ip::udp::endpoint &endpoint) { - size_t header_bytes_received; - struct Actions::header_s header; - - while (true) { - header_bytes_received = _socket.receive_from(asio::buffer(&header, HEADER_SIZE), endpoint); - - if (header_bytes_received != sizeof(Actions::header_s)) { - std::cerr << "Error: header not received" << std::endl; - continue; - } - if (header.nb_action > MAX_ACTIONS || header.nb_action < 0) { - std::cerr << "Error: too many actions received or no action" << std::endl; - continue; - } - for (int i = 0; i < header.nb_action; i++) { - handleBodyAction(header); - } - break; - } - } - void handleInitMsg(const std::any &msg) { - const Actions::msgInit_s &initMsg = std::any_cast(msg); - std::cout << "init" << std::endl; - } - void handleReadyMsg(const std::any &msg) { - const Actions::msgReady_s &readyMsg = std::any_cast(msg); - std::cout << "ready" << std::endl; - } - - protected: - private: - asio::io_context _context; // second context which will handle the outputs (handle actions and send it, each n ticks - std::thread _inputThread; // A thread for the first context - std::thread _clockThread; // A thread for the clock which is in the second context - std::array _outputThreads; // A thread for each player which is in the second context - - // Body handler vars - std::list _actions; // A list of actions which will be handled by the second context - std::list> _actionsHandler; // A list of functions which will be used to handle the actions - - std::mutex _queueMutex; // A mutex to lock the queue which will be used by both contexts - std::condition_variable _queueCondVar; // A condition variable to wait for the queue to be used by the second context - std::vector _endpoints; // A vector of endpoints which will be used to send the actions to the clients and identify them - std::array _ids; // An array of ids which will be used to identify the actions - asio::ip::udp::socket _socket; // The socket which will be used to send and receive the actions - asio::ip::udp::endpoint _endpoint; // The endpoint which will be used to send and receive the actions - - std::map< - Actions::actionType_t, - std::pair< - std::function &)>, - std::function - > - > _actionsHandlers = { - {'~', - std::make_pair( - std::function &)>(std::bind(&Nitwork::handleBody, this, std::placeholders::_1, std::placeholders::_2)), - std::function(std::bind(&Nitwork::handleInitMsg, this, std::placeholders::_1)) - ) - }, - {'#', - std::make_pair( - std::function &)>(std::bind(&Nitwork::handleBody, this, std::placeholders::_1, std::placeholders::_2)), - std::function(std::bind(&Nitwork::handleReadyMsg, this, std::placeholders::_1)) - ) - } - }; - - }; -} diff --git a/src/Nitwork/CMakeLists.txt b/src/Nitwork/CMakeLists.txt new file mode 100644 index 00000000..2369b803 --- /dev/null +++ b/src/Nitwork/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.15) + +target_include_directories( + ${PROJECT_NAME_CLIENT} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) +target_include_directories( + ${PROJECT_NAME_SERVER} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_sources( + ${PROJECT_NAME_CLIENT} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/Nitwork.cpp +) + +target_sources( + ${PROJECT_NAME_SERVER} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/Nitwork.cpp +) diff --git a/src/Nitwork/Nitwork.cpp b/src/Nitwork/Nitwork.cpp new file mode 100644 index 00000000..5c75da84 --- /dev/null +++ b/src/Nitwork/Nitwork.cpp @@ -0,0 +1,135 @@ +/* +** EPITECH PROJECT, 2023 +** R-Bus +** File description: +** Implementation of nitwork +*/ + +#include "Nitwork.hpp" + +namespace Nitwork { + // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) + Nitwork Nitwork::_instance = Nitwork(); + // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) + + Nitwork &Nitwork::getInstance() { + return _instance; + } + + bool Nitwork::Start(int port) { + try { + _endpoint = boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port); + _socket.open(boost::asio::ip::udp::v4()); + _socket.bind(_endpoint); + ContinuousReception(); + return true; + } catch (std::exception &e) { + std::cerr << e.what() << std::endl; + return false; + } + } + + bool Nitwork::Stop() { + _context.stop(); + _inputThread.join(); + _clockThread.join(); + for (auto &thread : _outputThreads) { + thread.join(); + } + } + + void Nitwork::ContinuousReception() { + _inputThread = std::thread([this]() { + _context.run(); + }); + } + + void Nitwork::ClockThread(int tick) { + _clockThread = std::thread([this]() { + while (true) { + std::this_thread::sleep_for(std::chrono::milliseconds(1000 / TICKS_PER_SECOND)); + _queueCondVar.notify_all(); + } + }); + } + + void Nitwork::ClientsDatasHandler() { + _outputThreads = std::array(); + for (int i = 0; i < MAX_PLAYERS; i++) { + _outputThreads.at(i) = std::thread([this]() { + std::unique_lock lock(_queueMutex); + while (true) { + _queueCondVar.wait(lock); + // handle actions and send it to the client + } + }); + } + } + + + void Nitwork::handleBodyActionDatas(const struct action_s &action, const struct header_s &header, const boost::system::error_code& error, std::size_t bytes_received) { + if (error) { + std::cerr << "Error: " << error.message() << std::endl; + return; + } + if (bytes_received != sizeof(enum n_actionType_t)) { + std::cerr << "Error: body not received" << std::endl; + return; + } + auto it = _actionsHandlers.find(action.magick); + if (it == _actionsHandlers.end()) { + std::cerr << "Error: action not found" << std::endl; + return; + } + it->second.first(header, it->second.second); + } + + void Nitwork::handleBodyAction(const struct header_s &header) { + struct action_s action = {NO_ACTION}; + + _socket.async_receive_from( + boost::asio::buffer(&action, sizeof(struct action_s)), + _endpoint, + boost::bind( + &Nitwork::handleBodyActionDatas, + this, + action, + header, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred + ) + ); + } + + void Nitwork::readDataFromEndpoint(boost::asio::ip::udp::endpoint &endpoint) { + size_t header_bytes_received = 0; + struct header_s header = {0, 0, 0, 0, 0, 0}; + + while (true) { + header_bytes_received = _socket.receive_from(boost::asio::buffer(&header, HEADER_SIZE), endpoint); + + if (header_bytes_received != sizeof(struct header_s)) { + std::cerr << "Error: header not received" << std::endl; + continue; + } + if (header.nb_action > MAX_NB_ACTION || header.nb_action < 0) { + std::cerr << "Error: too many actions received or no action" << std::endl; + continue; + } + for (int i = 0; i < header.nb_action; i++) { + handleBodyAction(header); + } + break; + } + } + + void Nitwork::handleInitMsg(const std::any &msg) { + const struct msgInit_s &initMsg = std::any_cast(msg); + std::cout << "init" << std::endl; + } + + void Nitwork::handleReadyMsg(const std::any &msg) { + const struct msgReady_s &readyMsg = std::any_cast(msg); + std::cout << "ready" << std::endl; + } +} diff --git a/src/Nitwork/Nitwork.h b/src/Nitwork/Nitwork.h new file mode 100644 index 00000000..78fc2829 --- /dev/null +++ b/src/Nitwork/Nitwork.h @@ -0,0 +1,48 @@ +/* +** EPITECH PROJECT, 2023 +** R-Bus +** File description: +** Internal header for Nitwork C +*/ + +#ifndef NITWORK_H + #define NITWORK_H + + #define HEADER_SIZE sizeof(struct header_s) + #define TICKS_PER_SECOND 128 + #define MAX_NB_ACTION 16 + +typedef char n_magick_t; +typedef int n_idsReceived_t; +typedef int n_id_t; +typedef int n_nbAction_t; + +enum n_actionType_t { + NO_ACTION = 0, + INIT = 1, + READY = 2, + N_ACTION_TYPE_MAX, +}; + +struct header_s { + n_magick_t magick1; + n_idsReceived_t ids_received; + n_id_t last_id_received; + n_id_t id; + n_nbAction_t nb_action; + n_magick_t magick2; +} __attribute__((packed)); + +struct action_s { + enum n_actionType_t magick; +} __attribute__((packed)); + +struct msgInit_s { + n_magick_t magick; +} __attribute__((packed)); + +struct msgReady_s { + n_magick_t magick; +} __attribute__((packed)); + +#endif diff --git a/src/Nitwork/Nitwork.hpp b/src/Nitwork/Nitwork.hpp new file mode 100644 index 00000000..a621c02e --- /dev/null +++ b/src/Nitwork/Nitwork.hpp @@ -0,0 +1,130 @@ +/* +** EPITECH PROJECT, 2023 +** R-Bus +** File description: +** Nitwork library, a portable network library +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Nitwork.h" + +#define MAX_PLAYERS 4 + +namespace Nitwork { + class Nitwork { + public: + Nitwork() + : _socket(_context) {} + + static Nitwork &getInstance(); + + // start the server + bool Start(int port); + + bool Stop(); + + void ContinuousReception(); + + // Method which handle clock and unlock client threads each n ticks + void ClockThread(int tick); + + void ClientsDatasHandler(); + + template + void sendDatasToEndpoint(boost::asio::ip::udp::endpoint &endpoint, T &datas) { + _socket.send_to(boost::asio::buffer(&datas, sizeof(T)), endpoint); + } + + template + void sendDatasToAll(T &datas) { + for (auto &endpoint : _endpoints) { + sendDatasToEndpoint(endpoint, datas); + } + } + + template + void handleBodyDatas(const struct header_s &header, const std::function &handler, B body, const boost::system::error_code& error, const std::size_t bytes_received) { + if (error) { + std::cerr << "Error: " << error.message() << std::endl; + return; + } + if (bytes_received != sizeof(B)) { + std::cerr << "Error: body not received" << std::endl; + return; + } + _actions.emplace_back(body); + _actionsHandler.emplace_back(handler); + } + + template + void handleBody(const struct header_s &header, const std::function &handler) { + B body; + + _socket.async_receive_from(boost::asio::buffer(&body, sizeof(B)), _endpoint, boost::bind(&Nitwork::handleBodyDatas, this, header, handler, body, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + + void handleBodyActionDatas(const struct action_s &action, const struct header_s &header, const boost::system::error_code& error, std::size_t bytes_received); + + void handleBodyAction(const struct header_s &header); + + void readDataFromEndpoint(boost::asio::ip::udp::endpoint &endpoint); + + void handleInitMsg(const std::any &msg); + + void handleReadyMsg(const std::any &msg); + + protected: + private: + // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) + static Nitwork _instance; + // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) + boost::asio::io_context _context; // second context which will handle the outputs (handle actions and send it, each n ticks + std::thread _inputThread; // A thread for the first context + std::thread _clockThread; // A thread for the clock which is in the second context + std::array _outputThreads; // A thread for each player which is in the second context + + // Body handler vars + std::list _actions; // A list of actions which will be handled by the second context + std::list> _actionsHandler; // A list of functions which will be used to handle the actions + + std::mutex _queueMutex; // A mutex to lock the queue which will be used by both contexts + std::condition_variable _queueCondVar; // A condition variable to wait for the queue to be used by the second context + std::vector _endpoints; // A vector of endpoints which will be used to send the actions to the clients and identify them + std::array _ids{}; // An array of ids which will be used to identify the actions + boost::asio::ip::udp::socket _socket; // The socket which will be used to send and receive the actions + boost::asio::ip::udp::endpoint _endpoint; // The endpoint which will be used to send and receive the actions + + const std::map< + enum n_actionType_t, + std::pair< + std::function &)>, + std::function + > + > _actionsHandlers = { + {INIT, + std::make_pair( + std::function &)>(std::bind(&Nitwork::handleBody, this, std::placeholders::_1, std::placeholders::_2)), + std::function(std::bind(&Nitwork::handleInitMsg, this, std::placeholders::_1)) + ) + }, + {READY, + std::make_pair( + std::function &)>(std::bind(&Nitwork::handleBody, this, std::placeholders::_1, std::placeholders::_2)), + std::function(std::bind(&Nitwork::handleReadyMsg, this, std::placeholders::_1)) + ) + } + }; + + + }; +}