Skip to content

Commit

Permalink
Merge pull request #10 from Jacquwes/docs
Browse files Browse the repository at this point in the history
Add doxygen documentation
  • Loading branch information
Jacquwes committed Sep 26, 2023
2 parents bdf44fa + 2e096a7 commit ea9eee5
Show file tree
Hide file tree
Showing 19 changed files with 3,138 additions and 84 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/doxygen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
on:
workflow_dispatch:
push:
branches:
- docs

name: Build doxygen documentation
jobs:
build-documentation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Doxygen and Dot
run: sudo apt install doxygen graphviz
- name: Run doxygen
run: doxygen doxyfile
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

docs

# User-specific files
*.rsuser
*.suo
Expand Down
2,862 changes: 2,862 additions & 0 deletions doxyfile

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions server/include/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,47 @@

namespace pine
{
/// @brief A server that accepts connections from clients.
class server
{
friend class server_connection;

public:
/// @brief Construct a server with the given asio context and port.
server(asio::io_context& io_context, uint16_t const& port = 80);

/// @brief Start listening for connections.
void listen();

/// @brief Stop listening for connections.
void stop();

/// @brief Disconnect a client.
/// @param client_id Id of the client to disconnect.
/// @return An asynchronous task completed when the client has been disconnected.
async_task disconnect_client(uint64_t const& client_id);

/// @brief Send a message to a client.
/// @param client Client to send the message to.
/// @param message Message to send.
/// @return An asynchronous task completed when the message has been sent.
async_task message_client(std::shared_ptr<connection> const& client, std::shared_ptr<socket_messages::message> const& message) const;

/// @brief Function called when a client sends a message to the server.
/// @param client Client that sent the message.
/// @param message Message sent by the client.
/// @return An asynchronous task completed when the message has been processed.
async_task on_message(std::shared_ptr<connection> client, std::shared_ptr<socket_messages::message> message);

private:
/// @brief Accept clients.
/// This function waits for clients to connect and creates a server connection for each client.
/// @return An asynchronous task completed when the server has stopped listening.
async_task accept_clients();

/// @brief Delete disconnected clients.
/// This function deletes disconnected clients from the server.
/// @return An asynchronous task completed when the server has stopped listening.
async_task delete_disconnected_clients();

std::condition_variable delete_clients_cv;
Expand Down
18 changes: 18 additions & 0 deletions server/include/server_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,37 @@ namespace pine
{
class server;

/// @brief A connection to a client.
class server_connection : public connection
{
public:
/// @brief Construct a server connection with the given socket and server.
server_connection(asio::ip::tcp::socket& socket, server& server);

/// @brief Start listening for messages from the client.
/// @return An asynchronous task completed when the connection has been closed.
async_task listen();

/// @brief Send an ack message to the client.
/// @param id Id of the message to acknowledge.
/// @return An asynchronous task completed when the message has been sent.
async_task send_ack(snowflake id);

private:
/// @brief Establish a connection with the client.
/// @return An asynchronous operation that returns true if the connection has been established.
async_operation<bool> establish_connection();

/// @brief Validate the connection with the client.
/// @return An asynchronous operation that returns true if the connection has been validated.
async_operation<bool> validate_connection();

/// @brief Check the version of the client.
/// @return An asynchronous operation that returns true if the version of the client is valid.
async_operation<bool> check_version();

/// @brief Identify the client.
/// @return An asynchronous operation that returns true if the client has been identified.
async_operation<bool> identify();

server& server_ref;
Expand Down
22 changes: 22 additions & 0 deletions shared/include/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,44 @@

namespace pine
{
/// @brief A connection base class that is used by the server and the client.
class connection : public std::enable_shared_from_this<connection>
{
public:
/// @brief Construct a connection with the given socket.
connection(asio::ip::tcp::socket& socket);

/// @brief Destroy the connection.
virtual ~connection();

/// @brief Receive a managed message from the connection.
async_operation<std::shared_ptr<socket_messages::message>> receive_message();

/// @brief Send a managed message to the connection.
/// @param message Message to send. The message must inherit from the message class.
/// @return An asynchronous task completed when the message has been sent.
async_task send_message(std::shared_ptr<socket_messages::message> const& message);

/// @brief Receive a raw message to the connection.
/// @param buffer_size Number of bytes to receive.
/// @return An asynchronous operation that returns the received bytes.
async_operation<std::vector<uint8_t>> receive_raw_message(uint64_t const& buffer_size);

/// @brief Send a raw message to the connection.
/// @param buffer Buffer to send.
/// @return An asynchronous task completed when the message has been sent.
async_task send_raw_message(std::vector<uint8_t> const& buffer);

/// @brief Close the connection.
void close();

/// @brief The id of the connection.
snowflake id{};

/// @brief The socket of the connection.
asio::ip::tcp::socket socket;

/// @brief The user data of the connection.
user user_data{};
};
}
23 changes: 15 additions & 8 deletions shared/include/coroutine.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include <stdexcept>
#include <type_traits>

/// @brief Continue execution on a different thread.
/// @param out The thread to continue execution on.
/// @return A coroutine awaitable.
inline auto switch_thread(std::jthread& out)
{
struct awaitable
Expand All @@ -23,6 +26,9 @@ inline auto switch_thread(std::jthread& out)
return awaitable{ &out };
}

/// @brief An awaitable coroutine that returns a value.
/// @tparam T The type of the value to return.
/// @details For more information see: https://en.cppreference.com/w/cpp/language/coroutines
template <typename T>
requires (!std::is_void_v<T>)
struct async_operation
Expand All @@ -39,33 +45,34 @@ struct async_operation

bool await_ready() const { return false; }
void await_suspend(std::coroutine_handle<> h) const { h.resume(); }
auto await_resume() { return _coro.promise()._value; }
auto await_resume() { return _coroutine.promise()._value; }

std::coroutine_handle<promise_type> _coro = nullptr;
std::coroutine_handle<promise_type> _coroutine = nullptr;

async_operation() = default;
explicit async_operation(std::coroutine_handle<promise_type> coro) : _coro(coro) {}
explicit async_operation(std::coroutine_handle<promise_type> coroutine) : _coroutine(coroutine) {}
async_operation(async_operation const&) = delete;
async_operation(async_operation&& other) noexcept
: _coro(other._coro)
: _coroutine(other._coroutine)
{
other._coro = nullptr;
other._coroutine = nullptr;
}
~async_operation()
{
if (_coro.address()) _coro.destroy();
if (_coroutine.address()) _coroutine.destroy();
}

async_operation& operator=(async_operation&& other) noexcept
{
if (&other != this)
{
_coro = other._coro;
other._coro = nullptr;
_coroutine = other._coroutine;
other._coroutine = nullptr;
}
}
};

/// @brief An awaitable coroutine that returns nothing.
struct async_task
{
struct promise_type
Expand Down
109 changes: 58 additions & 51 deletions shared/include/message.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,108 +8,115 @@

namespace pine
{
/// <summary>
/// Minimum length of a chat message. It is used to prevent spam. Equals to 1.
/// </summary>
/// @brief Minimum length of a chat message.
constexpr auto chat_message_min_length = 0x01;

/// <summary>
/// Maximum length of a chat message. It is used to prevent spam. Equals to 2000.
/// </summary>
/// @brief Maximum length of a chat message.
constexpr auto chat_message_max_length = 2000;

/// <summary>
/// Current version of the protocol.
/// </summary>
/// @brief Current version of the protocol.
constexpr auto current_version = 0x2;

/// <summary>
/// Minimum length of a username. Equals to 3.
/// </summary>
/// @brief Minimum length of a username.
constexpr auto username_min_length = 0x03;

/// <summary>
/// Maximum length of a username. Equals to 32.
/// </summary>
/// @brief Maximum length of a username.
constexpr auto username_max_length = 0x20;

namespace socket_messages
{
/// @brief Type of a message.
enum message_type : uint8_t
{
/// @brief Invalid message.
/// @details This message should never be sent.
INVALID_MESSAGE,

/// @brief Acknowledge message.
/// @details This message is sent as a response by the server
/// to each message sent by the client.
ACKNOWLEDGE_MESSAGE,

/// @brief Hello message.
/// @details This message is sent by the client and the server
/// to confirm the connection has been established.
HELLO_MESSAGE,

/// @brief Identify message.
/// @details This message is sent by the client to the server
/// to identify itself.
IDENTIFY_MESSAGE,

/// @brief Keep alive message.
/// @details This message is sent by the client every 5 seconds
/// to keep the connection alive.
KEEP_ALIVE_MESSAGE,

/// @brief Send chat message.
/// @details This message is sent by the client to the server
/// to send a chat message.
SEND_CHAT_MESSAGE,

/// @brief Receive chat message.
/// @details This message is sent by the server to the client
/// to send a chat message.
RECEIVE_CHAT_MESSAGE,

/// @brief Error message.
/// @details This message is sent by the server to the client
/// to send an error message.
ERROR_MESSAGE
};

/// <summary>
/// Represents the header of a message. It contains the type of the message, the size of the body and the id of the message.
/// It is located at the beginning of every message.
/// </summary>
/// @brief Represents a message header.
/// @details The header contains the type of the message,
/// the size of the body and the id of the message.
/// It is meant to be placed at the beginning of a message.
struct message_header
{
/// @brief Constructs an empty and invalid message header.
message_header() = default;

/// <summary>
/// Creates a new message header from a buffer. The buffer must contain at least 13 bytes.
/// The first byte is the type of the message, the next 8 bytes are the size of the body and the last 8 bytes are the id of the message.
/// </summary>
/// <param name="buffer"></param>
/// @brief Constructs a message header from a buffer.
explicit message_header(std::vector<uint8_t> const& buffer);

/// <summary>
/// Loads a message from a buffer. The buffer must contain at least 13 bytes.
/// The first byte is the type of the message, the next 8 bytes are the size of the body and the last 8 bytes are the id of the message.
/// </summary>
/// @brief Load the message header from a buffer.
void parse(std::vector<uint8_t> const& buffer);

/// <summary>
/// Serializes the message header to a buffer.
/// </summary>
/// @brief Serialize the message header to a buffer.
std::vector<uint8_t> serialize() const;


/// @brief Type of the message.
message_type type{};

/// @brief Size of the body of the message.
uint64_t body_size{};

/// @brief Id of the message.
snowflake id{};

/// <summary>
/// The size of the message header in bytes.
/// </summary>
/// @brief Size of the message header.
static uint64_t constexpr size = sizeof(message_type) + sizeof(uint64_t) + sizeof(id.value);
};

/// <summary>
/// Represents a message. It contains a header and a body.
/// </summary>
/// @brief Represents a message.
/// @details A message is composed of a header and a body.
struct message : std::enable_shared_from_this<message>
{
/// @brief Default destructor.
virtual ~message() = default;

/// <summary>
/// Returns the number of bytes of the body.
/// </summary>
/// @brief Size of the body.
virtual uint64_t get_body_size() const;

/// <summary>
/// Loads the body of the message from a buffer.
/// </summary>
/// <returns>True if the body was loaded successfully, false otherwise.</returns>
/// @brief Load the body of the message from a buffer.
/// @return `true` if the body was successfully loaded, `false` otherwise.
virtual bool parse_body(std::vector<uint8_t> const& buffer);

/// <summary>
/// Serializes the body of the message to a buffer.
/// </summary>
/// @brief Serialize the message to a buffer.
virtual std::vector<uint8_t> serialize() const;

/// <summary>
/// Returns the header of the message.
/// </summary>
/// @brief Header of the message.
message_header header{};
};
}
Expand Down
Loading

0 comments on commit ea9eee5

Please sign in to comment.