diff --git a/server/server.cpp b/server/server.cpp index 635d3c8..a991dee 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -64,7 +64,7 @@ class Session : public std::enable_shared_from_this { if (dt_server > 0) state_.simulate_physics(dt_server); - using time_point = std::chrono::steady_clock::time_point; + std::chrono::steady_clock::time_point uptime_timepoint{ std::chrono::duration_cast(std::chrono::milliseconds(clientTimestamp)) }; state_.lastUpdateServerTime = uptime_timepoint; diff --git a/src/Game.cpp b/src/Game.cpp index 447f401..8ab04c9 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -568,7 +568,7 @@ namespace ZL sparkEmitter.update(static_cast(delta)); planetObject.update(static_cast(delta)); - updateRemotePlayers(static_cast(delta)); + extrapolateRemotePlayers(static_cast(delta)); static float pingTimer = 0.0f; pingTimer += delta; @@ -1052,21 +1052,30 @@ namespace ZL } } - void Game::updateRemotePlayers(float deltaMs) { + void Game::extrapolateRemotePlayers(float deltaMs) { + + auto now = std::chrono::steady_clock::now(); latestRemotePlayers = networkClient->getRemotePlayers(); for (auto& [id, rp] : latestRemotePlayers) { - // Увеличиваем фактор интерполяции (базируется на частоте Snapshot = 1000мс) - rp.interpolationFactor += deltaMs / 1000.0f; - if (rp.interpolationFactor > 1.0f) rp.interpolationFactor = 1.0f; + // 1. Рассчитываем, сколько времени прошло с момента получения последнего пакета (в секундах) + float age_s = std::chrono::duration(now - rp.lastUpdateServerTime).count(); - // Линейная интерполяция позиции - rp.state.position = rp.startPosition + (rp.targetPosition - rp.startPosition) * rp.interpolationFactor; + // Ограничим экстраполяцию (например, не более 2 секунд), + // чтобы в случае лага корабли не улетали в бесконечность + if (age_s > 2.0f) age_s = 2.0f; - // Сферическая интерполяция вращения (Slerp) - Eigen::Quaternionf currentQ = rp.startRotation.slerp(rp.interpolationFactor, rp.targetRotation); - rp.state.rotation = currentQ.toRotationMatrix(); + // 2. Сбрасываем физическое состояние rp.state в значения из последнего пакета + // (Это важно: мы всегда экстраполируем от последнего достоверного серверного состояния) + // В WebSocketClient::updateRemotePlayer нужно убедиться, что rp.state обновляется данными из пакета. + + // 3. Вызываем физику, чтобы "догнать" реальное время + // Мы передаем age_s как один большой шаг симуляции + rp.simulate_physics(age_s); + + // Теперь rp.state.position и rp.state.rotation содержат актуальные + // предсказанные данные для рендеринга в текущем кадре. } } diff --git a/src/Game.h b/src/Game.h index d66d654..83d9b36 100644 --- a/src/Game.h +++ b/src/Game.h @@ -51,7 +51,7 @@ namespace ZL { void handleUp(int mx, int my); void handleMotion(int mx, int my); - void updateRemotePlayers(float deltaMs); + void extrapolateRemotePlayers(float deltaMs); SDL_Window* window; SDL_GLContext glContext; @@ -64,7 +64,7 @@ namespace ZL { std::vector boxCoordsArr; std::vector boxRenderArr; - std::unordered_map latestRemotePlayers; + std::unordered_map latestRemotePlayers; static const size_t CONST_TIMER_INTERVAL = 10; static const size_t CONST_MAX_TIME_INTERVAL = 1000; diff --git a/src/network/ClientState.h b/src/network/ClientState.h index edf77c2..267edb3 100644 --- a/src/network/ClientState.h +++ b/src/network/ClientState.h @@ -20,7 +20,6 @@ struct ClientState { int discreteAngle = -1; // Для расчета лага - uint64_t lastClientTimestamp = 0; std::chrono::steady_clock::time_point lastUpdateServerTime; void simulate_physics(float dt_s) { @@ -103,14 +102,14 @@ struct ClientState { ).count(); // 2. Вычисляем задержку - double lag_ms = 0.0; + float lag_ms = 0.0; if (nowTime > lastUpdateServerTime) { lag_ms = std::chrono::duration(nowTime - lastUpdateServerTime).count(); } // 3. Защита от слишком больших скачков (Clamp) // Если лаг более 500мс, ограничиваем его, чтобы избежать резких рывков - float final_lag_s = min(static_cast(lag_ms), 0.5f); + float final_lag_s = min(lag_ms, 0.5f); if (final_lag_s > 0.001f) { // Доматываем симуляцию на величину задержки diff --git a/src/network/NetworkInterface.h b/src/network/NetworkInterface.h index 2691be4..35080bf 100644 --- a/src/network/NetworkInterface.h +++ b/src/network/NetworkInterface.h @@ -1,7 +1,7 @@ #pragma once #include #include -#include "RemotePlayer.h" +#include "ClientState.h" // NetworkInterface.h - namespace ZL { @@ -12,6 +12,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/RemotePlayer.h b/src/network/RemotePlayer.h index ff9bb8d..5a1664d 100644 --- a/src/network/RemotePlayer.h +++ b/src/network/RemotePlayer.h @@ -1,15 +1,2 @@ #pragma once #include "ClientState.h" - -struct RemotePlayer { - ClientState state; - - // Данные для интерполяции - Eigen::Vector3f startPosition; - Eigen::Vector3f targetPosition; - Eigen::Quaternionf startRotation; - Eigen::Quaternionf targetRotation; - - float interpolationFactor = 0.0f; - uint64_t lastSnapshotTime = 0; -}; \ No newline at end of file diff --git a/src/network/WebSocketClient.cpp b/src/network/WebSocketClient.cpp index 276ea57..04c0b29 100644 --- a/src/network/WebSocketClient.cpp +++ b/src/network/WebSocketClient.cpp @@ -96,52 +96,30 @@ namespace ZL { if (id == this->clientId) continue; // // RemotePlayer - updateRemotePlayer(id, vals); + updateRemotePlayer(id, vals, serverTime); } } - void WebSocketClient::updateRemotePlayer(int id, const std::vector& vals) { + void WebSocketClient::updateRemotePlayer(int id, const std::vector& vals, uint64_t serverTime) { // , (TaskManager) std::lock_guard lock(playersMutex); auto& rp = remotePlayers[id]; - rp.state.id = id; + rp.id = id; - // 1. - // "", 1.0 - if (rp.lastSnapshotTime == 0) { - // - rp.startPosition = { std::stof(vals[1]), std::stof(vals[2]), std::stof(vals[3]) }; - rp.startRotation = Eigen::Quaternionf(std::stof(vals[4]), std::stof(vals[5]), std::stof(vals[6]), std::stof(vals[7])); - } - else { - // , , - rp.startPosition = rp.state.position; - rp.startRotation = Eigen::Quaternionf(rp.state.rotation); - } - - // 2. - rp.targetPosition = { std::stof(vals[1]), std::stof(vals[2]), std::stof(vals[3]) }; - rp.targetRotation = Eigen::Quaternionf( - std::stof(vals[4]), // w - std::stof(vals[5]), // x - std::stof(vals[6]), // y - std::stof(vals[7]) // z - ); + rp.position = { std::stof(vals[1]), std::stof(vals[2]), std::stof(vals[3]) }; + rp.rotation = Eigen::Quaternionf(std::stof(vals[4]), std::stof(vals[5]), std::stof(vals[6]), std::stof(vals[7])); // 3. ( ) - rp.state.velocity = std::stof(vals[8]); - rp.state.currentAngularVelocity = { std::stof(vals[9]), std::stof(vals[10]), std::stof(vals[11]) }; - rp.state.selectedVelocity = std::stoi(vals[12]); - rp.state.discreteMag = std::stoi(vals[13]); - rp.state.discreteAngle = std::stoi(vals[14]); + rp.velocity = std::stof(vals[8]); + rp.currentAngularVelocity = { std::stof(vals[9]), std::stof(vals[10]), std::stof(vals[11]) }; + rp.selectedVelocity = std::stoi(vals[12]); + rp.discreteMag = std::stoi(vals[13]); + rp.discreteAngle = std::stoi(vals[14]); + std::chrono::steady_clock::time_point uptime_timepoint{ std::chrono::duration_cast(std::chrono::milliseconds(serverTime)) }; + + rp.lastUpdateServerTime = uptime_timepoint; - // 4. - rp.interpolationFactor = 0.0f; - auto now_ms = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch() - ).count(); - rp.lastSnapshotTime = now_ms; } diff --git a/src/network/WebSocketClient.h b/src/network/WebSocketClient.h index 5f18ced..098088e 100644 --- a/src/network/WebSocketClient.h +++ b/src/network/WebSocketClient.h @@ -28,7 +28,7 @@ namespace ZL { bool connected = false; int clientId = -1; - std::unordered_map remotePlayers; + std::unordered_map remotePlayers; std::mutex playersMutex; void startAsyncRead(); @@ -41,7 +41,7 @@ namespace ZL { void Poll() override; void parseWorldUpdate(const std::string& msg); - void updateRemotePlayer(int id, const std::vector& vals); + void updateRemotePlayer(int id, const std::vector& vals, uint64_t serverTime); void Send(const std::string& message) override; void doWrite(); @@ -49,7 +49,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; }