From dd4e50e461f86f6e2ec19d2ebc619a836c0d3b50 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Mon, 22 Dec 2025 13:35:13 +0100 Subject: [PATCH 01/16] fix(lib>rteng): Fixed `GameEngine` constructor --- lib/rteng/include/rteng.hpp | 11 ++++++++++- lib/rteng/src/rteng.cpp | 6 ------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/rteng/include/rteng.hpp b/lib/rteng/include/rteng.hpp index 537acc0..a4192e1 100644 --- a/lib/rteng/include/rteng.hpp +++ b/lib/rteng/include/rteng.hpp @@ -5,9 +5,15 @@ #include "ECS.hpp" #include "MonoBehaviour.hpp" +#include "comp/Behaviour.hpp" namespace rteng { +template +struct ComponentsList +{ +}; + /** * @class GameEngine * @brief A class used to wrap and run an ecs. @@ -20,7 +26,10 @@ class GameEngine * @tparam Components The list of components to create the @code ecs@endcode with. */ template - explicit GameEngine(); + explicit GameEngine(ComponentsList) + { + _ecs = rtecs::ECS::createWithComponents(); + } /** * @brief Registers a new entity into the @code ecs@endcode. diff --git a/lib/rteng/src/rteng.cpp b/lib/rteng/src/rteng.cpp index 1fcfa56..e27184d 100644 --- a/lib/rteng/src/rteng.cpp +++ b/lib/rteng/src/rteng.cpp @@ -5,12 +5,6 @@ namespace rteng { -template -GameEngine::GameEngine() -{ - _ecs = rtecs::ECS::createWithComponents(); -} - template rtecs::EntityID GameEngine::registerEntity( const std::shared_ptr& mono_behaviour, From f3fb9625171d3061dddae151fe72630094fc4452 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Mon, 22 Dec 2025 14:56:42 +0100 Subject: [PATCH 02/16] ref(rteng>components): Removed external components --- lib/rteng/include/{comp => }/Behaviour.hpp | 0 lib/rteng/include/comp/IO.hpp | 45 ---------------------- lib/rteng/include/comp/Sprite.hpp | 20 ---------- lib/rteng/include/comp/Transform.hpp | 17 -------- lib/rteng/include/comp/position.hpp | 17 -------- lib/rteng/include/comp/rect.hpp | 34 ---------------- lib/rteng/include/rteng.hpp | 2 +- lib/rteng/src/rteng.cpp | 2 +- 8 files changed, 2 insertions(+), 135 deletions(-) rename lib/rteng/include/{comp => }/Behaviour.hpp (100%) delete mode 100644 lib/rteng/include/comp/IO.hpp delete mode 100644 lib/rteng/include/comp/Sprite.hpp delete mode 100644 lib/rteng/include/comp/Transform.hpp delete mode 100644 lib/rteng/include/comp/position.hpp delete mode 100644 lib/rteng/include/comp/rect.hpp diff --git a/lib/rteng/include/comp/Behaviour.hpp b/lib/rteng/include/Behaviour.hpp similarity index 100% rename from lib/rteng/include/comp/Behaviour.hpp rename to lib/rteng/include/Behaviour.hpp diff --git a/lib/rteng/include/comp/IO.hpp b/lib/rteng/include/comp/IO.hpp deleted file mode 100644 index 127b5e9..0000000 --- a/lib/rteng/include/comp/IO.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -namespace comp { - -struct IO -{ - static constexpr uint8_t STATE_CHANGED_BIT = 0b10; - - enum class ButtonState : uint8_t - { - UP = 0b00, // is not pressed - DOWN = 0b01, // is being held down - RELEASED = 0b10, // just got released - PRESSED = 0b11, // just got pressed - }; - struct Mouse - { - float x = 0; - float y = 0; - bool leftButton = false; - bool rightButton = false; - - template - void serialize(Archive& ar) - { - ar & x & y & leftButton & rightButton; - } - }; - ButtonState up; - ButtonState down; - ButtonState left; - ButtonState right; - ButtonState action1; - ButtonState action2; - Mouse mouse; - template - void serialize(Archive& ar) - { - ar & up & down & left & right & action1 & action2 & mouse; - } -}; - -} // namespace comp diff --git a/lib/rteng/include/comp/Sprite.hpp b/lib/rteng/include/comp/Sprite.hpp deleted file mode 100644 index e88d9ac..0000000 --- a/lib/rteng/include/comp/Sprite.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "rect.hpp" - -namespace comp { - -struct Sprite -{ - bool shown = true; - float scale = 1.0f; - MyColor color = {0, 0, 0, 0}; - - template - void serialize(Archive&) - { - // LALALALALLA J4ENTENDS PAS - } -}; - -} // namespace comp diff --git a/lib/rteng/include/comp/Transform.hpp b/lib/rteng/include/comp/Transform.hpp deleted file mode 100644 index 83d56eb..0000000 --- a/lib/rteng/include/comp/Transform.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -namespace comp { - -struct Transform -{ - float x = 0; - float y = 0; - - template - void serialize(Archive& ar) - { - ar & x & y; - } -}; - -} // namespace comp diff --git a/lib/rteng/include/comp/position.hpp b/lib/rteng/include/comp/position.hpp deleted file mode 100644 index 3fb1645..0000000 --- a/lib/rteng/include/comp/position.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -namespace comp { - -struct Position -{ - float x = 0.0f; - float y = 0.0f; - - template - void serialize(Archive& ar) - { - ar & x & y; - } -}; - -} // namespace comp diff --git a/lib/rteng/include/comp/rect.hpp b/lib/rteng/include/comp/rect.hpp deleted file mode 100644 index 8e226b3..0000000 --- a/lib/rteng/include/comp/rect.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -namespace comp { - -struct MyColor -{ - unsigned char r; // Color red value - unsigned char g; // Color green value - unsigned char b; // Color blue value - unsigned char a; // Color alpha value - - template - void serialize(Archive& ar) - { - ar & r & g & b & a; - } -}; - -struct Rectangle -{ - bool shown = true; - float width = 0.0f; - float height = 0.0; - MyColor outline = {0, 0, 0, 0}; - MyColor inFill = {0, 0, 0, 0}; - - template - void serialize(Archive& ar) - { - ar & shown & width & height & outline & inFill; - } -}; - -} // namespace comp diff --git a/lib/rteng/include/rteng.hpp b/lib/rteng/include/rteng.hpp index a4192e1..ddb7709 100644 --- a/lib/rteng/include/rteng.hpp +++ b/lib/rteng/include/rteng.hpp @@ -3,9 +3,9 @@ #include #include +#include "Behaviour.hpp" #include "ECS.hpp" #include "MonoBehaviour.hpp" -#include "comp/Behaviour.hpp" namespace rteng { diff --git a/lib/rteng/src/rteng.cpp b/lib/rteng/src/rteng.cpp index e27184d..ed93566 100644 --- a/lib/rteng/src/rteng.cpp +++ b/lib/rteng/src/rteng.cpp @@ -1,7 +1,7 @@ #include "rteng.hpp" +#include "Behaviour.hpp" #include "SparseSet.hpp" -#include "comp/Behaviour.hpp" namespace rteng { From d2bf4f9209cc4639888f64653b369fa627f42f99 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Tue, 23 Dec 2025 00:02:18 +0100 Subject: [PATCH 03/16] fix(lib>rteng): Moved templated function from `.cpp` tp `.hpp` --- lib/rteng/include/rteng.hpp | 20 +++++++++++++++++++- lib/rteng/src/rteng.cpp | 23 ----------------------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/lib/rteng/include/rteng.hpp b/lib/rteng/include/rteng.hpp index ddb7709..f643ab8 100644 --- a/lib/rteng/include/rteng.hpp +++ b/lib/rteng/include/rteng.hpp @@ -40,7 +40,25 @@ class GameEngine */ template rtecs::EntityID registerEntity(const std::shared_ptr& mono_behaviour, - Components&&... components); + Components&&... components) + { + const rtecs::EntityID entityId = _ecs->registerEntity...>( + std::forward(components)...); + + if (!mono_behaviour || !_ecs->hasEntityComponent(entityId)) { + return entityId; + } + auto& behaviourComponents = _ecs->getComponent(); + auto& behaviourSparseSet = + dynamic_cast&>(behaviourComponents); + + comp::Behaviour behaviourComp; + behaviourComp.instance = mono_behaviour; + behaviourComp.started = false; + + behaviourSparseSet.put(entityId, behaviourComp); + return entityId; + } /** * @brief Runs one round of the game loop and apply all registered systems. diff --git a/lib/rteng/src/rteng.cpp b/lib/rteng/src/rteng.cpp index ed93566..1747c1c 100644 --- a/lib/rteng/src/rteng.cpp +++ b/lib/rteng/src/rteng.cpp @@ -5,29 +5,6 @@ namespace rteng { -template -rtecs::EntityID GameEngine::registerEntity( - const std::shared_ptr& mono_behaviour, - Components&&... components) -{ - const rtecs::EntityID entityId = - _ecs->registerEntity...>(std::forward(components)...); - - if (!mono_behaviour || !_ecs->hasEntityComponent(entityId)) { - return entityId; - } - auto& behaviourComponents = _ecs->getComponent(); - auto& behaviourSparseSet = - dynamic_cast&>(behaviourComponents); - - comp::Behaviour behaviourComp; - behaviourComp.instance = mono_behaviour; - behaviourComp.started = false; - - behaviourSparseSet.put(entityId, behaviourComp); - return entityId; -} - void GameEngine::runOnce(const double dt) const { { From 8ee7d36b83bfd964471d0d94edacd1bed318b0a2 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Mon, 22 Dec 2025 13:23:32 +0100 Subject: [PATCH 04/16] feat(server): Added lobby implementation --- Server/src/lobby/lobby.cpp | 71 ++++++++++++++++++++++++++++++++++++++ Server/src/lobby/lobby.hpp | 47 +++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 Server/src/lobby/lobby.cpp create mode 100644 Server/src/lobby/lobby.hpp diff --git a/Server/src/lobby/lobby.cpp b/Server/src/lobby/lobby.cpp new file mode 100644 index 0000000..3e0e801 --- /dev/null +++ b/Server/src/lobby/lobby.cpp @@ -0,0 +1,71 @@ +#include "lobby.hpp" + +#include "logger/Thread.h" + +Lobby::Lobby(const lobby::Id id) + : _roomId(id), + _engine(rteng::ComponentsList{}), + _isRunning(false) +{ + LOG_INFO("Creating new lobby."); +} + +lobby::Id Lobby::getRoomId() const { return _roomId; } + +void Lobby::pushTask(lobby::Callback action) { _actionQueue.push(std::move(action)); } + +bool Lobby::hasJoined(const rtnt::core::session::Id sessionId) const +{ + return _players.contains(sessionId); +} + +bool Lobby::join(const rtnt::core::session::Id sessionId) +{ + if (_players.contains(sessionId)) { + LOG_WARN("Player already joined this lobby."); + return false; + } + _players.try_emplace(sessionId, 0); + if (_players.contains(sessionId)) { + return true; + } + LOG_WARN("Player couldn't join this lobby"); + return false; +} + +void Lobby::leave(const rtnt::core::session::Id sessionId) +{ + if (_players.contains(sessionId)) { + _players.erase(sessionId); + } else { + LOG_WARN("Player was not in this lobby"); + } +} + +void Lobby::stop() +{ + if (!_isRunning) { + return; + } + LOG_INFO("Stopping lobby {}.", _roomId); + _isRunning = false; + if (_thread.joinable()) { + _thread.join(); + } +} + +void Lobby::start() +{ + _isRunning = true; + _thread = std::thread(&Lobby::run, this); + _thread.detach(); +} + +void Lobby::run() +{ + logger::setThreadLabel(("Lobby " + std::to_string(_roomId)).c_str()); + while (_isRunning) { + _engine.runOnce(0.16); + } +} + diff --git a/Server/src/lobby/lobby.hpp b/Server/src/lobby/lobby.hpp new file mode 100644 index 0000000..e886049 --- /dev/null +++ b/Server/src/lobby/lobby.hpp @@ -0,0 +1,47 @@ +#pragma once +#include + +#include "rteng.hpp" +#include "rtnt/core/session.hpp" + +namespace comp { + +struct Dummy +{ + int k; +}; + +} // namespace comp + +#define ALL_COMPONENTS comp::Dummy + +namespace lobby { + +using Id = uint32_t; +using Callback = std::function; + +} // namespace lobby + +class Lobby +{ +public: + explicit Lobby(lobby::Id id); + + bool join(rtnt::core::session::Id sessionId); + lobby::Id getRoomId() const; + void leave(rtnt::core::session::Id sessionId); + bool hasJoined(rtnt::core::session::Id sessionId) const; + void pushTask(lobby::Callback action); + void start(); + void stop(); + +private: + lobby::Id _roomId; + std::queue _actionQueue; + rteng::GameEngine _engine; + std::unordered_map _players; + std::atomic _isRunning; + std::thread _thread; + + void run(); +}; From 6e7a0248fbe6f582141dcc79fb35839ad32dd1c0 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Mon, 22 Dec 2025 13:23:44 +0100 Subject: [PATCH 05/16] feat(server>lobby): Added lobbyManager --- Server/src/lobby/lobby_manager.cpp | 53 ++++++++++++++++++++++++++++++ Server/src/lobby/lobby_manager.hpp | 27 +++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 Server/src/lobby/lobby_manager.cpp create mode 100644 Server/src/lobby/lobby_manager.hpp diff --git a/Server/src/lobby/lobby_manager.cpp b/Server/src/lobby/lobby_manager.cpp new file mode 100644 index 0000000..15a57a5 --- /dev/null +++ b/Server/src/lobby/lobby_manager.cpp @@ -0,0 +1,53 @@ +#include "lobby_manager.hpp" + +#include + +namespace lobby { + +Id Manager::createLobby() +{ + static Id nbLobbies = 0; + _lobbies.emplace(nbLobbies, std::make_unique(nbLobbies)); + _lobbies.at(nbLobbies)->start(); + return nbLobbies++; +} + +Manager::~Manager() { stopAll(); } + +void Manager::stopAll() const +{ + for (const auto& lobby : _lobbies | std::views::values) { + lobby->stop(); + } +} + +void Manager::pushActionToLobby(rtnt::core::session::Id sessionId, + Callback action) +{ + const auto it = _playerLookup.find(sessionId); + if (it != _playerLookup.end()) { + Lobby* lobby = it->second; + lobby->pushTask(std::move(action)); + } else { + LOG_WARN("Session {} is not in any lobby.", sessionId); + } +} + +bool Manager::joinRoom(const rtnt::core::session::Id sessionId, + const lobby::Id roomId) const +{ + if (_lobbies.contains(roomId)) { + return _lobbies.at(roomId)->join(sessionId); + } + return false; +} + +void Manager::leaveRoom(rtnt::core::session::Id sessionId) +{ + const auto it = _playerLookup.find(sessionId); + if (it != _playerLookup.end()) { + it->second->leave(sessionId); + } +} + +} // namespace lobby diff --git a/Server/src/lobby/lobby_manager.hpp b/Server/src/lobby/lobby_manager.hpp new file mode 100644 index 0000000..1464294 --- /dev/null +++ b/Server/src/lobby/lobby_manager.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "lobby.hpp" +#include "rtnt/core/session.hpp" + +namespace lobby { + +class Manager +{ +public: + explicit Manager() = default; + ~Manager(); + + [[nodiscard]] bool joinRoom(rtnt::core::session::Id sessionId, + lobby::Id roomId = 0) const; + void stopAll() const; + void leaveRoom(rtnt::core::session::Id sessionId); + void pushActionToLobby(rtnt::core::session::Id sessionId, + Callback action); + Id createLobby(); + +private: + std::unordered_map> _lobbies; + std::unordered_map _playerLookup; +}; + +} // namespace lobby From 21a6d762d836d907ead6d28b4b953257e4c84977 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Mon, 22 Dec 2025 13:24:03 +0100 Subject: [PATCH 06/16] feat(server): Added Application featuring the lobbyManager --- Server/src/app.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++ Server/src/app.hpp | 29 ++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 Server/src/app.cpp create mode 100644 Server/src/app.hpp diff --git a/Server/src/app.cpp b/Server/src/app.cpp new file mode 100644 index 0000000..6bc7575 --- /dev/null +++ b/Server/src/app.cpp @@ -0,0 +1,55 @@ +#include "app.hpp" + +#include "logger/Logger.h" +#include "logger/Thread.h" +#include "utils.hpp" + +namespace server { + +App::App(const unsigned short port) + : _server(_context, + port) +{ + registerCallbacks(); + _server.onConnect( + [](std::shared_ptr) { LOG_INFO("Accepting new connection"); }); + _server.onDisconnect( + [](std::shared_ptr s) { LOG_INFO("Disconnected {}", s->getId()); }); + _server.onMessage([](std::shared_ptr s, rtnt::core::Packet& p) { + LOG_INFO("Client {} sent a packet of type {}", s->getId(), p.getId()); + }); + _lobbyManager.createLobby(); +} + +App::~App() +{ + LOG_INFO("Shutting down server."); + _lobbyManager.stopAll(); + _context.stop(); + if (_ioThread.joinable()) { + _ioThread.join(); + } +} + +void App::start() +{ + _ioThread = std::thread([this]() { + logger::setThreadLabel("IoThread"); + _context.run(); + }); + _ioThread.detach(); + _server.start(); + utils::LoopTimer loopTimer(TPS); + + while (true) { + _server.update(); + loopTimer.waitForNextTick(); + } +} + +void App::registerCallbacks() +{ + // Empty for now but register the packet callbacks here; +} + +} // namespace server diff --git a/Server/src/app.hpp b/Server/src/app.hpp new file mode 100644 index 0000000..e9e18a5 --- /dev/null +++ b/Server/src/app.hpp @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +#include "lobby/lobby_manager.hpp" +#include "rtnt/core/server.hpp" + +#define TPS 20 + +namespace server { + +class App +{ +public: + explicit App(unsigned short port); + ~App(); + + [[noreturn]] void start(); + +private: + asio::io_context _context; + rtnt::core::Server _server; + std::thread _ioThread; + lobby::Manager _lobbyManager; + + void registerCallbacks(); +}; + +} // namespace server From d1eea8adb1d052f7d7123c8d3d8f932081d0f191 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Mon, 22 Dec 2025 13:25:09 +0100 Subject: [PATCH 07/16] feat(server): Added cli error handling and application running --- Server/src/main.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Server/src/main.cpp b/Server/src/main.cpp index 2dc04b1..26386f6 100644 --- a/Server/src/main.cpp +++ b/Server/src/main.cpp @@ -1,3 +1,4 @@ +#include "app.hpp" #include "cli_parser.hpp" #include "logger/Logger.h" #include "logger/Sinks/LogFileSink.h" @@ -9,14 +10,16 @@ int main(int argc, Logger::getInstance().addSink(); Logger::getInstance().addSink("logs/latest.log"); - Logger::initialize( - "R-Type Server", argc, const_cast(argv), logger::BuildInfo::fromCMake()); + Logger::initialize("R-Type Server", argc, argv, logger::BuildInfo::fromCMake()); cli_parser::Parser p(argc, argv); - rteng::GameEngine eng(p.getValue("-p").as()); - eng.init(); - eng.run(); - LOG_INFO("Shutting down server."); + if (!p.hasFlag("-p")) { + LOG_FATAL("No port specified, use \"-p {port}\"."); + } + if (p.hasFlag("--graphical")) { + LOG_INFO("Running server with debug window. (unimplemented)"); + } - return 0; + server::App server(p.getValue("-p").as()); + server.start(); } From 804b76199bd264bac7af757db103f94cfaec6d9f Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Mon, 22 Dec 2025 13:28:34 +0100 Subject: [PATCH 08/16] feat(server): Server now compiles and run --- Server/CMakeLists.txt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Server/CMakeLists.txt b/Server/CMakeLists.txt index b84a07a..16cf94c 100644 --- a/Server/CMakeLists.txt +++ b/Server/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.20) +include(${CMAKE_BINARY_DIR}/conan_toolchain.cmake) + project(r-type_server VERSION 0.0.1 DESCRIPTION "R-Type Server" @@ -12,12 +14,16 @@ if(PROJECT_IS_TOP_LEVEL) message(WARNING "Building Server standalone, adding Shuvlog and rtnt manually") add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../lib/shuvlog shuvlog) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../lib/cli_parser cli_parser) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../lib/rtnt rtnt) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../lib/rteng rteng) endif() # --- Sources / Headers --- add_executable(${PROJECT_NAME} src/main.cpp + src/lobby/lobby.cpp + src/lobby/lobby_manager.cpp + src/app.cpp ) target_include_directories(${PROJECT_NAME} PRIVATE @@ -26,8 +32,12 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../common ) +find_package(asio REQUIRED) + # --- Libraries --- target_link_libraries(${PROJECT_NAME} PRIVATE + asio::asio + rtnt rteng cli_parser shuvlog @@ -52,7 +62,14 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23) if (MSVC) - target_compile_options(${PROJECT_NAME} PRIVATE /W4 /permissive-) + target_compile_definitions(${PROJECT_NAME} + PUBLIC + WIN32_LEAN_AND_MEAN + NOMINMAX + NOGDI + NOUSER + ) + target_compile_options(${PROJECT_NAME} PRIVATE /W4) else() target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror -pedantic From f82be5136d15b47196e8f24f0a0209eb6d1e247d Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Mon, 22 Dec 2025 13:30:52 +0100 Subject: [PATCH 09/16] feat(common): Added LoopTimer for rate limiting --- common/utils.hpp | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 common/utils.hpp diff --git a/common/utils.hpp b/common/utils.hpp new file mode 100644 index 0000000..eca4bd7 --- /dev/null +++ b/common/utils.hpp @@ -0,0 +1,57 @@ +#pragma once +#include +#include + +#ifdef _WIN32 +#include +#pragma comment(lib, "winmm.lib") + +void enableHighPrecisionTimer() { timeBeginPeriod(1); } +#endif + +#define SPIN_THRESHOLD std::chrono::milliseconds(2) + +namespace utils { + +using namespace std::chrono; + +inline double getElapsedTime() +{ + static time_point lastCallTime = steady_clock::now(); + const duration elapsed_seconds = steady_clock::now() - lastCallTime; + const double seconds = elapsed_seconds.count(); + lastCallTime = steady_clock::now(); + return seconds; +} + +class LoopTimer +{ +public: + explicit LoopTimer(const double tps) + : _interval(duration_cast(duration(1.0 / tps))) + { + _nextTick = steady_clock::now(); + } + + void waitForNextTick() + { + _nextTick += _interval; + const time_point now = steady_clock::now(); + if (now >= _nextTick) { + return; + } + const auto remaining = duration(_nextTick - now); + if (remaining > SPIN_THRESHOLD) { + std::this_thread::sleep_for(remaining - SPIN_THRESHOLD); + } + while (steady_clock::now() < _nextTick) { + std::this_thread::yield(); + } + } + +private: + steady_clock::duration _interval; + steady_clock::time_point _nextTick; +}; + +} // namespace utils From a5ea649fc972a8c2a03ef6f2c24e0c0c1f812f6e Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Mon, 22 Dec 2025 14:57:13 +0100 Subject: [PATCH 10/16] ref(common): Added previously created components --- common/components/IO.hpp | 45 +++++++++++++++++++++++++++++++++ common/components/Sprite.hpp | 20 +++++++++++++++ common/components/Transform.hpp | 17 +++++++++++++ common/components/position.hpp | 17 +++++++++++++ common/components/rect.hpp | 34 +++++++++++++++++++++++++ 5 files changed, 133 insertions(+) create mode 100644 common/components/IO.hpp create mode 100644 common/components/Sprite.hpp create mode 100644 common/components/Transform.hpp create mode 100644 common/components/position.hpp create mode 100644 common/components/rect.hpp diff --git a/common/components/IO.hpp b/common/components/IO.hpp new file mode 100644 index 0000000..127b5e9 --- /dev/null +++ b/common/components/IO.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +namespace comp { + +struct IO +{ + static constexpr uint8_t STATE_CHANGED_BIT = 0b10; + + enum class ButtonState : uint8_t + { + UP = 0b00, // is not pressed + DOWN = 0b01, // is being held down + RELEASED = 0b10, // just got released + PRESSED = 0b11, // just got pressed + }; + struct Mouse + { + float x = 0; + float y = 0; + bool leftButton = false; + bool rightButton = false; + + template + void serialize(Archive& ar) + { + ar & x & y & leftButton & rightButton; + } + }; + ButtonState up; + ButtonState down; + ButtonState left; + ButtonState right; + ButtonState action1; + ButtonState action2; + Mouse mouse; + template + void serialize(Archive& ar) + { + ar & up & down & left & right & action1 & action2 & mouse; + } +}; + +} // namespace comp diff --git a/common/components/Sprite.hpp b/common/components/Sprite.hpp new file mode 100644 index 0000000..e88d9ac --- /dev/null +++ b/common/components/Sprite.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "rect.hpp" + +namespace comp { + +struct Sprite +{ + bool shown = true; + float scale = 1.0f; + MyColor color = {0, 0, 0, 0}; + + template + void serialize(Archive&) + { + // LALALALALLA J4ENTENDS PAS + } +}; + +} // namespace comp diff --git a/common/components/Transform.hpp b/common/components/Transform.hpp new file mode 100644 index 0000000..83d56eb --- /dev/null +++ b/common/components/Transform.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace comp { + +struct Transform +{ + float x = 0; + float y = 0; + + template + void serialize(Archive& ar) + { + ar & x & y; + } +}; + +} // namespace comp diff --git a/common/components/position.hpp b/common/components/position.hpp new file mode 100644 index 0000000..3fb1645 --- /dev/null +++ b/common/components/position.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace comp { + +struct Position +{ + float x = 0.0f; + float y = 0.0f; + + template + void serialize(Archive& ar) + { + ar & x & y; + } +}; + +} // namespace comp diff --git a/common/components/rect.hpp b/common/components/rect.hpp new file mode 100644 index 0000000..8e226b3 --- /dev/null +++ b/common/components/rect.hpp @@ -0,0 +1,34 @@ +#pragma once + +namespace comp { + +struct MyColor +{ + unsigned char r; // Color red value + unsigned char g; // Color green value + unsigned char b; // Color blue value + unsigned char a; // Color alpha value + + template + void serialize(Archive& ar) + { + ar & r & g & b & a; + } +}; + +struct Rectangle +{ + bool shown = true; + float width = 0.0f; + float height = 0.0; + MyColor outline = {0, 0, 0, 0}; + MyColor inFill = {0, 0, 0, 0}; + + template + void serialize(Archive& ar) + { + ar & shown & width & height & outline & inFill; + } +}; + +} // namespace comp From 4da837f795d0a67d50d72c77a11898533ba21185 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Tue, 23 Dec 2025 00:00:14 +0100 Subject: [PATCH 11/16] feat(server): Added basic `onJoin` callback --- Server/src/lobby/lobby.cpp | 13 +++++++++++++ Server/src/lobby/lobby.hpp | 16 ++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Server/src/lobby/lobby.cpp b/Server/src/lobby/lobby.cpp index 3e0e801..a79424a 100644 --- a/Server/src/lobby/lobby.cpp +++ b/Server/src/lobby/lobby.cpp @@ -1,5 +1,8 @@ #include "lobby.hpp" +#include "components/position.hpp" +#include "components/type.hpp" +#include "enums/entity_types.hpp" #include "logger/Thread.h" Lobby::Lobby(const lobby::Id id) @@ -27,6 +30,12 @@ bool Lobby::join(const rtnt::core::session::Id sessionId) } _players.try_emplace(sessionId, 0); if (_players.contains(sessionId)) { + _actionQueue.push( + [](rteng::GameEngine& + engine) { // Create a new player entity on join (maybe do it otherwise) + engine.registerEntity( + nullptr, {10, 10}, {entity::Type::kPlayer}); + }); return true; } LOG_WARN("Player couldn't join this lobby"); @@ -64,7 +73,11 @@ void Lobby::start() void Lobby::run() { logger::setThreadLabel(("Lobby " + std::to_string(_roomId)).c_str()); + lobby::Callback callbackFunction; while (_isRunning) { + while (_actionQueue.pop(callbackFunction)) { + callbackFunction(_engine); + } _engine.runOnce(0.16); } } diff --git a/Server/src/lobby/lobby.hpp b/Server/src/lobby/lobby.hpp index e886049..cacfdf7 100644 --- a/Server/src/lobby/lobby.hpp +++ b/Server/src/lobby/lobby.hpp @@ -1,24 +1,16 @@ #pragma once #include +#include "concurrent_queue.hpp" #include "rteng.hpp" #include "rtnt/core/session.hpp" -namespace comp { - -struct Dummy -{ - int k; -}; - -} // namespace comp - -#define ALL_COMPONENTS comp::Dummy +#define ALL_COMPONENTS comp::Type, comp::Position namespace lobby { using Id = uint32_t; -using Callback = std::function; +using Callback = std::function; } // namespace lobby @@ -37,7 +29,7 @@ class Lobby private: lobby::Id _roomId; - std::queue _actionQueue; + utils::ConcurrentQueue _actionQueue; rteng::GameEngine _engine; std::unordered_map _players; std::atomic _isRunning; From 92b0d68bcee3e20fd3b56561b2e3cc3cda8686a2 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Tue, 23 Dec 2025 00:00:57 +0100 Subject: [PATCH 12/16] feat(common): Added `Type` component and enum --- common/components/type.hpp | 18 ++++++++++++++++++ common/enums/entity_types.hpp | 11 +++++++++++ 2 files changed, 29 insertions(+) create mode 100644 common/components/type.hpp create mode 100644 common/enums/entity_types.hpp diff --git a/common/components/type.hpp b/common/components/type.hpp new file mode 100644 index 0000000..03c0eed --- /dev/null +++ b/common/components/type.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "enums/entity_types.hpp" + +namespace comp { + +struct Type +{ + entity::Type type; + + template + void serialize(Archive& ar) + { + ar & type; + } +}; + +} // namespace comp diff --git a/common/enums/entity_types.hpp b/common/enums/entity_types.hpp new file mode 100644 index 0000000..e521aea --- /dev/null +++ b/common/enums/entity_types.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace entity { + +enum class Type +{ + kPlayer = 1, + kEnemy, +}; + +} From 2f2ddd48a52c6ebbce1426c4eb05514c0799501e Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Tue, 23 Dec 2025 00:01:13 +0100 Subject: [PATCH 13/16] feat(common): Added `ConcurrentQueue` --- common/concurrent_queue.hpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 common/concurrent_queue.hpp diff --git a/common/concurrent_queue.hpp b/common/concurrent_queue.hpp new file mode 100644 index 0000000..a255461 --- /dev/null +++ b/common/concurrent_queue.hpp @@ -0,0 +1,33 @@ +#pragma once +#include +#include + +namespace utils { + +template +class ConcurrentQueue +{ +public: + bool pop(T& a) + { + std::lock_guard lock(_mutex); + if (_queue.empty()) { + return false; + } + a = _queue.front(); + _queue.pop(); + return true; + } + + void push(const T& value) + { + std::lock_guard lock(_mutex); + _queue.push(value); + } + +private: + std::queue _queue; + std::mutex _mutex; +}; + +} // namespace utils From 5b41f5d7aa3c62c7ed124c2471bc8d9c3e69cf24 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Tue, 23 Dec 2025 11:34:33 +0100 Subject: [PATCH 14/16] fix(server): Fixed server starting (now starts before ioContext is ran) --- Server/src/app.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/src/app.cpp b/Server/src/app.cpp index 6bc7575..49cedef 100644 --- a/Server/src/app.cpp +++ b/Server/src/app.cpp @@ -33,12 +33,12 @@ App::~App() void App::start() { + _server.start(); _ioThread = std::thread([this]() { logger::setThreadLabel("IoThread"); _context.run(); }); _ioThread.detach(); - _server.start(); utils::LoopTimer loopTimer(TPS); while (true) { From ffc4e365010e34d07412e365e085422183ab1ead Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Tue, 23 Dec 2025 11:35:13 +0100 Subject: [PATCH 15/16] ref(server>lobby>gameEngine): Removed `ALL_COMPONENT` macro --- Server/src/lobby/lobby.cpp | 5 +++-- Server/src/lobby/lobby.hpp | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Server/src/lobby/lobby.cpp b/Server/src/lobby/lobby.cpp index a79424a..57712e3 100644 --- a/Server/src/lobby/lobby.cpp +++ b/Server/src/lobby/lobby.cpp @@ -1,5 +1,6 @@ #include "lobby.hpp" +#include "components/all.hpp" #include "components/position.hpp" #include "components/type.hpp" #include "enums/entity_types.hpp" @@ -7,7 +8,7 @@ Lobby::Lobby(const lobby::Id id) : _roomId(id), - _engine(rteng::ComponentsList{}), + _engine(components::GameComponents{}), _isRunning(false) { LOG_INFO("Creating new lobby."); @@ -33,7 +34,7 @@ bool Lobby::join(const rtnt::core::session::Id sessionId) _actionQueue.push( [](rteng::GameEngine& engine) { // Create a new player entity on join (maybe do it otherwise) - engine.registerEntity( + engine.registerEntity( nullptr, {10, 10}, {entity::Type::kPlayer}); }); return true; diff --git a/Server/src/lobby/lobby.hpp b/Server/src/lobby/lobby.hpp index cacfdf7..17ed11f 100644 --- a/Server/src/lobby/lobby.hpp +++ b/Server/src/lobby/lobby.hpp @@ -5,8 +5,6 @@ #include "rteng.hpp" #include "rtnt/core/session.hpp" -#define ALL_COMPONENTS comp::Type, comp::Position - namespace lobby { using Id = uint32_t; From b72acdaf96b92c409bcf1b33be8c23ae90603ae1 Mon Sep 17 00:00:00 2001 From: Pierre Marguerie Date: Tue, 23 Dec 2025 11:59:50 +0100 Subject: [PATCH 16/16] feat(server>documentation): Added stringdoc --- Server/src/app.hpp | 11 ++++++++ Server/src/lobby/lobby.hpp | 44 +++++++++++++++++++++++++++++- Server/src/lobby/lobby_manager.hpp | 29 +++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Server/src/app.hpp b/Server/src/app.hpp index e9e18a5..f217cf6 100644 --- a/Server/src/app.hpp +++ b/Server/src/app.hpp @@ -9,12 +9,23 @@ namespace server { +/** + * @class App + * @brief A server application containing a lobby manager. + */ class App { public: + /** + * @brief Creates a server listening to specified port. + * @param port The port to listen to. + */ explicit App(unsigned short port); ~App(); + /** + * @brief Starts the server and updates it periodically. + */ [[noreturn]] void start(); private: diff --git a/Server/src/lobby/lobby.hpp b/Server/src/lobby/lobby.hpp index 17ed11f..0b55483 100644 --- a/Server/src/lobby/lobby.hpp +++ b/Server/src/lobby/lobby.hpp @@ -12,24 +12,66 @@ using Callback = std::function; } // namespace lobby +/** + * @class Lobby + * @brief Encapsulates a gameEngine instance and it's networking interface + */ class Lobby { public: + /** + * @brief Creates a lobby with the specified @code id@endcode. + * @param id an uint32(lobby::Id) that represents the id of the lobby. + * + * Note that the uniqueness of the ID depends on the user providing a distinct value. + */ explicit Lobby(lobby::Id id); + /** + * @brief Tries to join this lobby. + * @param sessionId The id of the session trying to join. + * @return A boolean representing the status of the request. + */ bool join(rtnt::core::session::Id sessionId); + + /** + * @return The id of this lobby. + */ lobby::Id getRoomId() const; + + /** + * @brief Removes the @code SessionId@endcode from this lobby. + * @param sessionId The id of the session to remove. + */ void leave(rtnt::core::session::Id sessionId); + /** + * @param sessionId The researched id. + * @return Whether this lobby contains this @code sessionId@endcode. + */ bool hasJoined(rtnt::core::session::Id sessionId) const; + + /** + * @brief Pushes a task to be made inside the running thread. + * This function is thread-safe. + * @param action A function performing the required action. + */ void pushTask(lobby::Callback action); + + /** + * @brief Start this lobby. + */ void start(); + + /** + * @brief Stop this lobby. + */ void stop(); private: lobby::Id _roomId; utils::ConcurrentQueue _actionQueue; rteng::GameEngine _engine; - std::unordered_map _players; + std::unordered_map _players; std::atomic _isRunning; std::thread _thread; diff --git a/Server/src/lobby/lobby_manager.hpp b/Server/src/lobby/lobby_manager.hpp index 1464294..5e3d096 100644 --- a/Server/src/lobby/lobby_manager.hpp +++ b/Server/src/lobby/lobby_manager.hpp @@ -5,18 +5,45 @@ namespace lobby { +/** + * @class Manager + * @brief A simple lobby manager + */ class Manager { public: explicit Manager() = default; ~Manager(); + /** + * @brief Tries to join a specific lobby. + * @param sessionId The id of the session trying to join. + * @param roomId The id of the lobby to join. + * @return Whether the request is successful. + */ [[nodiscard]] bool joinRoom(rtnt::core::session::Id sessionId, - lobby::Id roomId = 0) const; + Id roomId = 0) const; + /** + * @brief Stops all lobbies. + */ void stopAll() const; + /** + * @brief Leaves the lobby corresponding to this sessionId. + * @param sessionId The id of the disconnecting session. + */ void leaveRoom(rtnt::core::session::Id sessionId); + /** + * @brief Pushes an action to be performed by the lobby. + * @param sessionId The id of the session triggering the action. + * @param action A function performing the triggered action. + */ void pushActionToLobby(rtnt::core::session::Id sessionId, Callback action); + /** + * @brief Creates a new lobby. + * The creation of the lobbies is not automatic. + * @return The id of the newly created lobby. + */ Id createLobby(); private: