R-Type

Description

This project is a recreation of the R-Type game using our own custom game engine. Inspired by the classic Shoot'em'up genre, players take control of a spaceship to combat waves of enemies and challenging bosses.

You can check the online documentation to this url : "https://pizza-aux-crevettes.github.io/R-Type/clang-format.html"


Table of contents


Prerequisites

Before getting started, make sure you have the following installed on your machine:

  • A C++ compiler supporting C++17
  1. Clone the repository:

    git clone --recurse-submodules git@github.com:EpitechPromo2027/B-CCP-500-TLS-5-2-rtype-anastasia.bouby.git
    cd B-CCP-500-TLS-5-2-rtype-anastasia.bouby
    
  2. Install UDev library (if necessary)

    On ubuntu:

    sudo apt install libxrandr-dev libxcursor-dev libudev-dev libopenal-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev libcriterion-dev libfreetype-dev libfreetype6 libfreetype6-dev git gcc g++ make cmake
    

    On fedora:

    sudo dnf install freetype-devel libglvnd-opengl libXrandr-devel libXcursor-devel xrandr freetype glew libjpeg-turbo libsndfile openal-soft libvorbis-devel flac-devel libX11-devel libGL-devel systemd-devel openal-soft-devel git gcc g++ make cmake
    
  3. Build the project:

    cmake -B build .
    cd build/
    make
    

Run the game

  1. Start the Server:

    Make sure to be in B-CCP-500-TLS-5-2-rtype-anastasia.bouby/

    ./build/server/Server
    
  2. Start the Client:

    Open a new terminal and make sure to be in B-CCP-500-TLS-5-2-rtype-anastasia.bouby/

    ./build/client/Client
    

Project structure

assets/: Game resources such as textures, sounds.

client/: Contains the client-side game logic and graphics.

external/: External library needed.

game_engine/: Handles server-side game_engine.

server/: Handles server-side networking.

User Guide

Welcome to the user guide for our game R-Type.
This guide will help you understand how to play and use the various options in the game. To learn how to install and launch our game, please click here.

Once the game is launched, you will be on the main menu. This menu consists of three buttons:

  • [PLAY]: Starts the game.
  • [OPTIONS]: Opens the options menu.
  • [EXIT]: Exits the game.

[OPTIONS] Menu

In the [OPTIONS] menu, you will find several settings to customize:

  • At the top of the screen, you can adjust the sound settings for the game:

    • The first slider allows you to adjust the volume of the sound effects (e.g., mouse click sounds, player shots during the game).
    • The second slider allows you to adjust the volume of the overall game music (whether in the menu or during gameplay). Drag the slider to adjust the volumes.
  • Below that, you will find five buttons to change the control keys. By default:

    • The Up Arrow moves the player up.
    • The Down Arrow moves the player down.
    • The Right Arrow moves the player right.
    • The Left Arrow moves the player left.
    • The Enter key toggles the autofire mode (the player shoots automatically).
    • The Spacebar allows you to shoot when autofire is disabled.

    To change a key, click the corresponding button for the action, then press the new key on your keyboard (for example, if you want the "Shoot" action to be the "L" key, click the "Shoot" button and then press the "L" key).

  • Below, you can check or uncheck an option to enable or disable a font change for people with reading difficulties.

  • There is also a third slider that allows you to adjust the text size in the game.

To exit this menu and return to the main menu, click the "Back" button in the top-right corner of the screen. Your settings will be saved automatically.

Game Settings and IP Field

On the main menu, you will also find two text fields:

  1. The first field allows you to enter your player name. If you leave it blank and click [PLAY], your default username will be "Guest".

  2. The second field allows you to enter the IP address of another computer to play multiplayer. Simply get the IP address of the PC running the server and enter it here. If this field is left empty, the default IP address will be your own PC's address (127.0.0.1). This feature is useful if you want to play multiplayer across different PCs.

Once you've filled out this information, you're ready to start the game by clicking [PLAY]!

In-Game

Once in the game, you can move by using the arrow keys on your keyboard or the keys you configured in the game options.
To shoot once, press the Spacebar (or the key you configured in the options).
To activate autofire, press Enter (or the configured key), and press it again to deactivate it.

Enemies and the Boss

During the game, you will encounter up to 4 types of enemies, each with different characteristics (some have more health or deal more damage than others, for example).
At the end of the level, you will face a boss, recognizable by its massive size. To win, you must defeat the boss, but be careful not to get eliminated before that!

Map Creation

You also have the option to create your own maps. Click here and follow the guide to generate a new map.


We hope you have a great time playing our game!

Development Team:
Anastasia Bouby, Elyne Masse, Eddy Gardes, Alexandre Chouteau, Benjamin Gayaud.

Map Editor

Description

This is a map generator using the Tkinter library to create a graphical user interface (GUI). The application allows users to generate their own maps with various configurations and options.


Table of contents


Prerequisites

Before you begin, ensure that you have the following installed:

  • Python 3.x (recommended version: 3.8 or higher)
  • Tkinter (comes pre-installed with Python)
  • pip (Python package manager)
  • pillow (recommended version: 8.0.0 or higher)
  • Tkmacos (only for macOS)

Check Python and Tkinter Installation

To verify if Python and Tkinter are installed, run the following commands in your terminal:

python3 --version
python3 -m tkinter

macOS Installation Guide

Step 1: Install Python (if necessary)

If Python is not installed, or if you want to use a different version, you can install it via Homebrew:

  1. Install Homebrew (if not already installed):

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    
  2. Install Python using Homebrew:

    brew install python
    

Step 2: Create and Activate a Virtual Environment

  1. Create a virtual environment:

    python3 -m venv map_editor_env
    
  2. Activate the virtual environment:

    source map_editor_env/bin/activate
    

Step 3: Install Tkinter (if necessary)

Tkinter usually comes pre-installed with Python on macOS. If it is not installed, you can install it with:

  brew install python-tk

Step 4: Install pip (if necessary)

If you encounter the error zsh: command not found: pip, install pip manually:

  1. Download the get-pip.py script:

    curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
    
  2. Install pip using Python:

    python3 get-pip.py
    
  3. Verify that pip is installed:

    pip3 --version
    

Step 5: Install Tkmacos (if necessary)

Inside the activated virtual environment, run:

  pip3 install tkmacosx

Step 6: Install Pillow

  pip install Pillow

Step 7: Run the Application

  cd map-editor/macos
  python3 map_editor

Linux Installation Guide (Ubuntu)

Step 1: Install Python (if necessary)

On most Linux distributions, Python is pre-installed. To check the installed version:

  python3 --version

If it's not installed, you can install it using the package manager for your distribution.

  sudo apt update
  sudo apt install python3

Step 2: Install Tkinter (if necessary)

Tkinter is usually included with Python. If you need to install it, use the following command :

  sudo apt install python3-tk

Step 3: Install pip (if necessary)

To install pip if it’s not already installed:

  sudo apt install python3-pip

Verify that pip is installed:

  pip3 --version

Step 4: Install Pillow

  pip install Pillow

Step 5: Run the Application

  cd map-editor/linux
  python3 map_editor

Once the map editor is launched, you can place your enemies and obstacles as you wish. Make sure to save your work in our project at this path: /server/maps/, and name your file “map1.map”.

Commit Standard for R-Type

This document outlines the commit standard applied on R-Type project. This convention ensures clear and structured messages for better readability and code management.


Commit Message Structure

Each commit message must follow this format:

<TYPE>: <DESCRIPTION>
  • TYPE: The type of change, written in uppercase.
  • DESCRIPTION: A concise and precise description of the modification.

Main Types

TypeDescription
ADDAdding a new feature
FIXFixing a bug
DOCSModifying or adding documentation
STYLEChanges related to formatting (e.g., spaces) without functional impact
REFACTORRefactoring code without adding features or fixing bugs
TESTAdding or updating tests
RMDelete one thing

Example Messages

  • Adding a feature:

    ADD: collision system for missiles
    
  • Fixing a bug:

    FIX: Fixed crash when moving player
    
  • Updating documentation:

    DOCS: Updated installation instructions
    
  • Improving style:

    STYLE: Applying clang-format to source files
    

Additional Rules

  • Language: All commit messages must be written in English.
  • Length: Descriptions should fit in a single line (around 72 characters).
  • Conciseness: Avoid unnecessary details. Detailed explanations must be added in an extended description.

Extended Format

For complex commits, you must add an extended description after a blank line:

<TYPE>: <DESCRIPTION>

<EXTENDED DESCRIPTION>

Example:

FIX: Fixed poorly rendered borders on enemies

Borders were misaligned due to a problem in the method
render() of the Enemy object. The function has been fixed to handle sizes.

Merging the code

To participate in the project, you must create GitHub branches. It is not possible to push directly to the main or dev branches. Your new branch must be based on the dev branch. Once you want to share your work, you need to create a pull request from your branch to the dev branch. This must be accepted by one of the project creators. Once the pull request is accepted, you can then merge your work. During regular project updates, the dev branch is merged into the main branch.

clang-format for R-Type

This document explains how to configure and use clang-format to ensure consistent coding standards in the R-Type project.


Why use clang-format?

  • Ensures a uniform coding style for all contributors.
  • Compatible with most IDEs (Visual Studio Code, CLion, etc.).
  • Automates code formatting to focus on development.

Installing clang-format

Linux

  1. Install clang-format using your package manager:
    sudo apt install clang-format
    

MacOS

  1. Install clang-format using Homebrew:
    brew install clang-format
    

Verification

  1. Make sure clang-format is installed correctly:
    clang-format --version
    

Configuration with .clang-format

Add the following .clang-format file to the root of the R-Type project to define the style rules.

BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 100
BreakBeforeBraces: Allman
AllowShortIfStatementsOnASingleLine: false
AlwaysBreakTemplateDeclarations: true
SortIncludes: true
IncludeBlocks: Preserve
SpacesInParentheses: false

Using clang-format

Manual

  1. Format a specific file:

    clang-format -i path/to/file.cpp
    
  2. Format the entire project:

    find . -name '*.cpp' -o -name '*.h' | xargs clang-format -i
    

With IDEs

VS Code

  1. Install the C/C++ and Clang-Format extensions.
  2. Add the following to settings.json:
    {
        "C_Cpp.clang_format_style": "file",
        "editor.formatOnSave": true
    }
    

CLion

  1. Navigate to File > Settings > Editor > Code Style > C/C++.
  2. Enable Use clang-format file.

CI/CD Integration

Add a step in your CI pipeline to validate formatting:

- name: clang-format check
  run: |
    find . -name '*.cpp' -o -name '*.h' | xargs clang-format --dry-run --Werror

Essential Commands

ActionCommand
Format a fileclang-format -i file.cpp
Format all files`find . -name '.cpp' -o -name '.h'
Verify without modifyingclang-format --dry-run file.cpp

References


Accessibility

We designed the game with a strong focus on accessibility to ensure an inclusive experience for all players. Here are the key features available:

Visual Options

  • Colorblind-friendly palettes: An optimized color palette is available to help players with colorblindness better distinguish game elements.
  • High contrast mode: Enhances visibility of enemies, projectiles, and game objects by improving the differentiation of elements.

Interaction Options

  • Customizable controls: Players can remap keys according to their preferences.
  • “Auto-Fire” mode: Enables automatic shooting to reduce the need for holding down buttons.

Audio Options

  • Volume settings: Adjust the volume of music, sound effects, and dialogue independently.

Interface Options

  • HUD and text size adjustments: Allows resizing of UI elements for improved readability.

Progression Options

  • Frequent checkpoints: Automatic saving and strategically placed checkpoints prevent the need to replay long sections.
  • Multiple difficulty levels: You can create your own map and adjust the difficulty level yourself.

Comparison: ECS (Entity-Component-System) vs COI (Composition-Over-Inheritance)

This document compares two fundamental approaches used in game engines: ECS (Entity-Component-System) and COI (Composition-Over-Inheritance). These paradigms shape how entities, data, and logic are structured in a project.


ECS (Entity-Component-System)

Description:

  • Entities are simple identifiers.
  • Components store only data.
  • Systems contain business logic and manipulate the associated components.

Advantages:

  • Modularity: Highly flexible, components can be dynamically added or removed.
  • Performance: Optimized for multicore processors through parallelization.
  • Clear separation: Data and logic are well isolated.

Disadvantages:

  • Higher initial complexity.
  • Performance management requires special attention (e.g., frequent memory allocations).

Use Cases:

  • AAA engines like Unity and Unreal Engine.
  • Games requiring the simultaneous management of thousands of entities.

References:


COI (Composition-Over-Inheritance)

Description:

  • Prefers object aggregation (objects containing other objects) over inheritance.
  • Features are divided into reusable and composable components.

Advantages:

  • Increased flexibility: Components can be added or removed without modifying entities.
  • Independence: Each behavior is autonomous and replaceable.
  • Avoids deep hierarchies: Reduces rigid dependencies.

Disadvantages:

  • May require more code to explicitly manage components.
  • Less performant and more scattered than ECS for projects with numerous entities.

Use Cases:

  • Frameworks like Godot (using combinable Nodes).
  • Medium-sized projects or those needing flexible behavior.

Comparison: ECS vs COI

AspectECSCOI
DescriptionSeparate identifiers, data, and systems.Independent components combined in entities.
Entity structureSimple identifiers linked to components.Entities directly containing their components.
DataData containers without logic.Contain both data and behaviors.
LogicCentralized in reusable systems.Encapsulated locally in each component.
ModularityDynamic: components easily added/removed.Directly integrated into the entity.
PerformanceExcellent (contiguous storage, cache-friendly).Lower performance (frequent cache misses).
ComplexityMore complex to set up and understand.Simpler for medium-sized projects.
ExamplesUnity, Unreal Engine.Godot, flexible medium-sized projects.

Recommendation

  • ECS: Ideal for complex games with thousands of entities to manage simultaneously.
  • COI: Perfect for medium-sized projects where readability and flexibility are priorities.

Comparison of Graphics Libraries for R-Type

This document compares three major graphics libraries: Raylib, SFML, and SDL, based on their usability, performance, features, and documentation. It also explains why SFML was chosen for the R-Type project.


Library Comparison

CriteriaRaylibSFMLSDL
UsabilityIntuitiveSimple and well-knownComplex but flexible
PerformanceGood for medium-sized projectsOptimized for 2DExcellent performance
Features2D/3D games, audio, input handling, basic UI2D games and applications, audio, networking, text rendering2D games, advanced platform handling
DocumentationVery well-documented with many examplesGood documentation and tutorialsGood but more technical
PlatformsWindows, macOS, Linux, Web, Android, iOSWindows, macOS, LinuxWindows, macOS, Linux, Android

Why We Chose SFML

We chose to use SFML for the following reasons:

  1. Adequate performance: SFML is optimized for 2D games like R-Type and provides performance suitable for the project’s needs.
  2. Ease of use: Its simplicity allows faster development and debugging, which is crucial for meeting project deadlines.
  3. Past experience: We have previously worked with SFML during our studies, reducing the learning curve.
  4. Quality documentation: SFML offers clear documentation along with practical tutorials, speeding up development.

References

Comparison of Programming Languages for R-Type Development

To undertake an ambitious project like R-Type, it is crucial to choose a programming language that meets the technical requirements of the game and the project’s objectives. Key criteria include performance, resource management, flexibility for working with graphics and network libraries, and ease of development.


1. C++

Advantages:

  • Optimal execution speed: C++ compiles directly to machine code, offering unmatched performance ideal for real-time games requiring high fluidity and immediate responsiveness.
  • Fine memory management: With pointers and modern tools like std::unique_ptr, C++ allows efficient resource management, essential in a game where objects (sprites, sounds, etc.) are frequently manipulated.
  • Mature ecosystem: Libraries like SFML or OpenGL for graphics provide powerful solutions.

Disadvantages:

  • Complexity: C++ can be harder to learn and master, particularly for manual memory management.
  • Development time: Compared to more modern languages, C++ development may take longer, though this is offset by control and performance.

For R-Type: C++ is particularly suited for games requiring fine-tuned performance and resource management, both critical for a project like R-Type.


2. C#

Advantages:

  • Clear and modern syntax: C# offers great code readability, reducing the time needed to implement features.
  • Integration with Unity: Paired with Unity, C# enables rapid development thanks to integrated tools for graphics, animations, and physics.
  • Automatic memory management: C# simplifies resource handling, avoiding common errors like memory leaks.

Disadvantages:

  • Lower performance: While performant, C# relies on the CLR (Common Language Runtime), introducing slight latency.
  • Dependency on frameworks: C#’s full potential often relies on tools like Unity, which can limit customization options.

For R-Type: C# is ideal for rapid development, but it may reach its limits in terms of performance for a game requiring ultra-optimized execution.


3. Python

Advantages:

  • Ease of use: Python is renowned for its simplicity, making it ideal for quickly testing gameplay concepts.
  • Accessible ecosystem: Libraries like Pygame allow effortless creation of 2D games.

Disadvantages:

  • Limited performance: Python is an interpreted language, making it unsuitable for games requiring intensive calculations or real-time responsiveness.
  • Abstract memory management: Garbage collection can cause unpredictable slowdowns.

For R-Type: Python is a good choice for prototyping or simple games, but it is not suitable for a final version requiring consistent performance.


4. Rust

Advantages:

  • Performance comparable to C++: Rust compiles to native code, offering similar speed.
  • Memory safety: Rust’s memory management system eliminates memory-related bugs while ensuring high performance.
  • Modern syntax: More intuitive than C++, Rust combines safety and expressiveness.

Disadvantages:

  • Steep learning curve: Rust takes time to fully grasp.
  • Limited ecosystem: Rust has fewer libraries dedicated to game development compared to C++.

For R-Type: Rust is an interesting option for a project focused on safety and performance, but its young ecosystem can complicate development.


5. Java

Advantages:

  • High portability: Thanks to the Java Virtual Machine, Java games can run on many platforms without modification.
  • Ease of development: Java is easy to learn and offers automatic memory management.

Disadvantages:

  • Lower performance: The JVM introduces latency, making Java less performant for complex real-time games.
  • Limited graphics libraries: While solutions like LWJGL exist, they are less powerful than those available in C++.

For R-Type: Java can be suitable for a basic 2D game, but its performance limitations and less rich ecosystem make it less fitting for a demanding project like R-Type.


Conclusion

To recreate R-Type, a game demanding fluidity, optimization, and precise resource management, C++ emerges as the most suitable choice. Its combination of raw performance, flexibility, and a rich ecosystem places it above other options. While C# and Rust offer compelling alternatives, they cannot match the level of control and efficiency that C++ provides.

Comparison of Networking Methods for R-Type

This document provides a direct comparison of TCP, UDP, and a hybrid TCP+UDP approach, tailored for the R-Type multiplayer game project. Each method is evaluated based on performance, reliability, and suitability for a networked gaming environment.


Comparison Criteria

1. Latency

  • TCP: High latency due to retransmission and sequencing mechanisms. Packet loss causes additional delays.
  • UDP: Low latency, packets are sent immediately without acknowledgment.
  • TCP+UDP: Balanced latency, with TCP handling critical data and UDP managing frequent updates.

2. Reliability

  • TCP: Very reliable, ensures all packets arrive in the correct order.
  • UDP: Less reliable, packets may be lost, duplicated, or arrive out of order.
  • TCP+UDP: Modulated reliability. TCP ensures reliability for critical communications, while UDP tolerates losses in non-critical updates.

3. Implementation Complexity

  • TCP: Easy to implement; retransmission and sequencing are handled by network libraries.
  • UDP: More complex; requires custom mechanisms to manage packet loss, latency, and synchronization.
  • TCP+UDP: High complexity; developers must integrate and synchronize both protocols.

4. Suitability for Fast-Paced Games

  • TCP: Poor, delays from retransmissions affect responsiveness.
  • UDP: Excellent, ideal for frequent updates with low latency.
  • TCP+UDP: Good, each protocol is used according to its strengths.

Comparison Table

CriteriaTCPUDPHybrid (TCP+UDP)
LatencyHighLowMedium
ReliabilityVery goodModerateVery good
ComplexityLowHighHigh
Suitable for fast gamesNoYesYes

Recommendation for R-Type

For a fast-paced game like R-Type, the hybrid TCP+UDP approach is highly recommended:

  • TCP: Used for critical data such as connection management, initial synchronization, and important game events.
  • UDP: Used for real-time updates of entities, movements, shots, and other elements requiring low latency.

This approach ensures a balance between reliability and responsiveness, crucial for a smooth gaming experience.


References and Tools

References


Client

The Client class manages the overall operation of the client.

It orchestrates the various components required for the game’s operation, from initializing the server to managing the entities to be displayed.

FunctionDescription
runNetworkClientThis function starts the server.
initializeNetworkThis function handles the server initialization.
manageBackgroundThis function simulates the game background being in motion. In reality, it replicates the given image and scrolls it.
manageSoundThis function setup the sound of the game.
processEventsThis is the function containing the loop that handles all game events (mouse clicks, keyboard key presses, etc.).
handleAutoFireThis function handles the auto-fire event when the corresponding key is pressed.
initializeServerThe initializeServer function establishes the network connection with the server (TCP/UDP) and initializes the required resources.
updateGameStateThis function calls the GameEngine and requests it to display all the entities contained in a list.
manageClientThe manageClient function is the main loop of the client. It initializes the game window, the necessary components (sound, network, menus, etc.), and manages the game lifecycle, including rendering, user events, state management (menu, gameplay, victory, defeat), and communication with the server. It runs as long as the window remains open.

Entity Manager

The EntityManager class manages the creation, deletion, and updating of entities.

FunctionsDescription
CompareEntitiesThis function compares the entity IDs with the list of already existing entities. If the ID exists, the entity should be updated; if it does not exist, a new entity should be created.
CreateEntityThis function creates the entities through the game engine based on the requested parameters.
setPlayerColorIt allows targeting the correct sprite color on an image (to apply setTextureRect() function) based on the requested entity.
setEnemyIt allows targeting the correct enemy sprite on an image (to apply setTextureRect() function) based on the requested entity.
manageBackgroundThe function that creates the background entity.
winGameThe function checks if the player has won by verifying if the boss’s health is 0 or not.
loseGameThe function checks if the player has lost by verifying if his health is 0 or not.

LifeBar

The LifeBar class manages the health bar functionality for a player, including creating health-related UI elements, updating health points, and rendering the life bar on the screen. It uses a singleton pattern to ensure a single instance is accessible globally.

FunctionsDescription
createEntityTextCreates a text entity with specified attributes (text, position, font size) and adjusts its color based on health points.
displayLifeBarDisplays the life bar with updated health points, rendering the entities to the window.
manageHealthUpdates the health points if the provided entity ID matches the player ID.

HotkeyManager

The HotkeysManager class handles hotkey-to-keyboard mappings, key reassignment, and autofire state. It processes input events, triggers actions, and sends hotkey data over the network, using a singleton design.s

FunctionsDescription
getAutoFireStateReturns the current state of the autofire feature (enabled or disabled).
checkKeyChecks if a triggered key event matches a registered hotkey and sends a network packet for the corresponding action.
isKeyUsedDetermines if a specific keyboard key is currently mapped to a hotkey.
isAutoFireToggles the autofire state when the Enter hotkey is pressed.
keyToStringConverts a keyboard key to its corresponding string representation for display or debugging purposes.

Menu

The Menu class manages the user interface for the game’s menu system, including buttons, input fields, and sprites. It handles the creation and rendering of menu entities, input handling, and transitions between different menu states.

FunctionsDescription
createEntityButtonCreates a button entity with a title, font, size, position, and a callback function to be triggered on click.
createEntitySpriteCreates a sprite entity with given size, texture, texture rectangle, and position.
createEntityRectCreates a rectangular button entity with size, position, color, and a callback function to be triggered on click.
createEntityInputCreates an input field entity with a font, size, position, and default text value for user input.
isClickedInputUpdates the state of whether the input fields (IP, port, or username) have been clicked.
setupInputHandles text input events to update the IP, port, or username fields based on user input.
initMainMenuInitializes and displays the main menu, creating and positioning all necessary UI elements.
displayMenuDisplays the appropriate menu (Main or Options) based on the current menu state, calling the relevant display functions.

OptionMenu

The OptionMenu class is responsible for managing the options menu in a game.

FunctionsDescription
createEntityTextCreates an entity containing text with a specified font, size, and position.
createEntityOptionButtonCreates an option button with a callback, position, and predefined size.
createEntityButtonCreates a standard button with a title, font, size, position, and callback for the action.
createEntitySliderCreates a slider with a value range and a callback to adjust volume or element size.
createEntityRectCreates an entity with a rectangle (button) of a defined size and an interaction callback.
createEntitySpriteCreates an entity with an image (sprite) and applies a texture at the specified position.
setNewKeyHandles changing a keyboard shortcut based on a key press event and updates the associated key.
displayOptionMenuInitializes and displays the options menu, adjusting interface elements based on window size.

WinMenu

The WinMenu class handles the creation and display of the lose menu in the game, where the player can view the game over screen and exit the game. It provides methods to create various entities such as buttons, text, sprites, and rectangles, and arranges them on the screen in a responsive manner. The class also includes a method to handle user interaction, specifically when the Exit button is clicked, closing the game window.

FunctionsDescription
createEntityButtonCreates and returns a button entity with specified properties, including a callback function for interaction.
createEntityTextCreates and returns a text entity with specified text, position, and font size, applying different colors based on text.
createEntitySpriteCreates and returns a sprite entity with specified size, texture, texture rectangle, and position.
createEntityRectCreates and returns a rectangle entity, which is typically used as a button, with a color and an interaction callback.
isClickedExitCloses the window when the “Exit” button is clicked.
displayWinMenuInitializes and renders all entities for the Win menu, including sprites, text, and buttons, with responsive positioning based on window size.

LoseMenu

The LoseMenu class handles the creation and display of the lose menu in the game, where the player can view the game over screen and exit the game. It provides methods to create various entities such as buttons, text, sprites, and rectangles, and arranges them on the screen in a responsive manner. The class also includes a method to handle user interaction, specifically when the Exit button is clicked, closing the game window.

FunctionsDescription
createEntityButtonCreates and returns a button entity with specified properties, including a callback function for interaction.
createEntityTextCreates and returns a text entity with specified text, position, and font size, applying different colors based on text.
createEntitySpriteCreates and returns a sprite entity with specified size, texture, texture rectangle, and position.
createEntityRectCreates and returns a rectangle entity, which is typically used as a button, with a color and an interaction callback.
isClickedExitCloses the window when the “Exit” button is clicked.
displayLoseMenuInitializes and renders all entities for the Win menu, including sprites, text, and buttons, with responsive positioning based on window size.

NetworkClient

The NetworkClient class manages network communication for the client, handling both TCP and UDP connections. It initializes and connects the sockets, and runs two separate threads to handle incoming messages for TCP and UDP protocols. It uses the SmartBuffer to send and receive messages, and relies on the Protocol class to handle the messages based on their operation codes. Errors during communication are logged using the Logger class.

FunctionsDescription
initInitializes both the TCP and UDP sockets by calling their init() methods.
connectTCPEstablishes a TCP connection to the server and logs the connection status.
connectUDPInitializes UDP communication and sends a default message to the server.
runCreates and runs two threads to handle TCP and UDP messages concurrently.
handleTcpMessagesContinuously receives and processes TCP messages in a loop, handling any exceptions with error logging.
handleUdpMessagesContinuously receives and processes UDP messages in a loop, handling exceptions by logging errors.

Protocol

The Protocol class handles the processing of various game-related messages, such as player creation, entity updates, health management, and more.The class includes methods for handling different types of network operations, including updating player positions, enemy data, bullets, and obstacles.

FunctionsDescription
handleMessageHandles incoming messages based on the opCode extracted from the buffer and calls the appropriate function for each message type.
handleCreatePlayerCallbackHandles player creation by receiving the player’s ID and dimensions, then initializes their properties in the EntityManager.
handleCreatePlayerBroadcastHandles player creation via a broadcast message, initializing the player’s ID, name, and properties in the EntityManager. It allows retrieving information about the existence of other players.
handleUpdatePlayerUpdates a player’s position based on the received data and updates the EntityManager.
handleUpdateViewportUpdates the client’s viewport with the new viewport value.
handleUpdateBlocksUpdates the obstacles based on the ID, coordinates, size, and type, then updates the EntityManager.
handleUpdateEnemiesUpdates the position and properties of enemies based on their type and adds them to the EntityManager.
handleUpdatePlayerInfosUpdates the player’s information such as score and kills based on the received data.
handleUpdateBulletsUpdates bullet information, including position and type, and adds them to the EntityManager.
handleDeleteEntityDeletes an entity from the EntityManager based on its ID, including related entities.
handleUpdateEntityHealthUpdates an entity’s health and manages win/loss conditions based on the entity’s health.

Singleton

The Singleton class implements the Singleton pattern, ensuring that there is only one instance of the class. It manages TCP and UDP sockets as well as a server address.

Socket

The Socket class manages the creation and closure of a network socket. During instantiation, it configures the server address (IP address and port) using the inet_pton method to validate the IP address. If the address is invalid, an exception is thrown. The close method checks if the socket is valid before closing it and resetting its value.

TcpSocket

The TcpSocket class represents a TCP socket that allows connecting to a server, sending, and receiving data. It inherits from the Socket class, with specific methods to initialize the socket (init), establish a connection (connect), send data (send), and receive data (receive). It handles errors by throwing exceptions in case of network operation failures and uses a singleton to store the active TCP socket.

FunctionsDescription
initCreate a TCP socket using socket() and save it in the singleton. Throw an exception in case of creation failure.
connectTry to connect to the TCP server using connect(). Throw an exception if the connection fails.
sendSend a message through the TCP socket using send(). Throw an exception if the sending fails.
receiveReceive a message through the TCP socket using recv(), then return a SmartBuffer containing the received data. Throw an exception if the reception fails.

UdpSocket

The UdpSocket class manages a UDP socket for sending and receiving data. It inherits from the Socket class and allows for initializing the socket, connecting to the server, sending, and receiving messages through the send and receive functions. It handles errors by throwing exceptions if an operation fails and closes the socket upon destruction.

FunctionsDescription
initCreate a UDP socket using socket(), then save it in the singleton. Throw an exception if the creation fails.
sendSend a message through the UDP socket using sendto(). Throw an exception if the sending fails.
receiveReceive a message through the UDP socket using recvfrom(), then return a SmartBuffer containing the received data. Throw an exception if the reception fails.

GetResponsiveValue

This class provides utility methods to calculate responsive positions and sizes for graphical elements, based on the ratio between base and current screen dimensions. It ensures consistent scaling of positions and dimensions across different screen sizes.

Logger

This class implements a color-coded logging utility for different log levels (e.g., info, success, error). It includes timestamped messages for easy debugging and supports additional categories like socket, packet, and thread logs.

Entity Component System (ECS)

Entity Component System (ECS) is a design architecture commonly used in game development and simulations. It emphasizes separation of concerns by dividing the functionality into three core components:

  • Entities: These are unique identifiers that represent individual objects in the system.
  • Components: These are data containers that hold specific attributes or properties of an entity.
  • Systems: These are responsible for the behavior and logic, operating on entities that have the required components.

By decoupling data (components) from logic (systems) and organizing them around entities, ECS enables flexibility, performance, and easier management of complex systems.

For more details, refer to:

Components

A component is a data container that holds specific attributes of an entity.

ComponentDescriptionValues
ButtonDefines entity as a buttonstd::string text
std::string fontFile
unsigned int characterSize
OptionButtonDefines entity as a checkbox buttonstd::pair<double, double> size
SliderDefines entity as a slider buttonstd::pair<double, double> length
std::pair<double, double> size
ButtonRectDefines entity as an input textconst std::pair<int, int> sizeRect
sf::Color color
bool showOutline
SpriteDefines entity as a Spritestd::pair<float, float> size
TextureDefines the texture of an entitystd::string texturePath
std::vector<int> textureRect
TextDefines entity as a textstd::string text
std::string fontFile
unsigned int characterSize
PositionStores positions of an entitystd::vector<std::pair<float, float>> positions
ColorDefines the color of an entity.std::vector<double> color
ShapeDefines entity as shape (rectangle or circle)ShapeType type
std::pair<double, double> size
float radius
SoundDefines a sound at an entitystd::string soundFile
LinkLink an entity to another entityint id

Entity

An entity is identified by an ID and contains all the components that represent its data.

Functions

  • Entity(int id, Args&&... args) -> Creates an entity with an ID and adds all the components provided in args.
  • void addComponent(const ComponentType& component) -> Adds a component in an entity.
  • void removeComponent() -> Removes component from an entity.
  • ComponentType& getComponent() -> Retrieves a component from an entity.
  • bool hasComponent() const -> Checks if an entity has a component.
  • int getEntityId() const -> Retrieves the ID of an entity.

Example

#include <Entity.hpp>
#include <components/Position.hpp>
#include <components/Text.hpp>
#include <components/Color.hpp>
#include <iostream>

int main() {
    GameEngine::Entity entity(1, Text("Hello, World!", "Arial.ttf"), Position({{0, 0}, {1, 1}}));

    entity.addComponent(Color({255, 0, 0, 1}));
    entity.removeComponent<Color>();

    if (entity.hasComponent<Position>()) {
        std::cout << "Entity has a position component" << std::endl;
    }

    return 0;
}

Systems

Systems are the core of the ECS architecture. They are the place where the logic of your game is implemented.

Functions

  • void render(sf::RenderWindow& window, std::map<int, Entity>& entities) -> Renders all entities that have a component capable of being rendered.
  • void update(int id, std::map<int, Entity>& entities, UpdateType type, const std::any& value, int posId = 0) -> Update the entity with the given id. The type of update is specified by the UpdateType enum. The value is the new value of the component.

Example

#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <map>
#include <components/Position.hpp>
#include <components/Link.hpp>
#include <components/Sprite.hpp>
#include <components/Text.hpp>
#include <components/Texture.hpp>
#include "Entity.hpp"
#include "System.hpp"

int main() {
    sf::RenderWindow window(sf::VideoMode(800, 600), "Client Game");
    std::map<int, GameEngine::Entity> entities;
    auto player = GameEngine::Entity(1, Sprite(), Texture("assets/sprite/spaceship.png", {0, 0, 34, 15}), Position({{0, 0}}));
    auto nametag = GameEngine::Entity(2, Text("Name", "assets/font/arial.ttf", 10), Position({{0, -14}}), Link(1));
    entities.emplace(1, std::move(player));
    entities.emplace(2, std::move(nametag));
    GameEngine::System system;
    float x, y = 0.0f;
    float deltaTime = 0.0f;
    float speed = 200.0f;
    sf::Clock clock;
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed)
                window.close();
        }
        deltaTime = clock.restart().asSeconds();

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Z)) {
            y -= speed * deltaTime;
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
            y += speed * deltaTime;
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q)) {
            x -= speed * deltaTime;
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) {
            x += speed * deltaTime;
        }
        system.update(1, entities, GameEngine::UpdateType::Position, std::pair(x, y));
        window.clear();
        system.render(window, entities);
        window.display();
    }
    return 0;
}

R-Type Network Architecture Documentation

This document provides a detailed explanation of the R-Type network implementation. It describes the structure, threading model, client management, and mechanisms for handling communication between the server and clients using TCP and UDP protocols.


Overview

The R-Type network is designed to handle real-time multiplayer interactions with a balance between reliability and performance. The server uses a dual-protocol approach:

  • TCP: Ensures reliable delivery of critical operations such as player creation and key press events.
  • UDP: Optimized for real-time updates like player positions, bullet movements, and obstacle updates, where occasional packet loss is acceptable.

Architecture and Structure

Server Components

  1. Main Server Class (Server)

    • Orchestrates the overall network flow.
    • Initializes both TCP and UDP sockets.
    • Starts separate threads for handling TCP and UDP operations.
  2. TCP Socket (TcpSocket)

    • Manages reliable client-server communication.
    • Handles client connections, disconnections, and data reception.
    • Binds each client connection to a specific player.
  3. UDP Socket (UdpSocket)

    • Manages real-time communication.
    • Broadcasts updates to all clients.
    • Operates three main loops:
      • Read Loop: Reads incoming UDP packets.
      • Update Loop: Processes game logic.
      • Send Loop: Sends updates to clients.
  4. SmartBuffer

    • Handles serialization and deserialization of data.
    • Used to encode and decode protocol-compliant messages.

Threading Model

The server uses a multi-threaded approach to handle TCP and UDP operations concurrently:

Threads

  1. TCP Thread:

    • Handles client connections and disconnections via the TcpSocket.
    • Reads and processes incoming data using a dedicated thread per client.
  2. UDP Threads:

    • Read Thread: Continuously receives packets from clients and processes them.
    • Update Thread: Runs the game logic, updating player positions, bullets, and obstacles.
    • Send Thread: Sends game state updates to all clients.

Thread Synchronization

  • Mutexes: Used to protect shared resources such as the list of connected clients and game entities.
  • Locks: Ensures that no two threads modify the same data simultaneously.

Client Management

Storing Clients

Clients are stored in separate lists for TCP and UDP communications:

  1. TCP Clients:

    • Maintained as a vector of socket file descriptors.
    • Each client is associated with a player ID, stored in the PlayerManager.
  2. UDP Clients:

    • Stored as a vector of sockaddr_in structures.
    • Each client’s address is added when a UDP packet is received.

Mapping Players to Clients

  • When a new player is created via TCP, their playerId is mapped to their TCP socket.
  • The same playerId is used for UDP communications to identify the player.

TCP Workflow

Connection Handling

  1. Initialization:

    • The TCP socket is created and bound to a port.
    • The socket listens for incoming connections.
  2. Accepting Connections:

    • New connections are accepted in a loop.
    • Each accepted client socket is passed to a dedicated thread for communication.

Data Handling

  1. Receiving Data:

    • Each client thread reads data from its associated socket.
    • Data is passed to the Protocol class for decoding and handling.
  2. Sending Data:

    • Responses are serialized using SmartBuffer and sent back to the client.
    • Broadcasts are handled by iterating over the list of clients.

UDP Workflow

Initialization

  • The UDP socket is created and bound to a port.
  • Unlike TCP, there is no need to establish connections.

Read Loop

  • Continuously reads incoming packets from any client.
  • Decodes messages using Protocol and updates the game state.

Update Loop

  • Executes game logic, such as updating player positions, bullets, and obstacles.
  • Uses mutexes to synchronize shared resources.

Send Loop

  • Serializes the current game state into protocol-compliant messages.
  • Sends updates to all connected clients using their stored addresses.

Key Classes and Responsibilities

1. Server

  • Entry point of the application.
  • Initializes TcpSocket and UdpSocket.
  • Manages threads for TCP and UDP workflows.

2. TcpSocket

  • Handles client connections and reliable communication.
  • Maintains a list of connected clients.
  • Maps client sockets to player IDs.

3. UdpSocket

  • Handles real-time communication.
  • Maintains a list of client addresses.
  • Reads, updates, and sends game state in separate threads.

4. Protocol

  • Decodes incoming messages and routes them to the appropriate handler.
  • Encodes outgoing messages to ensure protocol compliance.

5. SmartBuffer

  • Provides serialization and deserialization utilities.
  • Ensures efficient handling of binary data.

Communication Flow

  1. Player Creation:

    • A client sends a CREATE_PLAYER request via TCP.
    • The server creates a player and sends back a CREATE_PLAYER_CALLBACK response.
    • The server broadcasts a CREATE_PLAYER_BROADCAST message via UDP.
  2. Game Updates:

    • The server’s update thread processes the game state (e.g., player movements, bullets).
    • The send thread broadcasts updates via UDP to all clients.
  3. Hotkey Presses:

    • A client sends a HOTKEY_PRESSED message via TCP.
    • The server updates the player’s state based on the hotkey.

Summary

The R-Type network implementation leverages the strengths of both TCP and UDP protocols to deliver reliable and efficient communication. The architecture ensures scalability and real-time performance while maintaining a clear separation of responsibilities between components. Mutexes and threads ensure thread safety, enabling smooth operation in a multiplayer environment.

R-Type Miscellaneous Guide

This section covers various features of the project, such as adding enemies, obstacles, editing configurations, and understanding the logging system.


Adding New Enemies

To add new enemies, you need to update both EnemyManager and EnemyType enum in Enemy.hpp.

Steps to Add a New Enemy

  1. Define a New Enemy in the Enum

    • Open Enemy.hpp.
    • Add a new entry to the EnemyType enum:
      enum class EnemyType {
          NONE,
          GRUNT,
          SNIPER,
          TANK,
          SWARMER,
          BOSS,
          DRONE,
          MINION,
          CANNON,
          NEW_TYPE // Add your new enemy here
      };
      
  2. Configure the New Enemy

    • Go to EnemyManager.cpp.
    • Add the new enemy type to _enemyMapping:
      {"E009", {EnemyType::NEW_TYPE, 5, 60, 60, 800, 10, 1200, 700, 200}},
      
    • Here's a breakdown of the parameters: | Parameter | Description | |-----------------|---------------------------------------------------------------------------------| | EnemyType | The type of enemy, linked to the enum. | | speed | Movement speed of the enemy. | | width | Width of the enemy sprite (in pixels). | | height | Height of the enemy sprite (in pixels). | | bulletSpeed | Speed of the bullets fired by the enemy. | | bulletDamage | Damage dealt by the enemy's bullets. | | shootCooldown| Time between each shot (in milliseconds). | | shootRange | Range at which the enemy will start shooting (in pixels). | | health | Total health points of the enemy. |
  3. Map Integration

    • Use the new enemy code (e.g., E009) in your map files to place the enemy.
    • Example map snippet:
      B000B000B000E009B000B000
      

Example for Adding "E009"

{"E009", {EnemyType::DRONE, 4, 40, 40, 700, 8, 1000, 600, 100}},

This defines a new drone enemy:

  • Moves at speed 4.
  • Dimensions are 40x40 pixels.
  • Bullet speed is 700 and damage is 8.
  • Shoots every 1000 ms within a 600-pixel range.
  • Health is 100.

Adding New Obstacles

To add new obstacles:

  1. Update the ObstacleType Enum

    • Open ObstacleManager.hpp.
    • Add a new type to ObstacleType:
      enum class ObstacleType {
          NONE,
          OBSTACLE,
          OBSTACLE2,
          OBSTACLE3,
          NEW_OBSTACLE
      };
      
  2. Update the Obstacle Mapping

    • Go to ObstacleManager.cpp.
    • Add the new obstacle type:
      {"B005", ObstacleType::NEW_OBSTACLE},
      
  3. Map Integration

    • Use the new obstacle code (e.g., B005) in your map files.
    • Example:
      B000B005B000B000B000
      

Configuration (Config.hpp)

The Config.hpp file defines constants for gameplay, server settings, and default values. Here's a table of key configurations:

CategoryConstantDescription
SocketDEFAULT_BYTESDefault buffer size for socket communication.
PORTPort number for the server.
ServerCADENCYFrame rate interval in ms (affects TICK_PER_SECOND).
TICK_PER_SECONDServer update frequency.
ProgramSUCCESSExit code for successful operations.
ERRORExit code for errors.
PlayerPLAYER_WIDTHWidth of player sprites.
PLAYER_SPEEDMovement speed of players.
MapMAP_SPEEDSpeed at which the map scrolls.
MAP_WIDTH/HEIGHTDimensions of the map.
ObstacleOBSTACLE_SIZESize of obstacle sprites.

Logging System

The Logger provides different log levels for debugging and monitoring.

Log Levels and Colors

Log LevelColorUsage
infoBlueGeneral information.
successGreenSuccessful operations.
warningYellowWarnings that don't halt execution.
errorRedCritical errors.
socketCyanSocket-related logs.
packetMagentaPacket-specific logs.
threadPinkThread operations.
debugWhiteDebugging information.
traceGrayDetailed execution traces.

How to Log Messages

Use the static methods from Logger:

Logger::info("Server started.");
Logger::error("Failed to bind socket.");
Logger::debug("Player initialized.");

Logs are printed with timestamps for easy tracking.

R-Type Protocol Documentation

This document provides a detailed overview of the communication protocol for the R-Type server. It outlines the purpose, payload, and transmission method (TCP/UDP) for each operation code (OpCode). The protocol ensures consistent communication between the server and clients.


Overview

Transport Protocols

  • TCP: Reliable communication for operations requiring acknowledgment (e.g., player creation, hotkey inputs).
  • UDP: Lightweight and fast communication for real-time updates (e.g., player positions, bullets, obstacles).

Message Structure

All messages follow this general structure:

FieldTypeSize (bytes)Description
OpCodeint16_t2Identifies the operation (see below).
PayloadVariesVariableData relevant to the specific operation.

OpCode Definitions

1. DEFAULT

  • Value: 0
  • Description: Used to save the client on the server.
  • Payload: None.
  • Sent To: Server or specific client.
  • Transport: TCP.

2. HOTKEY_PRESSED

  • Value: 1
  • Description: Notifies the server of a key press by a specific player.
  • Payload:
    • playerId (int32_t): The ID of the player who pressed the key.
    • hotkeyCode (int16_t): The code of the pressed key.
  • Sent To: Server.
  • Transport: TCP.

3. CREATE_PLAYER

  • Value: 10
  • Description: Requests the creation of a new player on the server.
  • Payload:
    • playerName (string): The name of the player to create.
  • Sent To: Server.
  • Transport: TCP.

4. CREATE_PLAYER_CALLBACK

  • Value: 11
  • Description: Confirms the creation of a new player to the requesting client.
  • Payload:
    • playerId (int32_t): The ID of the newly created player.
    • width (int16_t): The width of the player’s sprite.
    • height (int16_t): The height of the player’s sprite.
  • Sent To: Specific client (the one who requested the creation).
  • Transport: TCP.

5. CREATE_PLAYER_BROADCAST

  • Value: 12
  • Description: Broadcasts the creation of a new player to all connected clients.
  • Payload:
    • playerId (int32_t): The ID of the new player.
    • playerName (string): The name of the new player.
    • width (int16_t): The width of the player’s sprite.
    • height (int16_t): The height of the player’s sprite.
  • Sent To: All clients.
  • Transport: UDP.

6. UPDATE_PLAYERS

  • Value: 20
  • Description: Updates player positions and states.
  • Payload:
    • playerId (int32_t): The ID of the player.
    • posX (int32_t): The player’s X-coordinate.
    • posY (int32_t): The player’s Y-coordinate.
  • Sent To: All clients.
  • Transport: UDP.

7. UPDATE_VIEWPORT

  • Value: 21
  • Description: Updates the current map viewport.
  • Payload:
    • viewport (double): The viewport’s position.
  • Sent To: All clients.
  • Transport: UDP.

8. UPDATE_OBSTACLES

  • Value: 22
  • Description: Updates obstacle positions or states.
  • Payload:
    • obstacleId (int32_t): The ID of the obstacle.
    • posX (int32_t): The obstacle’s X-coordinate.
    • posY (int32_t): The obstacle’s Y-coordinate.
    • size (int16_t): The size of the obstacle.
    • type (int16_t): The type of the obstacle.
  • Sent To: All clients.
  • Transport: UDP.

9. UPDATE_BULLETS

  • Value: 23
  • Description: Updates bullet positions or states.
  • Payload:
    • bulletId (int32_t): The ID of the bullet.
    • posX (int32_t): The bullet’s X-coordinate.
    • posY (int32_t): The bullet’s Y-coordinate.
    • type (int16_t): The type of the bullet.
  • Sent To: All clients.
  • Transport: UDP.

10. UPDATE_ENEMIES

  • Value: 24
  • Description: Updates enemy positions or states.
  • Payload:
    • enemyId (int32_t): The ID of the enemy.
    • posX (int32_t): The enemy’s X-coordinate.
    • posY (int32_t): The enemy’s Y-coordinate.
    • width (int16_t): The width of the enemy’s sprite.
    • height (int16_t): The height of the enemy’s sprite.
    • type (int16_t): The type of the enemy.
  • Sent To: All clients.
  • Transport: UDP.

11. UPDATE_ENTITY_HEALTH

  • Value: 25
  • Description: Broadcasts changes in the health of an entity (player or enemy).
  • Payload:
    • entityId (int32_t): The ID of the entity.
    • health (int16_t): The current health of the entity.
    • maxHealth (int16_t): The maximum health of the entity.
  • Sent To: All clients.
  • Transport: UDP.

12. UPDATE_PLAYER_INFOS

  • Value: 26
  • Description: Updates player's infos.
  • Payload:
    • playerId (int32_t): The ID of the player.
    • kills (int16_t): The kills amount of the player.
    • score (int32_t): The score of the player.
  • Sent To: Specific client.
  • Transport: UDP.

13. DELETE_ENTITY

  • Value: 30
  • Description: Deletes an entity from the game (e.g., player, enemy, bullet).
  • Payload:
    • entityId (int32_t): The ID of the entity to delete.
  • Sent To: All clients.
  • Transport: UDP.

Notes

  • The server and clients must strictly adhere to the protocol to ensure consistency.
  • Payloads must be interpreted exactly as defined.
  • TCP is used for operations requiring reliable delivery, while UDP is optimized for real-time updates.

SmartBuffer: Fast and Flexible Data Buffer

SmartBuffer is a custom library I built for managing binary data efficiently. It’s simple, fast, and works in any C++ project. The main idea is to make it easy to read, write, and manipulate raw data without worrying about memory leaks or performance hits.


Why SmartBuffer?

  1. Performance: Optimized for fast reads/writes with minimal overhead.
  2. No Memory Leaks: Fully manages its memory using std::vector.
  3. Universal: Works with any trivially copyable type (like int, float, etc.) and strings.
  4. Reusable: You can use it in any C++ project. It’s modular and lightweight.

How It Works

Core Features:

  • Dynamic Resizing: The buffer grows automatically when needed.
  • Serialization: Supports trivially copyable types and strings.
  • Operator Overloads: Use << and >> for easy read/write.
  • Buffer Reset: Clear all data or just reset the read pointer.

Structure

The buffer is built around:

  • std::vector<uint8_t>: Handles the raw data.
  • readOffset and writeOffset: Keep track of where to read and write.
  • Templates for read and write: Allow type-safe operations.

Key Methods

Initialization

SmartBuffer(size_t initialCapacity = 8);
  • Reserves initial capacity (default: 8 bytes).
  • The buffer expands as needed.

Writing Data

template <typename T>
void write(const T& value);
  • Supports any trivially copyable type.
  • For strings, writes the length first, then the content.

Reading Data

template <typename T>
T read();
  • Reads data back in the same order it was written.
  • Throws an exception if you try to read beyond the buffer.

Injecting Raw Data

void inject(const uint8_t* rawData, size_t size);
  • Adds raw binary data directly into the buffer.

Resetting

void reset(); // Clears all data
void resetRead(); // Resets the read pointer
  • reset(): Clears the whole buffer.
  • resetRead(): Resets the read pointer to the start.

Getters

size_t getSize() const;      // Total size of written data
const uint8_t* getBuffer() const; // Pointer to raw buffer data
  • Use these to access buffer stats or get the raw data.

Why .inl?

SmartBuffer’s implementation uses .inl for templates. Templates must be defined in the header (or included inline) so the compiler can generate code for specific types during compilation. This keeps the header clean and avoids duplication.


Example Usage

Writing and Reading

SmartBuffer buffer;

// Writing data
buffer << 42 << 3.14f << std::string("Hello");

// Reading data
int intValue;
float floatValue;
std::string stringValue;
buffer >> intValue >> floatValue >> stringValue;

// Output: 42, 3.14, "Hello"

Injecting Raw Data

uint8_t rawData[] = {0x01, 0x02, 0x03};
buffer.inject(rawData, sizeof(rawData));

Resetting

buffer.reset();      // Clear everything
buffer.resetRead();  // Start reading from the beginning

Technical Highlights

  1. Memory Management:

    • Uses std::vector for automatic memory handling.
    • Ensures no leaks even with dynamic resizing.
  2. Trivial Type Optimization:

    • Writes and reads trivially copyable types directly with std::memcpy.
  3. String Support:

    • Writes the length first as a uint32_t, then the content.
    • Reads back the length to reconstruct the string.
  4. Error Handling:

    • Throws std::runtime_error for out-of-bounds reads.

Performance

  • Speed: Minimal overhead, only resizes when necessary.
  • Safety: No manual memory management.
  • Efficiency: Avoids unnecessary copies with std::vector.

Where to Use It?

  • Networking (serialize/deserialize packets).
  • Game development (handling binary assets or messages).
  • General-purpose C++ projects needing binary buffers.