From d58b84d4a54c5d9438970d0b4139b8ef78b9ef67 Mon Sep 17 00:00:00 2001 From: sebastian Date: Thu, 6 Aug 2026 20:35:13 +0200 Subject: [PATCH] Multiplayer Connection Start --- CMakeLists.txt | 5 +++ engine/src/core/events/EventBus.h | 10 ++++- external/NetworkCore | 2 +- game/src/ColorRaceApp.cpp | 1 + game/src/ColorRaceApp.h | 23 +++++++++- game/src/mainMenu/MainMenuScene.cpp | 2 +- game/src/mainMenu/MainMenuScene.h | 5 ++- game/src/mainMenu/layer/MainMenuUiLayer.cpp | 22 ++++++++++ game/src/mainMenu/layer/MainMenuUiLayer.h | 12 +++++- game/src/multiplayer/LudoGameClient.cpp | 5 +++ game/src/multiplayer/LudoGameClient.h | 47 +++++++++++++++++++++ game/src/multiplayer/LudoNetworkEvents.h | 24 +++++++++++ server/CMakeLists.txt | 17 ++++++++ server/src/LudoGameServer.cpp | 26 ++++++++++++ server/src/LudoGameServer.h | 22 ++++++++++ server/src/main.cpp | 13 ++++++ 16 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 game/src/multiplayer/LudoGameClient.cpp create mode 100644 game/src/multiplayer/LudoGameClient.h create mode 100644 game/src/multiplayer/LudoNetworkEvents.h create mode 100644 server/CMakeLists.txt create mode 100644 server/src/LudoGameServer.cpp create mode 100644 server/src/LudoGameServer.h create mode 100644 server/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b445e6c..2d907b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -319,11 +319,16 @@ add_library(GameLib STATIC target_include_directories(GameLib PUBLIC game/src) target_link_libraries(GameLib PUBLIC Engine) +add_subdirectory(server) + # --- Executable --- add_executable(ColorRace game/src/main.cpp game/src/mainMenu/layer/MainMenuUiLayer.cpp game/src/mainMenu/layer/MainMenuUiLayer.h + game/src/multiplayer/LudoGameClient.cpp + game/src/multiplayer/LudoGameClient.h + game/src/multiplayer/LudoNetworkEvents.h ) target_link_libraries(ColorRace PRIVATE diff --git a/engine/src/core/events/EventBus.h b/engine/src/core/events/EventBus.h index 0da47c4..fba112d 100644 --- a/engine/src/core/events/EventBus.h +++ b/engine/src/core/events/EventBus.h @@ -5,6 +5,7 @@ #ifndef COLORRACE_EVENTBUS_H #define COLORRACE_EVENTBUS_H #include +#include namespace engine { class IEventBus { @@ -18,15 +19,22 @@ namespace engine { using Handler = std::function; void subscribe(Handler handler) { + std::lock_guard lock(m_mutex); m_handlers.push_back(handler); } void publish(Event& event) const { - for (const auto& handler : m_handlers) { + std::vector handlersCopy; + { + std::lock_guard lock(m_mutex); + handlersCopy = m_handlers; + } + for (const auto& handler : handlersCopy) { handler(event); } } private: + mutable std::mutex m_mutex; std::vector m_handlers; }; } // engine diff --git a/external/NetworkCore b/external/NetworkCore index f67ce80..944f2f6 160000 --- a/external/NetworkCore +++ b/external/NetworkCore @@ -1 +1 @@ -Subproject commit f67ce801f799aacb9d10b35423417953e954548b +Subproject commit 944f2f63e06e1d368f85c11916046312b1d0067f diff --git a/game/src/ColorRaceApp.cpp b/game/src/ColorRaceApp.cpp index 8591307..04ea4c3 100644 --- a/game/src/ColorRaceApp.cpp +++ b/game/src/ColorRaceApp.cpp @@ -13,6 +13,7 @@ void ColorRaceApp::onUpdate(float deltaTime) { getSceneManager().update(deltaTime); + } void ColorRaceApp::onRender() { diff --git a/game/src/ColorRaceApp.h b/game/src/ColorRaceApp.h index e8f5845..c64d195 100644 --- a/game/src/ColorRaceApp.h +++ b/game/src/ColorRaceApp.h @@ -6,12 +6,15 @@ #define COLORRACE_COLORRACEAPP_H #include "gameLayer/GameLayer.h" #include "GameScene.h" +#include "../../external/NetworkCore/src/client/GameClient.h" #include "core/Application.h" #include "ecs/EntityRegistry.h" #include "ecs/standardComponents/MeshComponent.h" #include "ecs/standardComponents/TransformComponent.h" #include "layer/SceneManager.h" #include "mainMenu/MainMenuScene.h" +#include "multiplayer/LudoGameClient.h" +#include "multiplayer/LudoNetworkEvents.h" #include "renderer/MeshRenderer.h" #include "renderer/entities/Camera.h" #include "renderer/shader/StaticShader.h" @@ -33,12 +36,28 @@ public: config.debugContext = true; return config; } - ColorRaceApp() : Application(makeConfig()) { - getSceneManager().switchTo(std::make_unique(m_applicationEventBus, getInputContextStack(), getKeyboard(), getMouse())); + ColorRaceApp() : Application(makeConfig()), m_networkEventBus(std::make_shared>()) { + getSceneManager().switchTo(std::make_unique(m_applicationEventBus, m_networkEventBus, getInputContextStack(), getKeyboard(), getMouse())); + m_networkEventBus->subscribe([this](NetworkEvents& event) { + std::visit([this](auto&& e) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + engine::net::ClientConfig client_config; + client_config.host = e.host; + client_config.port = e.port; + m_ludoGameClient = std::make_unique(client_config, m_networkEventBus); + m_ludoGameClient->onStart(); + } + }, event); + }); } protected: void onUpdate(float deltaTime) override; void onRender() override; + std::shared_ptr> m_networkEventBus; + +private: + std::unique_ptr m_ludoGameClient; }; diff --git a/game/src/mainMenu/MainMenuScene.cpp b/game/src/mainMenu/MainMenuScene.cpp index 33c344b..78e73f7 100644 --- a/game/src/mainMenu/MainMenuScene.cpp +++ b/game/src/mainMenu/MainMenuScene.cpp @@ -8,7 +8,7 @@ void MainMenuScene::onEnter() { Scene::onEnter(); - addLayer(std::make_unique(m_keyboard, m_mouse, getAnimationEventBus(), m_applicationEventBus, *assetManager)); + addLayer(std::make_unique(m_keyboard, m_mouse, getAnimationEventBus(), m_applicationEventBus, m_networkEventBus, *assetManager)); } std::vector MainMenuScene::getRequiredAssets() const { diff --git a/game/src/mainMenu/MainMenuScene.h b/game/src/mainMenu/MainMenuScene.h index be58bc2..7fe155a 100644 --- a/game/src/mainMenu/MainMenuScene.h +++ b/game/src/mainMenu/MainMenuScene.h @@ -7,13 +7,16 @@ #include #include "layer/Scene.h" +#include "multiplayer/LudoNetworkEvents.h" class MainMenuScene : public engine::Scene { public: - MainMenuScene(std::shared_ptr> applicationEventbus, engine::InputContextStack& inputContext, engine::Keyboard& keyboard, engine::Mouse& mouse) : Scene(std::move(applicationEventbus), inputContext, keyboard, mouse) {} + MainMenuScene(std::shared_ptr> applicationEventbus, std::shared_ptr> networkEventbus, engine::InputContextStack& inputContext, engine::Keyboard& keyboard, engine::Mouse& mouse) : Scene(std::move(applicationEventbus), inputContext, keyboard, mouse), m_networkEventBus(std::move(networkEventbus)) {} void onEnter() override; std::vector getRequiredAssets() const override; +private: + std::shared_ptr> m_networkEventBus; }; diff --git a/game/src/mainMenu/layer/MainMenuUiLayer.cpp b/game/src/mainMenu/layer/MainMenuUiLayer.cpp index 011419c..c5b086e 100644 --- a/game/src/mainMenu/layer/MainMenuUiLayer.cpp +++ b/game/src/mainMenu/layer/MainMenuUiLayer.cpp @@ -62,6 +62,23 @@ void MainMenuUiLayer::onAttachImpl() { auto backgroundTexture = m_assetManager.getTexture("main_menu_background"); backgroundImage = std::make_unique(backgroundTexture, glm::vec2(0,0), glm::vec2(1,1)); + + m_networkEventBus->subscribe([this](NetworkEvents& event) { + std::visit([this](auto&& e) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + if (e.success) { + spdlog::info("Received positive Connection Result"); + m_connectionError = false; + m_closeConnectPopup = true; + } else { + spdlog::info("Received negative Connection Result"); + m_connectionError = true; + m_errorMessage = e.errorMessage; + } + } + }, event); + }); } void MainMenuUiLayer::onDetachImpl() { @@ -74,6 +91,9 @@ void MainMenuUiLayer::renderConnectDialog() { ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("ConnectToServer", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + if (m_closeConnectPopup) { + ImGui::CloseCurrentPopup(); + } ImGui::InputText("Host", m_hostBuffer, sizeof(m_hostBuffer)); ImGui::InputInt("Port", &m_port); @@ -87,6 +107,8 @@ void MainMenuUiLayer::renderConnectDialog() { // ApplicationEvents::ConnectRequested e{ m_hostBuffer, static_cast(m_port) }; // m_appEvents.publish(e); // Popup offen lassen bis Ergebnis feststeht, siehe unten + NetworkEvents connectRequest = ConnectRequest(m_hostBuffer, static_cast(m_port)); + m_networkEventBus->publish(connectRequest); } ImGui::SameLine(); diff --git a/game/src/mainMenu/layer/MainMenuUiLayer.h b/game/src/mainMenu/layer/MainMenuUiLayer.h index 3cf7d6a..17ecca8 100644 --- a/game/src/mainMenu/layer/MainMenuUiLayer.h +++ b/game/src/mainMenu/layer/MainMenuUiLayer.h @@ -8,14 +8,20 @@ #include "layer/Layer.h" #include "loader/assets/AssetManager.h" +#include "multiplayer/LudoNetworkEvents.h" #include "renderer/GuiRenderer.h" #include "renderer/ui/GUITexture.h" class MainMenuUiLayer : public engine::Layer { public: - MainMenuUiLayer(engine::Keyboard &keyboard, engine::Mouse &mouse, engine::EventBus &animationEventBus, std::shared_ptr> applicationEventbus, engine::AssetManager& assetManager) - : Layer(keyboard, mouse, animationEventBus, std::move(applicationEventbus)), m_assetManager(assetManager) { + MainMenuUiLayer(engine::Keyboard &keyboard, + engine::Mouse &mouse, + engine::EventBus &animationEventBus, + std::shared_ptr> applicationEventbus, + std::shared_ptr> networkEventbus, + engine::AssetManager& assetManager) + : Layer(keyboard, mouse, animationEventBus, std::move(applicationEventbus)), m_assetManager(assetManager), m_networkEventBus(std::move(networkEventbus)) { } void onRender() override; @@ -26,6 +32,7 @@ protected: void onDetachImpl() override; private: + std::shared_ptr> m_networkEventBus; engine::AssetManager& m_assetManager; std::unique_ptr backgroundImage; engine::renderer::GuiRenderer m_guiRenderer; @@ -33,6 +40,7 @@ private: char m_hostBuffer[64] = "127.0.0.1"; int m_port = 7777; bool m_connectionError = false; + bool m_closeConnectPopup = false; std::string m_errorMessage; void renderConnectDialog(); diff --git a/game/src/multiplayer/LudoGameClient.cpp b/game/src/multiplayer/LudoGameClient.cpp new file mode 100644 index 0000000..33a7c71 --- /dev/null +++ b/game/src/multiplayer/LudoGameClient.cpp @@ -0,0 +1,5 @@ +// +// Created by sebastian on 06.08.26. +// + +#include "LudoGameClient.h" diff --git a/game/src/multiplayer/LudoGameClient.h b/game/src/multiplayer/LudoGameClient.h new file mode 100644 index 0000000..c30281b --- /dev/null +++ b/game/src/multiplayer/LudoGameClient.h @@ -0,0 +1,47 @@ +// +// Created by sebastian on 06.08.26. +// + +#ifndef COLORRACE_LUDOGAMECLIENT_H +#define COLORRACE_LUDOGAMECLIENT_H +#include + +#include "LudoNetworkEvents.h" +#include "../../../external/NetworkCore/src/client/ApplicationClient.h" +#include "core/events/EventBus.h" +#include "spdlog/spdlog.h" + + +class LudoGameClient : public engine::net::ApplicationClient{ +public: + explicit LudoGameClient(const engine::net::ClientConfig &config, std::shared_ptr> networkEventBus) + : ApplicationClient(config), m_networkEventBus(std::move(networkEventBus)) { + } + +protected: + void onConnected() override { + spdlog::info("LudoGameClient successfully connected to server"); + send({ + {"type", "auth"}, + {"payload", { + {"token", getConfig().token} + }} + }); + NetworkEvents connectResult = ConnectResult(true, ""); + m_networkEventBus->publish(connectResult); + } + + void onDisconnected() override { + std::cout << "Verbindung verloren, versuche Reconnect...\n"; + } + + void onMessage(nlohmann::json message) override { + std::cout << "Empfangen: " << message.dump() << "\n"; + } + +private: + std::shared_ptr> m_networkEventBus; +}; + + +#endif //COLORRACE_LUDOGAMECLIENT_H diff --git a/game/src/multiplayer/LudoNetworkEvents.h b/game/src/multiplayer/LudoNetworkEvents.h new file mode 100644 index 0000000..029d329 --- /dev/null +++ b/game/src/multiplayer/LudoNetworkEvents.h @@ -0,0 +1,24 @@ +// +// Created by sebastian on 06.08.26. +// + +#ifndef COLORRACE_LUDONETWORKEVENTS_H +#define COLORRACE_LUDONETWORKEVENTS_H +#include +#include +#include +#include + + +struct ConnectRequest { + std::string host; + uint16_t port; +}; + +struct ConnectResult { + bool success; + std::string errorMessage; +}; + +using NetworkEvents = std::variant; +#endif //COLORRACE_LUDONETWORKEVENTS_H diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt new file mode 100644 index 0000000..10887bc --- /dev/null +++ b/server/CMakeLists.txt @@ -0,0 +1,17 @@ + + +add_executable(ColorRaceServer + src/main.cpp + src/LudoGameServer.cpp + src/LudoGameServer.h +) + +target_include_directories(ColorRaceServer PRIVATE + src +) + +target_link_libraries(ColorRaceServer PRIVATE + NetworkCore +) + +target_compile_features(ColorRaceServer PRIVATE cxx_std_20) # an deinen tatsächlichen Standard anpassen \ No newline at end of file diff --git a/server/src/LudoGameServer.cpp b/server/src/LudoGameServer.cpp new file mode 100644 index 0000000..0399b1f --- /dev/null +++ b/server/src/LudoGameServer.cpp @@ -0,0 +1,26 @@ +// +// Created by sebastian on 06.08.26. +// + +#include "LudoGameServer.h" + +#include "spdlog/spdlog.h" + +void LudoGameServer::onStart() { + ApplicationServer::onStart(); + m_gameServer.onClientConnected([](engine::net::ClientId clientId) { + spdlog::info("Client connected: {}", clientId); + }); + + m_gameServer.onMessageReceived([this](engine::net::ClientId clientId, const nlohmann::json& msg) { + spdlog::info("Client {}: {}", clientId, msg.dump()); + if (msg.value("type", "") == "Ping") { + m_gameServer.sendTo(clientId, { {"type", "Pong"}, {"payload", nlohmann::json::object()} }); + } + }); +} + +void LudoGameServer::onTick(float deltaTime) { + ApplicationServer::onTick(deltaTime); + m_gameServer.update(); +} diff --git a/server/src/LudoGameServer.h b/server/src/LudoGameServer.h new file mode 100644 index 0000000..e250637 --- /dev/null +++ b/server/src/LudoGameServer.h @@ -0,0 +1,22 @@ +// +// Created by sebastian on 06.08.26. +// + +#ifndef COLORRACE_LUDOGAMESERVER_H +#define COLORRACE_LUDOGAMESERVER_H +#include "../../external/NetworkCore/src/server/ApplicationServer.h" +#include "../../external/NetworkCore/src/server/GameServer.h" + + +class LudoGameServer : public engine::net::ApplicationServer { +public: + explicit LudoGameServer(const uint16_t port, engine::net::ServerConfig& serverConfig) : m_gameServer(port, serverConfig) {}; +protected: + void onStart() override; + void onTick(float deltaTime) override; +private: + engine::net::GameServer m_gameServer; +}; + + +#endif //COLORRACE_LUDOGAMESERVER_H diff --git a/server/src/main.cpp b/server/src/main.cpp new file mode 100644 index 0000000..7bfa1eb --- /dev/null +++ b/server/src/main.cpp @@ -0,0 +1,13 @@ +#include +#include + +#include "LudoGameServer.h" +// +// Created by sebastian on 06.08.26. +// +int main() { + engine::net::ServerConfig config = engine::net::ServerConfig(); + LudoGameServer server = LudoGameServer(4242, config); + server.run(); + return 0; +} \ No newline at end of file