Working on server
This commit is contained in:
1 parent
ea26d6fb23
commit
1348efb5fe
13 files changed
+761
-193
No files matched your search
@@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <Eigen/Dense>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
|
||||
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
struct ClientState {
|
||||
int id;
|
||||
Eigen::Vector3f position = { 0, 0, 45000.0f };
|
||||
Eigen::Matrix3f rotation = Eigen::Matrix3f::Identity();
|
||||
Eigen::Vector3f currentAngularVelocity = Eigen::Vector3f::Zero();
|
||||
float velocity = 0.0f;
|
||||
int selectedVelocity = 0;
|
||||
float discreteMag = 0;
|
||||
int discreteAngle = -1;
|
||||
|
||||
// Для расчета лага
|
||||
uint64_t lastClientTimestamp = 0;
|
||||
std::chrono::steady_clock::time_point lastUpdateServerTime;
|
||||
|
||||
void simulate_physics(float dt_s) {
|
||||
// Константы из 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
|
||||
|
||||
// 1. Вычисляем targetAngularVelocity на лету из дискретных значений
|
||||
Eigen::Vector3f targetAngularVelocity = Eigen::Vector3f::Zero();
|
||||
|
||||
if (discreteMag > 0.001f) {
|
||||
float rad = static_cast<float>(discreteAngle) * static_cast<float>(M_PI) / 180.0f;
|
||||
// Направление из угла (как в твоем обновленном клиентском коде)
|
||||
Eigen::Vector3f targetDir(sinf(rad), cosf(rad), 0.0f);
|
||||
targetAngularVelocity = targetDir * discreteMag;
|
||||
}
|
||||
|
||||
// 2. Линейное изменение текущей угловой скорости к вычисленной цели
|
||||
Eigen::Vector3f diffVel = targetAngularVelocity - currentAngularVelocity;
|
||||
float diffLen = diffVel.norm();
|
||||
|
||||
if (diffLen > 0.0001f) {
|
||||
float maxChange = ANGULAR_ACCEL * dt_s;
|
||||
if (diffLen <= maxChange) {
|
||||
currentAngularVelocity = targetAngularVelocity;
|
||||
}
|
||||
else {
|
||||
currentAngularVelocity += (diffVel / diffLen) * maxChange;
|
||||
}
|
||||
}
|
||||
else if (discreteMag < 0.001f && currentAngularVelocity.norm() > 0.0001f) {
|
||||
// Если джойстик отпущен, используем ту же логику торможения (или ANGULAR_ACCEL)
|
||||
float currentSpeed = currentAngularVelocity.norm();
|
||||
float drop = ANGULAR_ACCEL * dt_s;
|
||||
if (currentSpeed <= drop) {
|
||||
currentAngularVelocity = Eigen::Vector3f::Zero();
|
||||
}
|
||||
else {
|
||||
currentAngularVelocity -= (currentAngularVelocity / currentSpeed) * drop;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Применение вращения к матрице (Интеграция)
|
||||
float speedScale = currentAngularVelocity.norm();
|
||||
if (speedScale > 0.0001f) {
|
||||
float deltaAlpha = speedScale * dt_s * ROTATION_SENSITIVITY;
|
||||
Eigen::Vector3f axis = currentAngularVelocity.normalized();
|
||||
|
||||
Eigen::Quaternionf rotateQuat(Eigen::AngleAxisf(deltaAlpha, axis));
|
||||
rotation = rotation * rotateQuat.toRotationMatrix();
|
||||
}
|
||||
|
||||
// 4. Линейное изменение линейной скорости
|
||||
float shipDesiredVelocity = static_cast<float>(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;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Обновление позиции
|
||||
if (std::abs(velocity) > 0.01f) {
|
||||
// Движение вперед по локальной оси Z (в твоем коде это {0, 0, -1})
|
||||
Eigen::Vector3f localMove(0.0f, 0.0f, -velocity * dt_s);
|
||||
position += rotation * localMove;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_lag_compensation(std::chrono::steady_clock::time_point nowTime) {
|
||||
|
||||
// 1. Получаем текущее время сервера в той же шкале (мс)
|
||||
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()
|
||||
).count();
|
||||
|
||||
// 2. Вычисляем задержку
|
||||
double lag_ms = 0.0;
|
||||
if (nowTime > lastUpdateServerTime) {
|
||||
lag_ms = std::chrono::duration<float>(nowTime - lastUpdateServerTime).count();
|
||||
}
|
||||
|
||||
// 3. Защита от слишком больших скачков (Clamp)
|
||||
// Если лаг более 500мс, ограничиваем его, чтобы избежать резких рывков
|
||||
float final_lag_s = min(static_cast<float>(lag_ms), 0.5f);
|
||||
|
||||
if (final_lag_s > 0.001f) {
|
||||
// Доматываем симуляцию на величину задержки
|
||||
// Мы предполагаем, что за это время параметры управления не менялись
|
||||
simulate_physics(final_lag_s);
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_state_string(int id) {
|
||||
// Используем кватернион для передачи вращения (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<std::string>& parts) {
|
||||
// Позиция
|
||||
position = { std::stof(parts[2]), std::stof(parts[3]), std::stof(parts[4]) };
|
||||
|
||||
// Для вращения клиент должен прислать либо кватернион, либо углы Эйлера.
|
||||
// Предположим, мы передаем 4 значения кватерниона для экономии:
|
||||
Eigen::Quaternionf q(std::stof(parts[5]), std::stof(parts[6]), std::stof(parts[7]), std::stof(parts[8]));
|
||||
rotation = q.toRotationMatrix();
|
||||
|
||||
currentAngularVelocity = Eigen::Vector3f{ std::stof(parts[9]), std::stof(parts[10]), std::stof(parts[11]) };
|
||||
velocity = std::stof(parts[12]);
|
||||
selectedVelocity = std::stoi(parts[13]);
|
||||
discreteMag = std::stof(parts[14]);
|
||||
discreteAngle = std::stoi(parts[15]);
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "RemotePlayer.h"
|
||||
|
||||
// NetworkInterface.h - Èíòåðôåéñ äëÿ ðàçíûõ òèïîâ ñîåäèíåíèé
|
||||
namespace ZL {
|
||||
@@ -10,5 +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<int, RemotePlayer> getRemotePlayers() = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#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;
|
||||
};
|
||||
@@ -1,6 +1,18 @@
|
||||
#include "WebSocketClient.h"
|
||||
#include <iostream>
|
||||
#include <SDL2/SDL.h>
|
||||
#include "RemotePlayer.h"
|
||||
|
||||
// Âñïîìîãàòåëüíûé split
|
||||
std::vector<std::string> split(const std::string& s, char delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
std::string token;
|
||||
std::istringstream tokenStream(s);
|
||||
while (std::getline(tokenStream, token, delimiter)) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@@ -55,19 +67,121 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void WebSocketClient::Poll() {
|
||||
// Çàáèðàåì ñîîáùåíèÿ èç î÷åðåäè è îáðàáàòûâàåì èõ â ãëàâíîì ïîòîêå
|
||||
std::lock_guard<std::mutex> lock(queueMutex);
|
||||
while (!messageQueue.empty()) {
|
||||
std::string msg = messageQueue.front();
|
||||
messageQueue.pop();
|
||||
// Òóò ìîæíî âûçâàòü êîëáýêè èãðû èëè îáíîâèòü ñîñòîÿíèå
|
||||
|
||||
if (msg.rfind("WORLD_UPDATE|", 0) == 0) {
|
||||
parseWorldUpdate(msg);
|
||||
std::cout << msg << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::parseWorldUpdate(const std::string& msg) {
|
||||
// Ôîðìàò: 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);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::updateRemotePlayer(int id, const std::vector<std::string>& vals) {
|
||||
// Èñïîëüçóåì ìüþòåêñ, òàê êàê ýòîò ìåòîä âûçûâàåòñÿ èç ñåòåâîãî ïîòîêà (TaskManager)
|
||||
std::lock_guard<std::mutex> lock(playersMutex);
|
||||
|
||||
auto& rp = remotePlayers[id];
|
||||
rp.state.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
|
||||
);
|
||||
|
||||
// 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]);
|
||||
|
||||
// 4. Ñáðàñûâàåì òàéìåð èíòåðïîëÿöèè
|
||||
rp.interpolationFactor = 0.0f;
|
||||
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()
|
||||
).count();
|
||||
rp.lastSnapshotTime = now_ms;
|
||||
}
|
||||
|
||||
|
||||
void WebSocketClient::Send(const std::string& message) {
|
||||
if (!connected) return;
|
||||
// Îòïðàâêó òîæå ìîæíî ñäåëàòü àñèíõðîííîé, ÷òîáû íå æäàòü â ãëàâíîì ïîòîêå
|
||||
|
||||
auto ss = std::make_shared<std::string>(message);
|
||||
ws_->async_write(boost::asio::buffer(*ss), [ss](boost::beast::error_code, std::size_t) {});
|
||||
|
||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||
writeQueue_.push(ss);
|
||||
|
||||
// Åñëè ñåé÷àñ íè÷åãî íå çàïèñûâàåòñÿ, èíèöèèðóåì ïåðâóþ çàïèñü
|
||||
if (!isWriting_) {
|
||||
doWrite();
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::doWrite() {
|
||||
// Ýòà ôóíêöèÿ âñåãäà âûçûâàåòñÿ ïîä ìüþòåêñîì èëè èç êîëáýêà
|
||||
if (writeQueue_.empty()) {
|
||||
isWriting_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
isWriting_ = true;
|
||||
auto message = writeQueue_.front();
|
||||
|
||||
// Çàõâàòûâàåì self (shared_from_this), ÷òîáû îáúåêò íå óäàëèëñÿ âî âðåìÿ çàïèñè
|
||||
ws_->async_write(
|
||||
boost::asio::buffer(*message),
|
||||
[this, message](boost::beast::error_code ec, std::size_t) {
|
||||
if (ec) {
|
||||
connected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||
writeQueue_.pop(); // Óäàëÿåì îòïðàâëåííîå ñîîáùåíèå
|
||||
doWrite(); // Ïðîâåðÿåì ñëåäóþùåå
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,16 @@ namespace ZL {
|
||||
std::queue<std::string> messageQueue;
|
||||
std::mutex queueMutex; // Çàùèòà äëÿ messageQueue
|
||||
|
||||
std::queue<std::shared_ptr<std::string>> writeQueue_;
|
||||
bool isWriting_ = false;
|
||||
std::mutex writeMutex_; // Îòäåëüíûé ìüþòåêñ äëÿ î÷åðåäè çàïèñè
|
||||
|
||||
bool connected = false;
|
||||
int clientId = -1;
|
||||
|
||||
std::unordered_map<int, RemotePlayer> remotePlayers;
|
||||
std::mutex playersMutex;
|
||||
|
||||
void startAsyncRead();
|
||||
void processIncomingMessage(const std::string& msg);
|
||||
|
||||
@@ -33,10 +40,18 @@ namespace ZL {
|
||||
void Connect(const std::string& host, uint16_t port) override;
|
||||
|
||||
void Poll() override;
|
||||
void parseWorldUpdate(const std::string& msg);
|
||||
void updateRemotePlayer(int id, const std::vector<std::string>& vals);
|
||||
|
||||
void Send(const std::string& message) override;
|
||||
void doWrite();
|
||||
|
||||
bool IsConnected() const override { return connected; }
|
||||
int GetClientId() const { return clientId; }
|
||||
|
||||
std::unordered_map<int, RemotePlayer> getRemotePlayers() override {
|
||||
std::lock_guard<std::mutex> lock(playersMutex);
|
||||
return remotePlayers;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user