diff --git a/proj-windows/CMakeLists.txt b/proj-windows/CMakeLists.txt index 942ab6e..fd01681 100644 --- a/proj-windows/CMakeLists.txt +++ b/proj-windows/CMakeLists.txt @@ -51,6 +51,8 @@ add_executable(space-game001 ../src/Projectile.h ../src/Projectile.cpp ../src/network/NetworkInterface.h + ../src/network/LocalClient.h + ../src/network/LocalClient.cpp ../src/network/WebSocketClient.h ../src/network/WebSocketClient.cpp ) diff --git a/src/Game.cpp b/src/Game.cpp index 101baf2..974ffe6 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -13,6 +13,12 @@ #include #endif +#ifdef NETWORK +#include "network/WebSocketClient.h" +#else +#include "network/LocalClient.h" +#endif + namespace ZL { #ifdef EMSCRIPTEN @@ -375,6 +381,14 @@ namespace ZL //rockTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", CONST_ZIP_FILE)); std::cout << "Init step 8 " << std::endl; + +#ifdef NETWORK + networkClient = std::make_unique(taskManager.getIOContext()); + networkClient->Connect("127.0.0.1", 8080); +#else + networkClient = std::make_unique(); + networkClient->Connect("", 0); +#endif } void Game::drawCubemap(float skyPercent) @@ -626,6 +640,13 @@ namespace ZL sparkEmitter.update(static_cast(delta)); planetObject.update(static_cast(delta)); + static float pingTimer = 0.0f; + pingTimer += delta; + if (pingTimer >= 1000.0f) { + networkClient->Send("PING"); + pingTimer = 0.0f; + } + if (Environment::tapDownHold) { float diffx = Environment::tapDownCurrentPos(0) - Environment::tapDownStartPos(0); @@ -765,7 +786,6 @@ namespace ZL } } uiManager.update(static_cast(delta)); - //#endif lastTickCount = newTickCount; } } @@ -844,31 +864,6 @@ namespace ZL if (event.type == SDL_MOUSEBUTTONDOWN) { int mx = event.button.x; int my = event.button.y; - - uiManager.onMouseDown(mx, my); - - bool uiHandled = false; - - for (const auto& button : uiManager.findButton("") ? std::vector>{} : std::vector>{}) { - (void)button; - } - - auto pressedSlider = [&]() -> std::shared_ptr { - for (const auto& slider : uiManager.findSlider("") ? std::vector>{} : std::vector>{}) { - (void)slider; - } - return nullptr; - }(); - - if (!uiManager.isUiInteraction()) { - Environment::tapDownHold = true; - - Environment::tapDownStartPos(0) = mx; - Environment::tapDownStartPos(1) = my; - - Environment::tapDownCurrentPos(0) = mx; - Environment::tapDownCurrentPos(1) = my; - } handleDown(mx, my); } if (event.type == SDL_MOUSEBUTTONUP) { @@ -884,6 +879,7 @@ namespace ZL handleMotion(mx, my); } + /* if (event.type == SDL_MOUSEWHEEL) { static const float zoomstep = 2.0f; if (event.wheel.y > 0) { @@ -916,11 +912,12 @@ namespace ZL Environment::shipVelocity -= 50.f; //x = x - 2.0; } - } + }*/ #endif } render(); mainThreadHandler.processMainThreadTasks(); + networkClient->Poll(); } void Game::handleDown(int mx, int my) @@ -953,6 +950,7 @@ namespace ZL Environment::tapDownCurrentPos(1) = my; } } + void Game::handleUp(int mx, int my) { int uiX = mx; diff --git a/src/Game.h b/src/Game.h index 40cc6c8..b0aa524 100644 --- a/src/Game.h +++ b/src/Game.h @@ -8,6 +8,7 @@ #include "UiManager.h" #include "Projectile.h" #include "utils/TaskManager.h" +#include "network/NetworkInterface.h" #include namespace ZL { @@ -34,6 +35,8 @@ namespace ZL { Renderer renderer; TaskManager taskManager; MainThreadHandler mainThreadHandler; + + std::unique_ptr networkClient; private: void processTickCount(); void drawScene(); diff --git a/src/network/LocalClient.cpp b/src/network/LocalClient.cpp new file mode 100644 index 0000000..57d3266 --- /dev/null +++ b/src/network/LocalClient.cpp @@ -0,0 +1,26 @@ +#include "LocalClient.h" +#include + + +namespace ZL { + + void LocalClient::Connect(const std::string& host, uint16_t port) { + } + + void LocalClient::Poll() { + + if (!messageQueue.empty()) { + std::string msg = messageQueue.front(); + messageQueue.pop(); + std::cout << "LocalClient received message: " << msg << std::endl; + } + } + + void LocalClient::Send(const std::string& message) { + + if (message == "PING") + { + messageQueue.push("PONG"); + } + } +} \ No newline at end of file diff --git a/src/network/LocalClient.h b/src/network/LocalClient.h new file mode 100644 index 0000000..3e9b15e --- /dev/null +++ b/src/network/LocalClient.h @@ -0,0 +1,21 @@ +#pragma once + +// WebSocketClient.h +#include "NetworkInterface.h" +#include + +namespace ZL { + class LocalClient : public INetworkClient { + private: + std::queue messageQueue; + public: + void Connect(const std::string& host, uint16_t port) override; + + void Poll() override; + + void Send(const std::string& message) override; + + bool IsConnected() const override { return true; } + int GetClientId() const { return 1; } + }; +} \ No newline at end of file diff --git a/src/network/WebSocketClient.cpp b/src/network/WebSocketClient.cpp index e69de29..5f0784e 100644 --- a/src/network/WebSocketClient.cpp +++ b/src/network/WebSocketClient.cpp @@ -0,0 +1,73 @@ +#include "WebSocketClient.h" +#include + + +namespace ZL { + + void WebSocketClient::Connect(const std::string& host, uint16_t port) { + try { + boost::asio::ip::tcp::resolver resolver(ioc_); + auto const results = resolver.resolve(host, std::to_string(port)); + + ws_ = std::make_unique>(ioc_); + + // Выполняем синхронный коннект и handshake для простоты старта + boost::beast::get_lowest_layer(*ws_).connect(results); + ws_->handshake(host, "/"); + + connected = true; + + // Запускаем асинхронное чтение в пуле потоков TaskManager + startAsyncRead(); + + } + catch (std::exception& e) { + std::cerr << "Network Error: " << e.what() << std::endl; + } + } + + void WebSocketClient::startAsyncRead() { + ws_->async_read(buffer_, [this](boost::beast::error_code ec, std::size_t bytes) { + if (!ec) { + std::string msg = boost::beast::buffers_to_string(buffer_.data()); + buffer_.consume(bytes); + + // Тяжелая десериализация происходит здесь (в потоке TaskManager) + processIncomingMessage(msg); + + startAsyncRead(); + } + else { + connected = false; + } + }); + } + + void WebSocketClient::processIncomingMessage(const std::string& msg) { + // Логика парсинга... + if (msg.rfind("ID:", 0) == 0) { + clientId = std::stoi(msg.substr(3)); + } + + // Безопасно кладем в очередь для главного потока + std::lock_guard lock(queueMutex); + messageQueue.push(msg); + } + + void WebSocketClient::Poll() { + // Забираем сообщения из очереди и обрабатываем их в главном потоке + std::lock_guard lock(queueMutex); + while (!messageQueue.empty()) { + std::string msg = messageQueue.front(); + messageQueue.pop(); + // Тут можно вызвать колбэки игры или обновить состояние + } + } + + void WebSocketClient::Send(const std::string& message) { + if (!connected) return; + // Отправку тоже можно сделать асинхронной, чтобы не ждать в главном потоке + auto ss = std::make_shared(message); + ws_->async_write(boost::asio::buffer(*ss), [ss](boost::beast::error_code, std::size_t) {}); + } +} \ No newline at end of file diff --git a/src/network/WebSocketClient.h b/src/network/WebSocketClient.h index 7add3fe..ffaaecc 100644 --- a/src/network/WebSocketClient.h +++ b/src/network/WebSocketClient.h @@ -3,25 +3,38 @@ // WebSocketClient.h #include "NetworkInterface.h" #include +#include +#include +#include +#include namespace ZL { class WebSocketClient : public INetworkClient { private: + // Переиспользуем io_context из TaskManager + boost::asio::io_context& ioc_; + + // Объекты переехали в члены класса + std::unique_ptr> ws_; + boost::beast::flat_buffer buffer_; + std::queue messageQueue; + std::mutex queueMutex; // Защита для messageQueue + bool connected = false; int clientId = -1; - public: - void Connect(const std::string& host, uint16_t port) override { - // В Emscripten здесь будет emscripten_websocket_new - // В Desktop - boost::beast::websocket::stream - } + void startAsyncRead(); + void processIncomingMessage(const std::string& msg); - void Poll() override { - // Читаем данные из сокета. - // Если получили ID: clientId = parseId(msg); connected = true; - // Если получили "PONG": логируем задержку. - } + public: + explicit WebSocketClient(boost::asio::io_context& ioc) : ioc_(ioc) {} + + void Connect(const std::string& host, uint16_t port) override; + + void Poll() override; + + void Send(const std::string& message) override; bool IsConnected() const override { return connected; } int GetClientId() const { return clientId; } diff --git a/src/utils/TaskManager.h b/src/utils/TaskManager.h index f1c5eb3..66423f8 100644 --- a/src/utils/TaskManager.h +++ b/src/utils/TaskManager.h @@ -24,6 +24,11 @@ namespace ZL { // Graceful shutdown ~TaskManager(); + + boost::asio::io_context& getIOContext() + { + return ioContext; + } };