diff --git a/CMakeLists.txt b/CMakeLists.txt index 8700d58..dc1b253 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,24 @@ cmake_minimum_required(VERSION 3.5) +set(CMAKE_CXX_STANDARD 20) project(R-Type-Reborn LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) + include_directories(includes) link_directories(libs) link_libraries(StellarForge) -add_executable(R-Type-Reborn ${R-Type-Reborn_SOURCES}) +find_package(asio REQUIRED) + +add_subdirectory(src/network) + +add_executable(R-Type-Reborn-server tests/server/main.cpp) +target_link_libraries(R-Type-Reborn-server PRIVATE rtype::network) + +add_executable(R-Type-Reborn-client tests/client/main.cpp) +target_link_libraries(R-Type-Reborn-client PRIVATE rtype::network) + -add_subdirectory(src) +include_directories(src/network) +#add_subdirectory(tests) diff --git a/conanfile.txt b/conanfile.txt new file mode 100644 index 0000000..300a0b1 --- /dev/null +++ b/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +asio/1.31.0 + +[generators] +CMakeDeps +CMakeToolchain diff --git a/documentation/conan_installation_guide.md b/documentation/conan_installation_guide.md new file mode 100644 index 0000000..c5d205c --- /dev/null +++ b/documentation/conan_installation_guide.md @@ -0,0 +1,80 @@ + +# Installing `Conan` + +This guide provides step-by-step instructions to install `Conan` on Windows, Linux, and macOS, including how to configure your system for use. + +## Windows / Windows server + +1. **Open PowerShell as Administrator**. +2. **Install Conan using `pip`**: + - First, ensure that you have Python and `pip` installed. If not, download and install Python from the [official website](https://www.python.org/downloads/). + - Run the following command to install Conan: + ```bash + pip install conan + ``` + +3. **Verify installation**: + After installing, verify Conan is installed by running: + ```bash + conan --version + ``` + +4. **Add Conan to the system path** (optional): + If you encounter issues with the `conan` command, you may need to add Python's Scripts folder to your system path: + - Open **Start** and search for **Environment Variables**. + - In the System Properties window, click on **Environment Variables**. + - Under **System variables**, find and select the **Path** variable, then click **Edit**. + - Click **New** and add the path to Python’s Scripts folder, typically `C:\Users\YourUsername\AppData\Local\Programs\Python\PythonXX\Scripts`. + - Click **OK** to close all windows. + +## Linux + +1. **Open a terminal**. +2. **Install Conan using `pip`**: + First, make sure you have Python and `pip` installed. Then, install Conan: + ```bash + sudo apt update + sudo apt install python3-pip + pip3 install conan + ``` + +3. **Add Conan to the system path** (if necessary): + - If `conan` is not recognized, ensure `~/.local/bin` is in your PATH by adding the following line to your `.bashrc` or `.zshrc` file: + ```bash + export PATH="$HOME/.local/bin:$PATH" + ``` + - Apply the changes: + ```bash + source ~/.bashrc + ``` + +4. **Verify installation**: + Restart your terminal and run: + ```bash + conan --version + ``` + +## macOS + +1. **Open a terminal**. +2. **Install Homebrew** (if not already installed): + - Homebrew is a package manager for macOS. You can install it with: + ```bash + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + ``` + +3. **Install Conan using Homebrew**: + - Once Homebrew is installed, install Conan with: + ```bash + brew install conan + ``` + +4. **Verify installation**: + Restart your terminal and run: + ```bash + conan --version + ``` + +--- + +Follow these instructions to ensure a smooth installation of `Conan` on your system. diff --git a/documentation/vcpkg_installation_guide.md b/documentation/vcpkg_installation_guide.md new file mode 100644 index 0000000..c2ff043 --- /dev/null +++ b/documentation/vcpkg_installation_guide.md @@ -0,0 +1,106 @@ + +# Installing `vcpkg` (outdated guide conan is used now) + +This guide provides step-by-step instructions to install `vcpkg` on Windows, Linux, and macOS, including how to add it to your system path. + +## Windows / Windows server + +1. **Open PowerShell as Administrator**. +2. **Clone the `vcpkg` repository**: + ```bash + git clone https://github.com/microsoft/vcpkg.git + cd vcpkg + .\bootstrap-vcpkg.bat + ``` + +3. **Add `vcpkg` to the system path**: + - Open **Start** and search for **Environment Variables**. + - In the System Properties window, click on **Environment Variables**. + - Under **System variables**, find and select the **Path** variable, then click **Edit**. + - Click **New** and add the full path to the `vcpkg` executable, e.g., `C:\path\to\vcpkg`. + - Click **OK** to close all windows. + + +
+ + +4. **Verify installation**: + After adding `vcpkg` to the path, restart your terminal or PowerShell and run: + ```bash + vcpkg --version + ``` + +## Linux + +1. **Open a terminal**. +2. **Install essential build tools**: + ```bash + sudo apt-get update + sudo apt-get install build-essential curl zip unzip + ``` + +3. **Clone the `vcpkg` repository**: + ```bash + git clone https://github.com/microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ``` + +4. **Add `vcpkg` to the system path**: + - Open your `.bashrc` or `.zshrc` file: + ```bash + nano ~/.bashrc + ``` + - Add the following line at the end: + ```bash + export PATH="$HOME/path/to/vcpkg:$PATH" + ``` + - Save and exit the editor, then run: + ```bash + source ~/.bashrc + ``` + +5. **Verify installation**: + Restart your terminal and run: + ```bash + vcpkg --version + ``` + +## macOS + +1. **Open a terminal**. +2. **Install Xcode Command Line Tools**: + ```bash + xcode-select --install + ``` + +3. **Clone the `vcpkg` repository**: + ```bash + git clone https://github.com/microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ``` + +4. **Add `vcpkg` to the system path**: + - Open your `.bash_profile` or `.zshrc` file: + ```bash + nano ~/.bash_profile + ``` + - Add the following line: + ```bash + export PATH="$HOME/path/to/vcpkg:$PATH" + ``` + - Save and exit, then run: + ```bash + source ~/.bash_profile + ``` + +5. **Verify installation**: + Restart your terminal and run: + ```bash + vcpkg --version + ``` + +--- + +Follow these instructions to ensure a smooth installation of `vcpkg` on your system. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 786a7fc..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -target_sources(R-Type-Reborn - PUBLIC - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp -) diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 61a1531..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* -** EPITECH PROJECT, 2024 -** R-Type-Reborn -** File description: -** No file there , just an epitech header example . -** You can even have multiple lines if you want ! -*/ - -#include "StellarForge/UtilExemple.hpp" - -int main(int argc, char **argv) { - UtilExemple::myFunction(); - return 0; -} diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt new file mode 100644 index 0000000..48f5477 --- /dev/null +++ b/src/network/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.5) +add_library(Rtype-network STATIC) +add_library(rtype::network ALIAS Rtype-network) +target_link_libraries(Rtype-network + PUBLIC + asio::asio +) + +target_sources(Rtype-network + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/server/Server.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/client/Client.hpp + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/client/Client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/server/Server.cpp +) diff --git a/src/network/client/Client.cpp b/src/network/client/Client.cpp new file mode 100644 index 0000000..c23ca62 --- /dev/null +++ b/src/network/client/Client.cpp @@ -0,0 +1,83 @@ +/* +** EPITECH PROJECT, 2024 +** R-Type-Reborn +** File description: +** No file there , just an epitech header example . +** You can even have multiple lines if you want ! +*/ +#include "Client.hpp" +#include +#include +#include +#include + +Network::Client::Client(std::string host, const unsigned short udp_port, unsigned short tcp_port) + : _host(std::move(host)), _UDP_PORT(udp_port), _TCP_PORT(tcp_port), _udp_socket(_io_context), _tcp_socket(_io_context) +{ + _id = -1; +} + +Network::Client::~Client() +{ + _io_context.stop(); +} + +void Network::Client::connect(callback function) +{ + // TCP connection init + asio::ip::tcp::resolver resolver(_io_context); + asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(_host, std::to_string(_TCP_PORT)); + + asio::error_code error; + asio::connect(_tcp_socket, endpoints, error); + if (error) { + std::cerr << "Error: " << error.message() << std::endl; + return; + } + + asio::read(_tcp_socket, asio::buffer(&_id, sizeof(_id)), error); + if (error) { + std::cerr << "Error: " << error.message() << std::endl; + return; + } else { + std::cout << "Connected to server with id " << static_cast(_id) << std::endl; + } + + // UDP connection init + _udp_socket.open(asio::ip::udp::v4()); + _endpoint = asio::ip::udp::endpoint(asio::ip::address::from_string(_host), _UDP_PORT); + _callback = std::move(function); + receive(); +} + +void Network::Client::send(const std::vector &data) +{ + _udp_socket.async_send_to(asio::buffer(data), _endpoint, + [](const asio::error_code &error, std::size_t bytes_transferred) { + if (error) { + std::cerr << "Error: " << error.message() << std::endl; + } + }); + std::cout << "Data sent" << std::endl; +} + +void Network::Client::receive() { + _udp_socket.async_receive_from(asio::buffer(_recv_buffer), _endpoint, + [this](const asio::error_code &error, std::size_t bytes_read) + { + if (!error) { + if(_callback) { + _callback(_recv_buffer, _endpoint); + } + receive(); + } else { + std::cerr << "Error: " << error.message() << std::endl; + } + }); +} + +void Network::Client::disconnect() +{ + _tcp_socket.close(); + _udp_socket.close(); +} diff --git a/src/network/client/Client.hpp b/src/network/client/Client.hpp new file mode 100644 index 0000000..d5b4c49 --- /dev/null +++ b/src/network/client/Client.hpp @@ -0,0 +1,106 @@ +/* +** EPITECH PROJECT, 2024 +** R-Type-Reborn +** File description: +** No file there , just an epitech header example . +** You can even have multiple lines if you want ! +*/ + +#ifndef CLIENT_HPP +#define CLIENT_HPP + +#include +#include + +/** + * @namespace Network + * @brief Main namespace for the network library + * @details This namespace contains all the classes, functions and enums for the network library + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ +namespace Network { + + /** + * @class Client + * @brief Represents a network client + * @details This class handles the network client operations such as connecting, sending, receiving, and disconnecting. + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + class Client { + + using callback = std::function &data, const asio::ip::udp::endpoint &client_endpoint)>; + public: + /** + * @brief Constructs a new network client object + * @param host The host address to connect to + * @param udp_port The UDP port to use + * @param tcp_port The TCP port to use + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + Client(std::string host, unsigned short udp_port, unsigned short tcp_port); + + /** + * @brief Destroys the network client object + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + ~Client(); + + /** + * @brief Connects to the server with tcp and udp sockets + * @param function The callback function to handle received data + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void connect(callback function); + + /** + * @brief Sends data to the server with udp socket + * @param data The data to send + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void send(const std::vector &data); + + /** + * @brief Receives data from the server with udp socket + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void receive(); + + /** + * @brief Disconnects from the client from the server + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void disconnect(); + + private: + std::string _host; ///< The host address + unsigned short _TCP_PORT; ///< The TCP port + unsigned short _UDP_PORT; ///< The UDP port + + asio::io_context _io_context; ///< The IO context + asio::ip::udp::socket _udp_socket; ///< The UDP socket + asio::ip::tcp::socket _tcp_socket; ///< The TCP socket + asio::ip::udp::endpoint _endpoint; ///< The UDP endpoint + + int8_t _id; ///< The client ID + std::vector _recv_buffer; ///< The receive buffer + callback _callback; ///< The callback function + }; +} + +#endif // CLIENT_HPP diff --git a/src/network/server/Server.cpp b/src/network/server/Server.cpp new file mode 100644 index 0000000..41441ee --- /dev/null +++ b/src/network/server/Server.cpp @@ -0,0 +1,108 @@ +/* +** EPITECH PROJECT, 2024 +** R-Type-Reborn +** File description: +** No file there , just an epitech header example . +** You can even have multiple lines if you want ! +*/ + +#include "Server.hpp" +#include +#include +#include +#include + +Network::Server::Server(unsigned short TCP_port, unsigned short UDP_port) + :_io_context(), _acceptor(_io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), TCP_port)), + _socket(_io_context, asio::ip::udp::endpoint(asio::ip::udp::v4(), UDP_port)) { + _TCP_port = TCP_port; + _UDP_port = UDP_port; + _recv_buffer.resize(1024); +} + +void Network::Server::start(callback function) { + _callback = std::move(function); + + std::cout << "Server started on TCP port " << _TCP_port << " and UDP port " << _UDP_port << std::endl; + std::cout << "Server hostname: " << asio::ip::host_name() << std::endl; + acceptTCP(); + receiveUDP(); + _io_context.run(); +} + +void Network::Server::stop() { + _io_context.stop(); + if (_socket.is_open()) { + _socket.close(); + } +} + +void Network::Server::send(const std::vector& data, const asio::ip::udp::endpoint& client_endpoint) { + _socket.async_send_to(asio::buffer(data), client_endpoint, + [](const asio::error_code& error, std::size_t bytes_transferred) { + if (error) { + std::cerr << "Error: " << error.message() << std::endl; + } + } + ); +} + +void Network::Server::acceptTCP() { + auto socket = std::make_shared(_io_context); + + _acceptor.async_accept(*socket, [this, socket](const asio::error_code& error) { + if (!error) { + int8_t id = 0; // create_client_id(); + while (_clients.find(id) != _clients.end()) { + id++; // id = create_client_id(); + } + _clients[id] = asio::ip::udp::endpoint(); + readTCP(*socket, id); + + asio::write(*socket, asio::buffer(&id, sizeof(id))); + acceptTCP(); + } + }); +} + +void Network::Server::readTCP(asio::ip::tcp::socket& tcp_socket, int8_t id) { + asio::async_read_until(tcp_socket, asio::dynamic_buffer(_recv_buffer), '\n', + [this, &tcp_socket, id](const asio::error_code& error, std::size_t bytes_transferred) { + if (error) { + if (error == asio::error::eof) { + std::cout << "Client disconnected" << std::endl; + } else { + std::cerr << "Error: " << error.message() << std::endl; + } + _clients.erase(id); + } else { + readTCP(tcp_socket, id); + } + } + ); +} + +void Network::Server::receiveUDP() { + _socket.async_receive_from(asio::buffer(_recv_buffer), _remote_endpoint, + [this](const asio::error_code& error, std::size_t rc_bytes) { + if (!error && rc_bytes > 0) { + try { + std::string message(_recv_buffer.begin(), _recv_buffer.begin() + rc_bytes); + std::cout << "Received: " << message << std::endl; + } catch (std::exception& e) { + std::cerr << "Exception: " << e.what() << std::endl; + } + } + receiveUDP(); + } + ); +} + +std::string Network::Server::getHostIP() const { + return asio::ip::host_name(); +} + +int8_t Network::Server::create_client_id() { + static int8_t id = 0; + return id++; +} diff --git a/src/network/server/Server.hpp b/src/network/server/Server.hpp new file mode 100644 index 0000000..f22e85e --- /dev/null +++ b/src/network/server/Server.hpp @@ -0,0 +1,134 @@ +/* +** EPITECH PROJECT, 2024 +** R-Type-Reborn +** File description: +** No file there , just an epitech header example . +** You can even have multiple lines if you want ! +*/ + +#ifndef SERVER_HPP +#define SERVER_HPP + +#include +#include +#include + +/** + * @namespace Network + * @brief Main namespace for the network library + * @details This namespace contains all the classes, functions and enums for the network library + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ +namespace Network { + + /** + * @class Server + * @brief Represents a network server + * @details This class handles the network server operations such as starting, stopping, sending, and receiving data. + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + class Server { + using callback = std::function &data, + const asio::ip::udp::endpoint &client_endpoint)>; + public: + /** + * @brief Constructs a new Server object + * @param TCP_port The TCP port to use + * @param UDP_port The UDP port to use + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + explicit Server(unsigned short TCP_port, unsigned short UDP_port); + + /** + * @brief Starts the server listening for incoming connections + * @param function The callback function to handle received data + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void start(callback function); + + /** + * @brief Stops the server and closes all connections + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void stop(); + + /** + * @brief Sends data to a client with a specific endpoint in UDP mode + * @param data The data to send + * @param client_endpoint The endpoint of the client to send data to + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void send(const std::vector &data, const asio::ip::udp::endpoint &client_endpoint); + + /** + * @brief Accepts a new TCP connection + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void acceptTCP(); + + /** + * @brief Reads data from a TCP connection + * @param tcp_socket The TCP socket to read from + * @param id The client ID + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void readTCP(asio::ip::tcp::socket &tcp_socket, int8_t id); + + /** + * @brief Receives data from a UDP connection + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + void receiveUDP(); + + /** + * @brief Creates a new client ID + * @return The new client ID + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + static int8_t create_client_id(); + + /** + * @brief Gets the host IP address + * @return The host IP address on the current network + * @version 0.1.0 + * @since 0.1.0 + * @author Simon GANIER-LOMBARD + */ + std::string getHostIP() const; + + private: + unsigned short _TCP_port; ///< The TCP port + unsigned short _UDP_port; ///< The UDP port + + asio::io_context _io_context; ///< The IO context + asio::ip::tcp::acceptor _acceptor; ///< The TCP acceptor for incoming connections + asio::ip::udp::socket _socket; ///< The UDP socket for incoming connections + + std::unordered_map _clients; ///< The clients map with their ID and endpoint + std::vector _recv_buffer; ///< The receive buffer for incoming data + asio::ip::udp::endpoint _remote_endpoint; ///< The remote endpoint + callback _callback; ///< The callback function + }; +} + +#endif // SERVER_HPP diff --git a/tests/client/main.cpp b/tests/client/main.cpp new file mode 100644 index 0000000..766d4c6 --- /dev/null +++ b/tests/client/main.cpp @@ -0,0 +1,50 @@ +/* +** EPITECH PROJECT, 2024 +** R-Type-Reborn +** File description: +** No file there , just an epitech header example . +** You can even have multiple lines if you want ! +*/ + +#include "client/Client.hpp" + +/* +** EPITECH PROJECT, 2024 +** R-Type-Reborn +** File description: +** No file there , just an epitech header example . +** You can even have multiple lines if you want ! +*/ + +#include "client/Client.hpp" +#include + +int main(int argc, char* argv[]) +{ + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return 1; + } + + const std::string host = argv[1]; + const unsigned short TCP_port = static_cast(std::stoi(argv[2])); + const unsigned short UDP_port = static_cast(std::stoi(argv[3])); + + try { + Network::Client client(host, UDP_port, TCP_port); + std::cout << "Client trying to connect to server..." << std::endl; + client.connect([](const std::vector& data, const asio::ip::udp::endpoint& client_endpoint) { + // Handle received data + }); + + const std::vector message = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; + client.send(message); + sleep(6); + + std::cout << "Message sent: " << std::endl; + } catch (std::exception& e) { + std::cerr << "Exception: " << e.what() << std::endl; + } + + return 0; +} diff --git a/tests/server/main.cpp b/tests/server/main.cpp new file mode 100644 index 0000000..008a94c --- /dev/null +++ b/tests/server/main.cpp @@ -0,0 +1,38 @@ +/* +** EPITECH PROJECT, 2024 +** R-Type-Reborn +** File description: +** No file there , just an epitech header example . +** You can even have multiple lines if you want ! +*/ + +#include "server/Server.hpp" +#include + +int main(int argc, char* argv[]) +{ + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return 1; + } + + const unsigned short TCP_port = static_cast(std::stoi(argv[1])); + const unsigned short UDP_port = static_cast(std::stoi(argv[2])); + + try { + Network::Server server(TCP_port, UDP_port); + server.start([](const std::vector& data, const asio::ip::udp::endpoint& client_endpoint) { + // Handle received data + std::cout << "Received data from client: " << client_endpoint << std::endl; + }); + + + + std::cout << "Server started on TCP port " << TCP_port << " and UDP port " << UDP_port << std::endl; + + } catch (std::exception& e) { + std::cerr << "Exception: " << e.what() << std::endl; + } + + return 0; +}