From 6aff22ec53f43d342375f1f6dab376ba0bb84b68 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sun, 18 Jan 2026 11:29:15 +0300 Subject: [PATCH] Working on cleanup --- server/server.cpp | 179 ++----------------------- src/Game.cpp | 131 +------------------ src/Game.h | 3 +- src/network/ClientState.h | 105 +++++++++++---- src/network/NetworkInterface.h | 60 +-------- src/network/WebSocketClient.cpp | 223 ++------------------------------ src/network/WebSocketClient.h | 6 +- 7 files changed, 112 insertions(+), 595 deletions(-) diff --git a/server/server.cpp b/server/server.cpp index c3f025e..06f2331 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -38,25 +38,18 @@ class Session : public std::enable_shared_from_this { websocket::stream ws_; beast::flat_buffer buffer_; int id_; - //ClientState state_; - std::vector timedClientStates; - - + + ClientStateInterval timedClientStates; void process_message(const std::string& msg) { auto now_server = std::chrono::system_clock::now(); auto parts = split(msg, ':'); - if (parts.empty()) return; - std::cout << msg << std::endl; - - auto now_ms = std::chrono::duration_cast( - now_server.time_since_epoch() - ).count(); - - //Apply server delay: - now_ms -= SERVER_DELAY; + if (parts.size() < 16) + { + throw std::runtime_error("Unknown message type received, too small"); + } uint64_t clientTimestamp = std::stoull(parts[1]); @@ -64,51 +57,19 @@ class Session : public std::enable_shared_from_this { receivedState.id = id_; - /* - // " " - long long deltaMs = 0.0f; - if (state_.lastUpdateServerTime.time_since_epoch().count() > 0) { - deltaMs = (clientTimestamp - now_ms); - } - - - if (deltaMs > 0) state_.simulate_physics(deltaMs);*/ - std::chrono::system_clock::time_point uptime_timepoint{ std::chrono::duration_cast(std::chrono::milliseconds(clientTimestamp)) }; receivedState.lastUpdateServerTime = uptime_timepoint; - - // - if (parts[0] == "ROT") { - receivedState.discreteAngle = std::stoi(parts[2]); - receivedState.discreteMag = std::stof(parts[3]); - receivedState.handle_full_sync(parts, 4); - //std::cout << "ROT id = " << this->id_ << " discreteMag=" << state_.discreteMag << std::endl; - //receivedState.apply_lag_compensation(now_server); - //receivedState.lastUpdateServerTime = now_server; - retranslateMessage(msg); - } - else if (parts[0] == "VEL") { - receivedState.selectedVelocity = std::stoi(parts[2]); - receivedState.handle_full_sync(parts, 3); - //receivedState.apply_lag_compensation(now_server); - //receivedState.lastUpdateServerTime = now_server; - retranslateMessage(msg); - } - else if (parts[0] == "PING") { + if (parts[0] == "UPD") { receivedState.handle_full_sync(parts, 2); retranslateMessage(msg); - //receivedState.apply_lag_compensation(now_server); - //receivedState.lastUpdateServerTime = now_server; } - timedClientStates.push_back(receivedState); - - auto cutoff_time = now_server - std::chrono::milliseconds(CUTOFF_TIME); - - while (timedClientStates.size() > 0 && timedClientStates[0].lastUpdateServerTime < cutoff_time) + else { - timedClientStates.erase(timedClientStates.begin()); + throw std::runtime_error("Unknown message type received: " + parts[0]); } + + timedClientStates.add_state(receivedState); } void retranslateMessage(const std::string& msg) @@ -131,12 +92,7 @@ public: void init() { - //state_.lastUpdateServerTime = std::chrono::system_clock::now(); } - /* - std::string get_state_string() { - return state_.get_state_string(id_); - }*/ void run() { @@ -154,76 +110,6 @@ public: }); } - bool canFetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) - { - if (timedClientStates.empty()) - { - return false; - } - if (timedClientStates[0].lastUpdateServerTime > targetTime) - { - return false; - } - - return true; - } - - ClientState fetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) { - - ClientState closestState; - - if (timedClientStates.empty()) - { - throw std::runtime_error("No timed client states available"); - return closestState; - } - if (timedClientStates[0].lastUpdateServerTime > targetTime) - { - throw std::runtime_error("Found time but it is in future"); - return closestState; - } - if (timedClientStates.size() == 1) - { - closestState = timedClientStates[0]; - closestState.apply_lag_compensation(targetTime); - return closestState; - } - - - for (size_t i = 0; i < timedClientStates.size() - 1; ++i) - { - const auto& earlierState = timedClientStates[i]; - const auto& laterState = timedClientStates[i + 1]; - if (earlierState.lastUpdateServerTime <= targetTime && laterState.lastUpdateServerTime >= targetTime) - { - closestState = earlierState; - closestState.apply_lag_compensation(targetTime); - return closestState; - } - } - - closestState = timedClientStates[timedClientStates.size() - 1]; - closestState.apply_lag_compensation(targetTime); - return closestState; - } - - void tick_physics_global(std::chrono::system_clock::time_point now) { - /*long long deltaMs = 0; - - - // , - if (state_.lastUpdateServerTime.time_since_epoch().count() > 0) { - deltaMs = std::chrono::duration_cast( - now - state_.lastUpdateServerTime - ).count(); - } - - if (deltaMs > 0) { - state_.simulate_physics(deltaMs); - state_.lastUpdateServerTime = now; // - }*/ - } - void send_message(std::string msg) { auto ss = std::make_shared(std::move(msg)); ws_.async_write(net::buffer(*ss), [ss](beast::error_code, std::size_t) {}); @@ -256,52 +142,13 @@ private: }; void update_world(net::steady_timer& timer, net::io_context& ioc) { - auto now = std::chrono::system_clock::now(); - //Apply server delay - now -= std::chrono::milliseconds(SERVER_DELAY); + // TODO: Renew game state - /* { - std::lock_guard lock(g_sessions_mutex); - - // 1. - for (auto& session : g_sessions) { - session->tick_physics_global(now); - } - - static auto last_broadcast = now; - if (std::chrono::duration(now - last_broadcast).count() >= 1.0f) { - last_broadcast = now; - - auto now_ms = std::chrono::duration_cast( - now.time_since_epoch() - ).count(); - - // - std::string snapshot = "WORLD_UPDATE|"; - snapshot += std::to_string(now_ms) + "|"; - snapshot += std::to_string(g_sessions.size()) + "|"; - for (size_t i = 0; i < g_sessions.size(); ++i) { - if (g_sessions[i]->canFetchClientStateAtTime(now)) - { - snapshot += g_sessions[i]->fetchClientStateAtTime(now).get_state_string(); - if (i < g_sessions.size() - 1) snapshot += ";"; // - } - } - - // - for (auto& session : g_sessions) { - session->send_message(snapshot); - } - } - - }*/ - - // : (50), (1000 ) timer.expires_after(std::chrono::milliseconds(50)); timer.async_wait([&](const boost::system::error_code& ec) { if (!ec) update_world(timer, ioc); - }); + }); } int main() { diff --git a/src/Game.cpp b/src/Game.cpp index 280061a..d63b260 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -587,7 +587,6 @@ namespace ZL // Итерируемся по актуальным данным из extrapolateRemotePlayers for (auto const& [id, remotePlayer] : latestRemotePlayers) { - //if (id == networkClient->GetClientId()) continue; // Не рисуем себя через этот цикл if (!remotePlayer.canFetchClientStateAtTime(now)) { @@ -612,28 +611,6 @@ namespace ZL // 3. Поворот врага renderer.RotateMatrix(playerState.rotation); - /* - - // 1. Камера и вид игрока - renderer.TranslateMatrix({ 0, 0, -1.0f * Environment::zoom }); - renderer.RotateMatrix(Environment::inverseShipMatrix); - - // 2. Относительная позиция (ОЧЕНЬ ВАЖНО) - // Мы перемещаем объект в позицию (Враг - Я) - // Если враг в 44928, а я в 45000, результат будет -72 по Z. - // Поскольку камера смотрит в -Z, корабль должен быть ПЕРЕД нами. - Eigen::Vector3f relativePos = playerState.position - Environment::shipPosition; - renderer.TranslateMatrix(relativePos); - - // 3. Поворот врага - renderer.RotateMatrix(playerState.rotation); - - // 4. Смещение самого меша (если оно есть в drawShip, оно должно быть и тут) - // В твоем drawShip() есть: renderer.TranslateMatrix({ 0, -6.f, 0 }); - renderer.TranslateMatrix({ 0, -6.f, 0 }); - - */ - renderer.DrawVertexRenderStruct(spaceship); renderer.PopMatrix(); } @@ -673,14 +650,11 @@ namespace ZL sparkEmitter.update(static_cast(delta)); planetObject.update(static_cast(delta)); - extrapolateRemotePlayers(); - - //bool sendRotation = false; static float pingTimer = 0.0f; pingTimer += delta; if (pingTimer >= 1000.0f) { - std::string pingMsg = "PING:" + std::to_string(now_ms) + ":" + formPingMessageContent(); + std::string pingMsg = "UPD:" + std::to_string(now_ms) + ":" + formPingMessageContent(); networkClient->Send(pingMsg); std::cout << "Sending: " << pingMsg << std::endl; @@ -691,8 +665,7 @@ namespace ZL { Environment::shipSelectedVelocity = newShipVelocity; - std::string msg = "VEL:" + std::to_string(now_ms) + ":" + std::to_string(Environment::shipSelectedVelocity); - msg = msg + ":" + formPingMessageContent(); + std::string msg = "UPD:" + std::to_string(now_ms) + ":" + formPingMessageContent(); networkClient->Send(msg); //} } @@ -725,22 +698,9 @@ namespace ZL Environment::lastSentAngle = discreteAngle; Environment::lastSentMagnitude = discreteMag; - std::string msg = "ROT:" + std::to_string(now_ms) + ":" + std::to_string(Environment::lastSentAngle) + ":" + std::to_string(Environment::lastSentMagnitude); - msg = msg + ":" + formPingMessageContent(); + std::string msg = "UPD:" + std::to_string(now_ms) + ":" + formPingMessageContent(); networkClient->Send(msg); std::cout << "Sending: " << msg << std::endl; - - //sendRotation = true; - /*auto now_ms = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch() - ).count(); - - // Формируем сетевой пакет - // Нам нужно отправить: дискретный угол, дискретную силу и текущую матрицу/позицию для синхронизации - std::string msg = "ROT:" + std::to_string(now_ms) + ":" + std::to_string(discreteAngle) + ":" + std::to_string(discreteMag); - msg = msg + ":" + formPingMessageContent(); - networkClient->Send(msg); - std::cout << "Sending: " << msg << std::endl;*/ } // 4. Логика вращения (угловое ускорение) @@ -799,23 +759,9 @@ namespace ZL Environment::lastSentAngle = discreteAngle; Environment::lastSentMagnitude = discreteMag; - std::string msg = "ROT:" + std::to_string(now_ms) + ":" + std::to_string(Environment::lastSentAngle) + ":" + std::to_string(Environment::lastSentMagnitude); - msg = msg + ":" + formPingMessageContent(); + std::string msg = "UPD:" + std::to_string(now_ms) + ":" + formPingMessageContent(); networkClient->Send(msg); std::cout << "Sending: " << msg << std::endl; - - //sendRotation = true; - /* - auto now_ms = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch() - ).count(); - - // Формируем сетевой пакет - // Нам нужно отправить: дискретный угол, дискретную силу и текущую матрицу/позицию для синхронизации - std::string msg = "ROT:" + std::to_string(now_ms) + ":" + std::to_string(discreteAngle) + ":" + std::to_string(discreteMag); - msg = msg + ":" + formPingMessageContent(); - networkClient->Send(msg); - std::cout << "Sending: " << msg << std::endl;*/ } @@ -861,7 +807,6 @@ namespace ZL } } - //std::cout << "shipVelocity=" << Environment::shipVelocity << " delta=" << delta << std::endl; // Движение вперед (существующая логика) if (fabs(Environment::shipVelocity) > 0.01f) { @@ -870,20 +815,6 @@ namespace ZL Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted; } - /* - if (sendRotation) - { - sendRotation = false; - - // Формируем сетевой пакет - // Нам нужно отправить: дискретный угол, дискретную силу и текущую матрицу/позицию для синхронизации - std::string msg = "ROT:" + std::to_string(now_ms) + ":" + std::to_string(Environment::lastSentAngle) + ":" + std::to_string(Environment::lastSentMagnitude); - msg = msg + ":" + formPingMessageContent(); - networkClient->Send(msg); - std::cout << "Sending: " << msg << std::endl; - } - */ - for (auto& p : projectiles) { if (p && p->isActive()) { p->update(static_cast(delta), renderer); @@ -1166,59 +1097,6 @@ namespace ZL } } - void Game::extrapolateRemotePlayers() { - - /*auto now = std::chrono::system_clock::now(); - - //Apply server delay: - now -= std::chrono::milliseconds(CLIENT_DELAY); - - latestRemotePlayers = networkClient->getRemotePlayers(); - - for (auto& [id, rp] : latestRemotePlayers) { - - }*/ - - /* - * - auto now = std::chrono::system_clock::now(); - - //Apply server delay: - now -= std::chrono::milliseconds(CLIENT_DELAY); - - latestRemotePlayers = networkClient->getRemotePlayers(); - - for (auto& [id, rp] : latestRemotePlayers) { - // 1. Рассчитываем, сколько времени прошло с момента получения последнего пакета (в секундах) - - auto deltaMs = std::chrono::duration_cast( - now - rp.lastUpdateServerTime - ).count(); - - //float age_s = std::chrono::duration(now - rp.lastUpdateServerTime).count(); - - // Ограничим экстраполяцию (например, не более 2 секунд), - // чтобы в случае лага корабли не улетали в бесконечность - if (deltaMs < 0) deltaMs = 0; - if (deltaMs > 2000) deltaMs = 2000; - - // 2. Сбрасываем физическое состояние rp.state в значения из последнего пакета - // (Это важно: мы всегда экстраполируем от последнего достоверного серверного состояния) - // В WebSocketClient::updateRemotePlayer нужно убедиться, что rp.state обновляется данными из пакета. - - // 3. Вызываем физику, чтобы "догнать" реальное время - // Мы передаем age_s как один большой шаг симуляции - rp.simulate_physics(deltaMs); - rp.lastUpdateServerTime = now; - - // Теперь rp.state.position и rp.state.rotation содержат актуальные - // предсказанные данные для рендеринга в текущем кадре. - } - - networkClient->updateRemotePlayers(latestRemotePlayers); - */ - } - std::string Game::formPingMessageContent() { Eigen::Quaternionf q(Environment::shipMatrix); @@ -1238,7 +1116,6 @@ namespace ZL + std::to_string(Environment::lastSentMagnitude) + ":" // Используем те же static переменные из блока ROT + std::to_string(Environment::lastSentAngle); - return pingMsg; } diff --git a/src/Game.h b/src/Game.h index 1b68e81..37ec3a1 100644 --- a/src/Game.h +++ b/src/Game.h @@ -51,7 +51,6 @@ namespace ZL { void handleUp(int mx, int my); void handleMotion(int mx, int my); - void extrapolateRemotePlayers(); std::string formPingMessageContent(); SDL_Window* window; @@ -65,7 +64,7 @@ namespace ZL { std::vector boxCoordsArr; std::vector boxRenderArr; - std::unordered_map latestRemotePlayers; + std::unordered_map latestRemotePlayers; float newShipVelocity = 0; diff --git a/src/network/ClientState.h b/src/network/ClientState.h index ea11745..711533d 100644 --- a/src/network/ClientState.h +++ b/src/network/ClientState.h @@ -14,8 +14,9 @@ constexpr float SHIP_ACCEL = 1.0f * 1000.0f; constexpr float ROTATION_SENSITIVITY = 0.002f; constexpr long long SERVER_DELAY = 0; //ms -constexpr long long CLIENT_DELAY = 1500; //ms +constexpr long long CLIENT_DELAY = 200; //ms constexpr long long CUTOFF_TIME = 5000; //ms + struct ClientState { int id = 0; Eigen::Vector3f position = { 0, 0, 45000.0f }; @@ -83,12 +84,9 @@ struct ClientState { Eigen::Quaternionf rotateQuat(Eigen::AngleAxisf(deltaAlpha, axis)); rotation = rotation * rotateQuat.toRotationMatrix(); - //std::cout << "Rotating ship. d="<< delta <<" DeltaAlpha: " << deltaAlpha << ", Axis: [" << axis.x() << ", " << axis.y() << ", " << axis.z() << "]\n"; - } - else { - //std::cout << "NOT Rotating ship. speedScale=" << speedScale << " discreteMag=" << discreteMag << "\n"; } + // 4. Линейное изменение линейной скорости float shipDesiredVelocity = selectedVelocity * 100.f; @@ -136,27 +134,10 @@ struct ClientState { } } - std::string get_state_string() { - // Используем кватернион для передачи вращения (4 числа вместо 9) - Eigen::Quaternionf q(rotation); - - std::string s = std::to_string(id) + "," - + std::to_string(position.x()) + "," + std::to_string(position.y()) + "," + std::to_string(position.z()) + "," - + std::to_string(q.w()) + "," + std::to_string(q.x()) + "," + std::to_string(q.y()) + "," + std::to_string(q.z()) + "," - + std::to_string(velocity) + "," - + std::to_string(currentAngularVelocity.x()) + "," + std::to_string(currentAngularVelocity.y()) + "," + std::to_string(currentAngularVelocity.z()) + "," - + std::to_string(selectedVelocity) + "," - + std::to_string(discreteMag) + "," - + std::to_string(discreteAngle); - return s; - } - void handle_full_sync(const std::vector& parts, int startFrom) { // Позиция position = { std::stof(parts[startFrom]), std::stof(parts[startFrom+1]), std::stof(parts[startFrom+2]) }; - // Для вращения клиент должен прислать либо кватернион, либо углы Эйлера. - // Предположим, мы передаем 4 значения кватерниона для экономии: Eigen::Quaternionf q( std::stof(parts[startFrom+3]), std::stof(parts[startFrom+4]), @@ -174,3 +155,83 @@ struct ClientState { discreteAngle = std::stoi(parts[startFrom+13]); } }; + + +struct ClientStateInterval +{ + std::vector timedStates; + + void add_state(const ClientState& state) + { + auto nowTime = std::chrono::system_clock::now(); + + if (timedStates.size() > 0 && timedStates[timedStates.size() - 1].lastUpdateServerTime == state.lastUpdateServerTime) + { + timedStates[timedStates.size() - 1] = state; + } + else + { + timedStates.push_back(state); + } + + auto cutoff_time = nowTime - std::chrono::milliseconds(CUTOFF_TIME); + + while (timedStates.size() > 0 && timedStates[0].lastUpdateServerTime < cutoff_time) + { + timedStates.erase(timedStates.begin()); + } + } + + bool canFetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const + { + if (timedStates.empty()) + { + return false; + } + if (timedStates[0].lastUpdateServerTime > targetTime) + { + return false; + } + + return true; + } + + ClientState fetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const { + + ClientState closestState; + + if (timedStates.empty()) + { + throw std::runtime_error("No timed client states available"); + return closestState; + } + if (timedStates[0].lastUpdateServerTime > targetTime) + { + throw std::runtime_error("Found time but it is in future"); + return closestState; + } + if (timedStates.size() == 1) + { + closestState = timedStates[0]; + closestState.apply_lag_compensation(targetTime); + return closestState; + } + + + for (size_t i = 0; i < timedStates.size() - 1; ++i) + { + const auto& earlierState = timedStates[i]; + const auto& laterState = timedStates[i + 1]; + if (earlierState.lastUpdateServerTime <= targetTime && laterState.lastUpdateServerTime >= targetTime) + { + closestState = earlierState; + closestState.apply_lag_compensation(targetTime); + return closestState; + } + } + + closestState = timedStates[timedStates.size() - 1]; + closestState.apply_lag_compensation(targetTime); + return closestState; + } +}; diff --git a/src/network/NetworkInterface.h b/src/network/NetworkInterface.h index c50f0f9..03f93a5 100644 --- a/src/network/NetworkInterface.h +++ b/src/network/NetworkInterface.h @@ -7,64 +7,6 @@ // NetworkInterface.h - namespace ZL { - struct RemotePlayer - { - std::vector timedRemoteStates; - - bool canFetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const - { - if (timedRemoteStates.empty()) - { - return false; - } - if (timedRemoteStates[0].lastUpdateServerTime > targetTime) - { - return false; - } - - return true; - } - - ClientState fetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const { - - ClientState closestState; - - if (timedRemoteStates.empty()) - { - throw std::runtime_error("No timed client states available"); - return closestState; - } - if (timedRemoteStates[0].lastUpdateServerTime > targetTime) - { - throw std::runtime_error("Found time but it is in future"); - return closestState; - } - if (timedRemoteStates.size() == 1) - { - closestState = timedRemoteStates[0]; - closestState.apply_lag_compensation(targetTime); - return closestState; - } - - - for (size_t i = 0; i < timedRemoteStates.size() - 1; ++i) - { - const auto& earlierState = timedRemoteStates[i]; - const auto& laterState = timedRemoteStates[i + 1]; - if (earlierState.lastUpdateServerTime <= targetTime && laterState.lastUpdateServerTime >= targetTime) - { - closestState = earlierState; - closestState.apply_lag_compensation(targetTime); - return closestState; - } - } - - closestState = timedRemoteStates[timedRemoteStates.size() - 1]; - closestState.apply_lag_compensation(targetTime); - return closestState; - } - }; - class INetworkClient { public: virtual ~INetworkClient() = default; @@ -72,6 +14,6 @@ namespace ZL { virtual void Send(const std::string& message) = 0; virtual bool IsConnected() const = 0; virtual void Poll() = 0; // - virtual std::unordered_map getRemotePlayers() = 0; + virtual std::unordered_map getRemotePlayers() = 0; }; } diff --git a/src/network/WebSocketClient.cpp b/src/network/WebSocketClient.cpp index d8d153c..f7a0790 100644 --- a/src/network/WebSocketClient.cpp +++ b/src/network/WebSocketClient.cpp @@ -43,10 +43,7 @@ namespace ZL { if (!ec) { std::string msg = boost::beast::buffers_to_string(buffer_.data()); buffer_.consume(bytes); - - // ( TaskManager) processIncomingMessage(msg); - startAsyncRead(); } else { @@ -98,52 +95,8 @@ namespace ZL { std::chrono::system_clock::time_point uptime_timepoint{ std::chrono::duration_cast(std::chrono::milliseconds(sentTime)) }; remoteState.lastUpdateServerTime = uptime_timepoint; - - if (subType == "VEL") { - remoteState.selectedVelocity = std::stoi(parts[4]); - int startFrom = 5; - remoteState.position = { std::stof(parts[startFrom]), std::stof(parts[startFrom + 1]), std::stof(parts[startFrom + 2]) }; - Eigen::Quaternionf q( - std::stof(parts[startFrom + 3]), - std::stof(parts[startFrom + 4]), - std::stof(parts[startFrom + 5]), - std::stof(parts[startFrom + 6])); - remoteState.rotation = q.toRotationMatrix(); - - remoteState.currentAngularVelocity = Eigen::Vector3f{ - std::stof(parts[startFrom + 7]), - std::stof(parts[startFrom + 8]), - std::stof(parts[startFrom + 9]) }; - remoteState.velocity = std::stof(parts[startFrom + 10]); - remoteState.selectedVelocity = std::stoi(parts[startFrom + 11]); - remoteState.discreteMag = std::stof(parts[startFrom + 12]); - remoteState.discreteAngle = std::stoi(parts[startFrom + 13]); - - } - else if (subType == "ROT") { - remoteState.discreteAngle = std::stoi(parts[4]); - remoteState.discreteMag = std::stof(parts[5]); - int startFrom = 6; - remoteState.position = { std::stof(parts[startFrom]), std::stof(parts[startFrom + 1]), std::stof(parts[startFrom + 2]) }; - Eigen::Quaternionf q( - std::stof(parts[startFrom + 3]), - std::stof(parts[startFrom + 4]), - std::stof(parts[startFrom + 5]), - std::stof(parts[startFrom + 6])); - remoteState.rotation = q.toRotationMatrix(); - - remoteState.currentAngularVelocity = Eigen::Vector3f{ - std::stof(parts[startFrom + 7]), - std::stof(parts[startFrom + 8]), - std::stof(parts[startFrom + 9]) }; - remoteState.velocity = std::stof(parts[startFrom + 10]); - remoteState.selectedVelocity = std::stoi(parts[startFrom + 11]); - remoteState.discreteMag = std::stof(parts[startFrom + 12]); - remoteState.discreteAngle = std::stoi(parts[startFrom + 13]); - } - else if (subType == "PING") { - //remoteState.discreteAngle = std::stoi(parts[4]); - //remoteState.discreteMag = std::stof(parts[5]); + + if (subType == "UPD") { int startFrom = 4; remoteState.position = { std::stof(parts[startFrom]), std::stof(parts[startFrom + 1]), std::stof(parts[startFrom + 2]) }; Eigen::Quaternionf q( @@ -162,181 +115,21 @@ namespace ZL { remoteState.discreteMag = std::stof(parts[startFrom + 12]); remoteState.discreteAngle = std::stoi(parts[startFrom + 13]); } + else + { + throw std::runtime_error("Unknown EVENT subtype: " + subType); + } { std::lock_guard pLock(playersMutex); auto& rp = remotePlayers[remoteId]; - - - if (rp.timedRemoteStates.size() > 0 && rp.timedRemoteStates[rp.timedRemoteStates.size() - 1].lastUpdateServerTime == remoteState.lastUpdateServerTime) - { - rp.timedRemoteStates[rp.timedRemoteStates.size() - 1] = remoteState; - } - else - { - rp.timedRemoteStates.push_back(remoteState); - } - - //rp.timedRemoteStates.push_back(remoteState); - - auto cutoff_time = nowTime - std::chrono::milliseconds(CUTOFF_TIME); - - while (rp.timedRemoteStates.size() > 0 && rp.timedRemoteStates[0].lastUpdateServerTime < cutoff_time) - { - rp.timedRemoteStates.erase(rp.timedRemoteStates.begin()); - } + rp.add_state(remoteState); } - - - - - - - /* - std::lock_guard pLock(playersMutex); - if (remotePlayers.count(remoteId)) { - auto& rp = remotePlayers[remoteId]; - - if (subType == "VEL") { - rp.selectedVelocity = std::stoi(parts[4]); - int startFrom = 5; - rp.position = { std::stof(parts[startFrom]), std::stof(parts[startFrom+1]), std::stof(parts[startFrom+2]) }; - Eigen::Quaternionf q( - std::stof(parts[startFrom + 3]), - std::stof(parts[startFrom + 4]), - std::stof(parts[startFrom + 5]), - std::stof(parts[startFrom + 6])); - rp.rotation = q.toRotationMatrix(); - - rp.currentAngularVelocity = Eigen::Vector3f{ - std::stof(parts[startFrom + 7]), - std::stof(parts[startFrom + 8]), - std::stof(parts[startFrom + 9]) }; - rp.velocity = std::stof(parts[startFrom + 10]); - rp.selectedVelocity = std::stoi(parts[startFrom + 11]); - rp.discreteMag = std::stof(parts[startFrom + 12]); - rp.discreteAngle = std::stoi(parts[startFrom + 13]); - - } - else if (subType == "ROT") { - rp.discreteAngle = std::stoi(parts[4]); - rp.discreteMag = std::stof(parts[5]); - int startFrom = 6; - rp.position = { std::stof(parts[startFrom]), std::stof(parts[startFrom + 1]), std::stof(parts[startFrom + 2]) }; - Eigen::Quaternionf q( - std::stof(parts[startFrom + 3]), - std::stof(parts[startFrom + 4]), - std::stof(parts[startFrom + 5]), - std::stof(parts[startFrom + 6])); - rp.rotation = q.toRotationMatrix(); - - rp.currentAngularVelocity = Eigen::Vector3f{ - std::stof(parts[startFrom + 7]), - std::stof(parts[startFrom + 8]), - std::stof(parts[startFrom + 9]) }; - rp.velocity = std::stof(parts[startFrom + 10]); - rp.selectedVelocity = std::stoi(parts[startFrom + 11]); - rp.discreteMag = std::stof(parts[startFrom + 12]); - rp.discreteAngle = std::stoi(parts[startFrom + 13]); - } - std::cout << "EVENT Received discreteMag=" << rp.discreteMag << std::endl; - - // --- --- - // - long long lag_s = now_ms - sentTime; - - // ( ) - lag_s = std::max(0ll, std::min(lag_s, 1000ll)); - - if (lag_s > 0) { - // "" - // ClientState.h simulate_physics - // discreteAngle/Mag - rp.simulate_physics(lag_s); - - } - - // , Game.cpp - // , now_ms - rp.lastUpdateServerTime = nowTime; - }*/ - } - else if (msg.rfind("WORLD_UPDATE|", 0) == 0) { - parseWorldUpdate(msg, nowTime); } + } } - - void WebSocketClient::parseWorldUpdate(const std::string& msg, std::chrono::system_clock::time_point now_ms) { - // , (TaskManager) - std::lock_guard lock(playersMutex); - - // : WORLD_UPDATE|server_now_ms|count|id,x,y,z,w,qx,qy,qz,v;... - auto parts = split(msg, '|'); - if (parts.size() < 4) return; - - uint64_t serverTime = std::stoull(parts[1]); - int count = std::stoi(parts[2]); - auto playersData = split(parts[3], ';'); - - for (const auto& pData : playersData) { - auto vals = split(pData, ','); - if (vals.size() < 9) continue; - - int id = std::stoi(vals[0]); - if (id == this->clientId) continue; // - - // RemotePlayer - updateRemotePlayer(id, vals, serverTime, now_ms); - } - } - - void WebSocketClient::updateRemotePlayer(int id, const std::vector& vals, uint64_t serverTime, std::chrono::system_clock::time_point now_ms) { - - //auto& rp = remotePlayers[id]; - - ClientState remoteState; - - remoteState.id = id; - - remoteState.position = { std::stof(vals[1]), std::stof(vals[2]), std::stof(vals[3]) }; - remoteState.rotation = Eigen::Quaternionf(std::stof(vals[4]), std::stof(vals[5]), std::stof(vals[6]), std::stof(vals[7])); - - // 3. ( ) - remoteState.velocity = std::stof(vals[8]); - remoteState.currentAngularVelocity = { std::stof(vals[9]), std::stof(vals[10]), std::stof(vals[11]) }; - remoteState.selectedVelocity = std::stoi(vals[12]); - remoteState.discreteMag = std::stof(vals[13]); - remoteState.discreteAngle = std::stoi(vals[14]); - std::chrono::system_clock::time_point uptime_timepoint{ std::chrono::duration_cast(std::chrono::milliseconds(serverTime)) }; - - //std::cout << "PING Received discreteMag=" << rp.discreteMag << std::endl; - - remoteState.lastUpdateServerTime = uptime_timepoint; - - auto& rp = remotePlayers[id]; - - if (rp.timedRemoteStates.size() > 0 && rp.timedRemoteStates[rp.timedRemoteStates.size() - 1].lastUpdateServerTime == remoteState.lastUpdateServerTime) - { - rp.timedRemoteStates[rp.timedRemoteStates.size() - 1] = remoteState; - } - else - { - rp.timedRemoteStates.push_back(remoteState); - } - - auto cutoff_time = now_ms - std::chrono::milliseconds(CUTOFF_TIME); - - while (rp.timedRemoteStates.size() > 0 && rp.timedRemoteStates[0].lastUpdateServerTime < cutoff_time) - { - rp.timedRemoteStates.erase(rp.timedRemoteStates.begin()); - } - //rp.apply_lag_compensation(now_ms); - - } - - void WebSocketClient::Send(const std::string& message) { if (!connected) return; diff --git a/src/network/WebSocketClient.h b/src/network/WebSocketClient.h index e551504..ee7d119 100644 --- a/src/network/WebSocketClient.h +++ b/src/network/WebSocketClient.h @@ -29,7 +29,7 @@ namespace ZL { bool connected = false; int clientId = -1; - std::unordered_map remotePlayers; + std::unordered_map remotePlayers; std::mutex playersMutex; void startAsyncRead(); @@ -41,8 +41,6 @@ namespace ZL { void Connect(const std::string& host, uint16_t port) override; void Poll() override; - void parseWorldUpdate(const std::string& msg, std::chrono::system_clock::time_point now_ms); - void updateRemotePlayer(int id, const std::vector& vals, uint64_t serverTime, std::chrono::system_clock::time_point now_ms); void Send(const std::string& message) override; void doWrite(); @@ -50,7 +48,7 @@ namespace ZL { bool IsConnected() const override { return connected; } int GetClientId() const { return clientId; } - std::unordered_map getRemotePlayers() override { + std::unordered_map getRemotePlayers() override { std::lock_guard lock(playersMutex); return remotePlayers; }