diff --git a/server/server.cpp b/server/server.cpp index 81dde92..067076a 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -57,13 +57,13 @@ class Session : public std::enable_shared_from_this { uint64_t clientTimestamp = std::stoull(parts[1]); // " " - float dt_server = 0.0f; + long long deltaMs = 0.0f; if (state_.lastUpdateServerTime.time_since_epoch().count() > 0) { - dt_server = (clientTimestamp - now_ms) * 1000.f; + deltaMs = (clientTimestamp - now_ms); } - if (dt_server > 0) state_.simulate_physics(dt_server); + if (deltaMs > 0) state_.simulate_physics(deltaMs); std::chrono::steady_clock::time_point uptime_timepoint{ std::chrono::duration_cast(std::chrono::milliseconds(clientTimestamp)) }; state_.lastUpdateServerTime = uptime_timepoint; @@ -74,16 +74,19 @@ class Session : public std::enable_shared_from_this { state_.discreteAngle = std::stoi(parts[2]); state_.discreteMag = std::stof(parts[3]); state_.apply_lag_compensation(now_server); + state_.lastUpdateServerTime = now_server; retranslateMessage(msg); } else if (parts[0] == "VEL") { state_.selectedVelocity = std::stoi(parts[2]); state_.apply_lag_compensation(now_server); + state_.lastUpdateServerTime = now_server; retranslateMessage(msg); } else if (parts[0] == "PING") { state_.handle_full_sync(parts); state_.apply_lag_compensation(now_server); + state_.lastUpdateServerTime = now_server; } } @@ -131,23 +134,21 @@ public: } void tick_physics_global(std::chrono::steady_clock::time_point now) { - float dt = 0.0f; + long long deltaMs = 0; // , if (state_.lastUpdateServerTime.time_since_epoch().count() > 0) { - dt = std::chrono::duration(now - state_.lastUpdateServerTime).count(); + deltaMs = std::chrono::duration_cast( + now - state_.lastUpdateServerTime + ).count(); } - if (dt > 0.0001f) { - state_.simulate_physics(dt); + if (deltaMs > 0) { + state_.simulate_physics(deltaMs); state_.lastUpdateServerTime = now; // } } - void tick_physics(float dt_s) { - state_.simulate_physics(dt_s); - } - 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) {}); diff --git a/src/Game.cpp b/src/Game.cpp index 6ff3bc4..1d726cf 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -627,7 +627,7 @@ namespace ZL sparkEmitter.update(static_cast(delta)); planetObject.update(static_cast(delta)); - extrapolateRemotePlayers(static_cast(delta)); + extrapolateRemotePlayers(); static float pingTimer = 0.0f; pingTimer += delta; @@ -659,10 +659,11 @@ namespace ZL + std::to_string(Environment::lastSentAngle); networkClient->Send(pingMsg); + std::cout << "Sending: " << pingMsg << std::endl; pingTimer = 0.0f; } - static const float CONST_ACCELERATION = 1.f; + //static const float CONST_ACCELERATION = 1.f; float shipDesiredVelocity = Environment::shipSelectedVelocity * 100.f; @@ -670,7 +671,7 @@ namespace ZL { if (Environment::shipVelocity < shipDesiredVelocity) { - Environment::shipVelocity += delta * CONST_ACCELERATION; + Environment::shipVelocity += delta * SHIP_ACCEL; if (Environment::shipVelocity > shipDesiredVelocity) { Environment::shipVelocity = shipDesiredVelocity; @@ -678,7 +679,7 @@ namespace ZL } else if (Environment::shipVelocity > shipDesiredVelocity) { - Environment::shipVelocity -= delta * CONST_ACCELERATION; + Environment::shipVelocity -= delta * SHIP_ACCEL; if (Environment::shipVelocity < shipDesiredVelocity) { Environment::shipVelocity = shipDesiredVelocity; @@ -784,6 +785,7 @@ namespace ZL networkClient->Send(msg); } + float currentSpeed = Environment::currentAngularVelocity.norm(); if (currentSpeed > 0.0001f) { @@ -807,6 +809,7 @@ namespace ZL } } + std::cout << "shipVelocity=" << Environment::shipVelocity << " delta=" << delta << std::endl; // Движение вперед (существующая логика) if (fabs(Environment::shipVelocity) > 0.01f) { @@ -1097,7 +1100,7 @@ namespace ZL } } - void Game::extrapolateRemotePlayers(float deltaMs) { + void Game::extrapolateRemotePlayers() { auto now = std::chrono::steady_clock::now(); @@ -1105,11 +1108,16 @@ namespace ZL for (auto& [id, rp] : latestRemotePlayers) { // 1. Рассчитываем, сколько времени прошло с момента получения последнего пакета (в секундах) - float age_s = std::chrono::duration(now - rp.lastUpdateServerTime).count(); + + auto deltaMs = std::chrono::duration_cast( + now - rp.lastUpdateServerTime + ).count(); + + //float age_s = std::chrono::duration(now - rp.lastUpdateServerTime).count(); // Ограничим экстраполяцию (например, не более 2 секунд), // чтобы в случае лага корабли не улетали в бесконечность - if (age_s > 2.0f) age_s = 2.0f; + if (deltaMs > 2000) deltaMs = 2000; // 2. Сбрасываем физическое состояние rp.state в значения из последнего пакета // (Это важно: мы всегда экстраполируем от последнего достоверного серверного состояния) @@ -1117,11 +1125,14 @@ namespace ZL // 3. Вызываем физику, чтобы "догнать" реальное время // Мы передаем age_s как один большой шаг симуляции - rp.simulate_physics(age_s); + rp.simulate_physics(deltaMs); + rp.lastUpdateServerTime = now; // Теперь rp.state.position и rp.state.rotation содержат актуальные // предсказанные данные для рендеринга в текущем кадре. } + + networkClient->updateRemotePlayers(latestRemotePlayers); } diff --git a/src/Game.h b/src/Game.h index 5724a1e..296f654 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 extrapolateRemotePlayers(float deltaMs); + void extrapolateRemotePlayers(); SDL_Window* window; SDL_GLContext glContext; diff --git a/src/network/ClientState.h b/src/network/ClientState.h index 267edb3..75c9c09 100644 --- a/src/network/ClientState.h +++ b/src/network/ClientState.h @@ -3,12 +3,16 @@ #include #define _USE_MATH_DEFINES #include - +#include using std::min; using std::max; +constexpr float ANGULAR_ACCEL = 0.005f * 1000.0f; +constexpr float SHIP_ACCEL = 1.0f * 1000.0f; +constexpr float ROTATION_SENSITIVITY = 0.002f; + struct ClientState { int id; Eigen::Vector3f position = { 0, 0, 45000.0f }; @@ -22,13 +26,70 @@ struct ClientState { // Для расчета лага std::chrono::steady_clock::time_point lastUpdateServerTime; - void simulate_physics(float dt_s) { + void simulate_physics(size_t delta) { // Константы из Game.cpp, приведенные к секундам (умножаем на 1000) - const float ANGULAR_ACCEL = 0.005f * 1000.0f; - const float ROTATION_SENSITIVITY = 0.002f * 1000.0f; - const float SHIP_ACCEL = 1.0f * 1000.0f; // CONST_ACCELERATION + //const float ANGULAR_ACCEL = 0.005f * 1000.0f; + //const float ROTATION_SENSITIVITY = 0.002f; + //const float SHIP_ACCEL = 1.0f * 1000.0f; // CONST_ACCELERATION // 1. Вычисляем targetAngularVelocity на лету из дискретных значений + + if (discreteMag > 0.01f) + { + float rad = static_cast(discreteAngle) * static_cast(M_PI) / 180.0f; + + // Целевая угловая скорость (дискретная сила определяет модуль вектора) + // Вектор {cos, sin, 0} дает нам направление отклонения джойстика + Eigen::Vector3f targetAngularVelDir(sinf(rad), cosf(rad), 0.0f); + Eigen::Vector3f targetAngularVelocity = targetAngularVelDir * discreteMag; + + Eigen::Vector3f diffVel = targetAngularVelocity - currentAngularVelocity; + float diffLen = diffVel.norm(); + + if (diffLen > 0.0001f) { + // Вычисляем, на сколько мы можем изменить скорость в этом кадре + float maxChange = ANGULAR_ACCEL * static_cast(delta); + + if (diffLen <= maxChange) { + // Если до цели осталось меньше, чем шаг ускорения — просто прыгаем в цель + currentAngularVelocity = targetAngularVelocity; + } + + else { + // Линейно двигаемся в сторону целевого вектора + currentAngularVelocity += (diffVel / diffLen) * maxChange; + } + } + } + else + { + float currentSpeed = currentAngularVelocity.norm(); + + if (currentSpeed > 0.0001f) { + float drop = ANGULAR_ACCEL * static_cast(delta); + if (currentSpeed <= drop) { + currentAngularVelocity = Eigen::Vector3f::Zero(); + } + else { + // Уменьшаем модуль вектора, сохраняя направление + currentAngularVelocity -= (currentAngularVelocity / currentSpeed) * drop; + } + } + } + + float speedScale = currentAngularVelocity.norm(); + if (speedScale > 0.0001f) { + // Коэффициент чувствительности вращения + + float deltaAlpha = speedScale * static_cast(delta) * ROTATION_SENSITIVITY; + + Eigen::Vector3f axis = currentAngularVelocity.normalized(); + Eigen::Quaternionf rotateQuat(Eigen::AngleAxisf(deltaAlpha, axis)); + + rotation = rotation * rotateQuat.toRotationMatrix(); + } + + /* Eigen::Vector3f targetAngularVelocity = Eigen::Vector3f::Zero(); if (discreteMag > 0.001f) { @@ -61,8 +122,9 @@ struct ClientState { else { currentAngularVelocity -= (currentAngularVelocity / currentSpeed) * drop; } - } + }*/ + /* // 3. Применение вращения к матрице (Интеграция) float speedScale = currentAngularVelocity.norm(); if (speedScale > 0.0001f) { @@ -71,50 +133,62 @@ struct ClientState { Eigen::Quaternionf rotateQuat(Eigen::AngleAxisf(deltaAlpha, axis)); rotation = rotation * rotateQuat.toRotationMatrix(); - } + }*/ // 4. Линейное изменение линейной скорости - float shipDesiredVelocity = static_cast(selectedVelocity) * 100.f; - float speedDiff = shipDesiredVelocity - velocity; - if (std::abs(speedDiff) > 0.001f) { - float speedStep = SHIP_ACCEL * dt_s; - if (std::abs(speedDiff) <= speedStep) { - velocity = shipDesiredVelocity; - } - else { - velocity += (speedDiff > 0 ? 1.0f : -1.0f) * speedStep; - } - } + float shipDesiredVelocity = selectedVelocity * 100.f; - // 5. Обновление позиции - if (std::abs(velocity) > 0.01f) { - // Движение вперед по локальной оси Z (в твоем коде это {0, 0, -1}) - Eigen::Vector3f localMove(0.0f, 0.0f, -velocity * dt_s); - position += rotation * localMove; + if (velocity < shipDesiredVelocity) + { + velocity += delta * SHIP_ACCEL; + if (velocity > shipDesiredVelocity) + { + velocity = shipDesiredVelocity; + } + } + else if (velocity > shipDesiredVelocity) + { + velocity -= delta * SHIP_ACCEL; + if (velocity < shipDesiredVelocity) + { + velocity = shipDesiredVelocity; + } + } + std::cout << "velocity=" << velocity << " delta=" << delta << std::endl; + + if (fabs(velocity) > 0.01f) + { + Eigen::Vector3f velocityDirection = { 0,0, -velocity * delta / 1000.f }; + Eigen::Vector3f velocityDirectionAdjusted = rotation * velocityDirection; + position = position + velocityDirectionAdjusted; } } void apply_lag_compensation(std::chrono::steady_clock::time_point nowTime) { // 1. Получаем текущее время сервера в той же шкале (мс) - auto now_ms = std::chrono::duration_cast( + /*uto now_ms = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch() ).count(); + + long long deltaMs = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch() - lastUpdateServerTime + ).count();*/ // 2. Вычисляем задержку - float lag_ms = 0.0; + long long deltaMs = 0; if (nowTime > lastUpdateServerTime) { - lag_ms = std::chrono::duration(nowTime - lastUpdateServerTime).count(); + deltaMs = std::chrono::duration_cast(nowTime - lastUpdateServerTime).count(); } // 3. Защита от слишком больших скачков (Clamp) // Если лаг более 500мс, ограничиваем его, чтобы избежать резких рывков - float final_lag_s = min(lag_ms, 0.5f); + long long final_lag_ms = min(deltaMs, 500ll); - if (final_lag_s > 0.001f) { + if (final_lag_ms > 0) { // Доматываем симуляцию на величину задержки // Мы предполагаем, что за это время параметры управления не менялись - simulate_physics(final_lag_s); + simulate_physics(final_lag_ms); } } diff --git a/src/network/NetworkInterface.h b/src/network/NetworkInterface.h index 35080bf..8f1eb33 100644 --- a/src/network/NetworkInterface.h +++ b/src/network/NetworkInterface.h @@ -13,5 +13,6 @@ namespace ZL { virtual bool IsConnected() const = 0; virtual void Poll() = 0; // virtual std::unordered_map getRemotePlayers() = 0; + virtual void updateRemotePlayers(const std::unordered_map& newRemotePlayers) = 0; }; } diff --git a/src/network/WebSocketClient.cpp b/src/network/WebSocketClient.cpp index 0d8046e..3bf868a 100644 --- a/src/network/WebSocketClient.cpp +++ b/src/network/WebSocketClient.cpp @@ -69,8 +69,10 @@ namespace ZL { void WebSocketClient::Poll() { std::lock_guard lock(queueMutex); + auto nowTime = std::chrono::steady_clock::now(); + auto now_ms = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch() + nowTime.time_since_epoch() ).count(); while (!messageQueue.empty()) { @@ -99,21 +101,22 @@ namespace ZL { // --- --- // - float lag_s = static_cast(now_ms - sentTime) / 1000.0f; + long long lag_s = now_ms - sentTime; // ( ) - lag_s = std::max(0.0f, std::min(lag_s, 1.0f)); + lag_s = std::max(0ll, std::min(lag_s, 1000ll)); - if (lag_s > 0.001f) { + if (lag_s > 0) { // "" // ClientState.h simulate_physics // discreteAngle/Mag rp.simulate_physics(lag_s); + } // , Game.cpp // , now_ms - rp.lastUpdateServerTime = std::chrono::steady_clock::now(); + rp.lastUpdateServerTime = nowTime; } } else if (msg.rfind("WORLD_UPDATE|", 0) == 0) { diff --git a/src/network/WebSocketClient.h b/src/network/WebSocketClient.h index 098088e..d5472d4 100644 --- a/src/network/WebSocketClient.h +++ b/src/network/WebSocketClient.h @@ -53,5 +53,14 @@ namespace ZL { std::lock_guard lock(playersMutex); return remotePlayers; } + + void updateRemotePlayers(const std::unordered_map& newRemotePlayers) override { + std::lock_guard lock(playersMutex); + + for (const auto& [id, newPlayer] : newRemotePlayers) { + remotePlayers[id] = newPlayer; + } + + } }; } \ No newline at end of file