Skip to content

[Network] Dimension specifications

Morgan L edited this page Nov 2, 2024 · 21 revisions
logo

Specifications

Summary

Introduction

logo

The Dimension library is a UDP network library developed by mlargeot and MaxenceLgt. Initially designed for the RType project, this library is built to be generic and modular, allowing it to integrate network communication with a game engine as an external component and by using scripts.

Dimension operates using packet transfers, with packets that can be created and modified as needed. The library also provides a default list of packets to streamline development. Additionally, it supports Room management, enabling multiple game instances (multi-instance) within the same game environment. It provides essential features for server-client communication, including:

  • Establishing and managing server-client connections
  • Transferring data across the network.

Communication Protocol

In multiplayer gaming and other real-time applications, network communication must be both fast and efficient to maintain a smooth and responsive user experience. Choosing UDP as the communication protocol offers clear advantages by minimizing latency and reducing overhead from built-in reliability management. Originally developed for a specific project, this network library was designed to be independent, making it suitable for integration into various real-time game engines.

Why UDP is Ideal for Real-Time Network Communication

  1. Minimal Latency and Enhanced Responsiveness: UDP enables data transmission without enforcing strict ordering or delivery verification, which avoids delays caused by retransmissions, common in TCP. This delay-free exchange is essential in real-time applications, where even slight lag can impact user experience and gameplay responsiveness.

  2. Simplicity and Flexibility: By not handling guaranteed delivery or ordering as TCP does, UDP simplifies data flow management, enabling higher data throughput, which is especially valuable in data-heavy environments like gaming.

Addressing UDP Limitations with Optional Acknowledgments

While UDP doesn’t provide built-in delivery guarantees, applications that require reliability for critical data can implement an optional acknowledgment system. This approach would add packet identifiers and confirmation responses to track essential data delivery without compromising overall performance. Adding this functionality can offer:

  • Selective Reliability: By targeting only critical packets for acknowledgment, this mechanism would limit retransmissions to essential data, maintaining the continuity of important information while keeping communication lightweight.

  • Flexible Implementation: This acknowledgment system can be enabled or adjusted based on the specific needs of the game engine or application, allowing adaptability according to tolerance for data loss.

Conclusion

By using UDP as the core protocol, this network library maximizes the speed of data exchanges in real-time applications while offering the potential for targeted reliability mechanisms, such as acknowledgments, to offset UDP’s limitations. This combination makes the library ideal for scenarios where performance is paramount while providing the flexibility to meet the unique needs of real-time gaming and other applications.

Network library

In our library, network communication between the server and client is a crucial aspect that must be both optimized and robust. One of the primary constraints is the requirement to use UDP communication between the server and client, as the library aims to integrate into the RType project. After researching potential C++ network libraries that support UDP, we initially considered RakNet, a cross-platform C++ library that provides TCP-like reliability features. However, due to project requirements and after discussions with the educational team, we decided to use Asio for this library (see Asio documentation).

Choice of Asio Library

Asio was selected for the following reasons:

  1. Project Requirements: While the RType requires the use of UDP, we are aware of scenarios where TCP might be practical, and Asio offers flexibility to implement both protocols. It’s important to note that implementing TCP may require thorough justification, so it will only be used if absolutely necessary.
  2. Cross-Platform Compatibility: Like RakNet, Asio is cross-platform, supporting Windows, macOS, and Linux, which aligns with our project's goal of being cross-platform.
  3. Lightweight and Efficient: Compared to other libraries like Boost.Asio, Asio provides the same core functionality in a more lightweight form, without the additional dependencies that can sometimes lead to performance overhead. Asio can fulfill our network requirements with reduced bloat, making it ideal for performance-sensitive applications such as real-time games.

Comparative Analysis: Asio vs. Boost.Asio

Similarities

  • Core Functionality: Both libraries offer a robust foundation for asynchronous networking, with support for TCP and UDP protocols.
  • Scalability and Flexibility: Both are designed with scalability in mind, making them suitable for applications that need to handle many concurrent connections, such as multiplayer games.

Differences

  • Dependencies: Boost.Asio is a part of the larger Boost library, which requires incorporating a larger set of dependencies into the project. Asio, on the other hand, is available as a standalone library, making it a lighter option and better suited for projects where minimizing dependencies is a priority.
  • Overhead: Boost.Asio can introduce additional overhead due to its integration with the broader Boost ecosystem. In contrast, Asio provides the core networking functionality we need with less overhead, which is ideal for real-time applications where performance is critical.

Conclusion on Asio vs. Boost.Asio

While Boost.Asio is feature-rich, Asio provides the exact functionality needed for this project without the additional weight of the entire Boost library. Asio’s leaner structure allows us to focus on optimizing our network code for the specific needs of a multiplayer gaming environment, particularly when UDP is mandated.

Conclusion

In summary, Asio was chosen for its lightweight design, support for both UDP and TCP, and ability to meet our project's cross-platform requirements. We will proceed with UDP as the primary protocol, carefully evaluating and justifying any potential inclusion of TCP based on specific project needs.

Architecture

---
title: Dimension Architecture
---
flowchart TD
    A[dimension] --> B[endpoint]
    A --> C[packet]
    A --> D[IdFactory.cpp//hpp]
    B --> E[IEndpoint.hpp]
    E --> F[AEndpoint.cpp//hpp]
    F --> G[client]
    F --> H[server]
    F --> I[room]
    G --> J[Client.cpp//hpp]
    H --> K[AServer.cpp//hpp]
    I --> L[Room.cpp//hpp]
    C --> M[PacketFactory.cpp//hpp]
    C --> N[PacketManager.cpp//hpp]
    C --> O[APacket.cpp//hpp]
    O --> P[basics]
    P --> Q[HiServer
            HiClient
            AdiosServer
            Ping
            ...]
Loading

IdFactory

In an ECS (Entity Component System), entity IDs may not necessarily be shared between the server and client, as the client might have graphical entities that the server does not manage.

To address this, network IDs can be implemented. The IdFactory facilitates the creation and assignment of these network IDs, ensuring proper management across the network.

Packet

PacketManager

The PacketManager is responsible for creating and managing different packet types, each derived from APacket, in the Dimension library. By utilizing both empty and deserialized packet instantiation, it enables efficient and flexible packet management for networking.

Key Features

  1. Packet Registration: Using registerPacket, new packet types are registered and assigned unique type IDs. This enables the factory to dynamically instantiate the right packet type when needed, with strict checks ensuring each type derives from APacket.

  2. Packet Creation for Sending: createPacketToSend creates an empty instance of a specified packet type. This method supports data preparation before transmission by dynamically retrieving the packet type through its type index.

  3. Deserialization of Packets: createPacketFromBuffer takes a serialized buffer, extracts the packet type, and instantiates the correct packet by calling the appropriate constructor. Exception handling ensures robustness, with errors raised for incomplete or malformed data.

  4. Type Management: getIndexFromType and getTypeFromIndex manage the mapping between packet types and indices, enabling easy packet identification and validation across different packet classes.

PacketFactory

The PacketFactory is a core class in the Dimension library, designed to centrally create and manage various packet types using a Factory pattern. This enables dynamic creation of packets, whether empty or initialized from received data buffers.

Key Features

  1. Packet Creation from Buffer: createPacketFromBuffer converts a serialized buffer into a usable packet, with exception handling for invalid data.

  2. Creation of Empty Packets: createEmptyPacket generates packet instances ready to be sent, without initial data.

  3. Packet Type Management: Methods getIndexFromType and getTypeFromIndex allow mapping of packet types, simplifying identification and management.

Packet Registration

Upon initialization, PacketFactory registers various packet types (HiServer, CreateEntity, etc.) with the PacketManager, making each type immediately accessible without further changes.

APacket

APacket is the abstract containing each packet common methods & attributes. All packet must inherit from APacket to be implemented and used correctly

APacket structure:

#pragma once

#include <chrono>
#include <cstdint>
#include <cstring>
#include <exception>
#include <string>
#include <vector>

namespace dimension
{
class APacket
{
   public:
    APacket(uint8_t type);
    APacket(std::vector<char> &buffer);
    virtual ~APacket() = default;

    static uint32_t getPacketSizeFromBuffer(std::vector<char> &buffer);
    static uint8_t getPacketTypeFromBuffer(std::vector<char> &buffer);

   public:
    std::vector<char> serialize() const;
    std::vector<char> serializeHeader() const;

    virtual std::vector<char> serializeData() const = 0;

   public:
    uint32_t getPacketSize() const;
    uint8_t getPacketType() const;
    uint64_t getPacketTimeStamp() const;
    uint32_t getHeaderSize() const;

   protected:
    uint32_t _packetSize;
    uint8_t _packetType;
    uint64_t _packetTimeStamp;
    uint16_t _packetDataSize;
};
}  // namespace dimension

Basics

Basics directory contains all default packet provided by the Dimension library inheriting from APacket.

Endpoint

The Endpoint directory contains the main components handling network communication between the server, client, and rooms. These components share a common communication structure, managed through AEndpoint, which inherits from the IEndpoint interface. This abstract base class centralizes essential network operations, such as sending and receiving packets, to ensure a consistent approach across different endpoints.

Key Features

  1. Packet Transmission (send):
    The send method is responsible for transmitting packets to a specified endpoint.

    • Packets are serialized before transmission, with error handling for any serialization issues.
    • If the packet is new, it’s added to a validation list to enable retries in case of delivery failure.
    • Asynchronous sending via ASIO allows non-blocking operations, with error logging for any issues encountered during transmission.
  2. Packet Reception (receive):
    The receive method listens for incoming data, managing asynchronous data retrieval.

    • On packet reception, handleDataReceived processes the data, identifying its type and determining the correct handling.
    • Received packets are pushed into a queue, allowing further processing by other components.
  3. Packet Handling (handleDataReceived):
    When data arrives, it’s deserialized into a packet object through the PacketFactory.

    • Packet validation packets (e.g., acknowledgments) are processed directly by deleteFromValidationList to update the validation status.
    • Other packets are enqueued for handling, with their types logged for monitoring purposes.
  4. Validation List and Resending Mechanism:

    • The resendValidationList method retries sending packets in the validation list if they haven’t been acknowledged within a specified timeframe.
    • This ensures reliable packet delivery by resending any packet that has not received confirmation.
  5. Packet Queue Management:

    • The popReceiveQueue and getRcvQueue methods allow other parts of the program to access and manage received packets, ensuring efficient processing of incoming data.

Previous page: Dimension Documentation
Next page: Protocol

Clone this wiki locally