space-game001/src/network/WebSocketClient.cpp
2026-01-17 19:16:06 +03:00

359 lines
15 KiB
C++

#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 {
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<boost::beast::websocket::stream<boost::beast::tcp_stream>>(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<std::mutex> lock(queueMutex);
messageQueue.push(msg);
}
void WebSocketClient::Poll() {
std::lock_guard<std::mutex> lock(queueMutex);
while (!messageQueue.empty()) {
auto nowTime = std::chrono::system_clock::now();
//Apply server delay:
nowTime -= std::chrono::milliseconds(CLIENT_DELAY);
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
nowTime.time_since_epoch()
).count();
std::string msg = messageQueue.front();
messageQueue.pop();
if (msg.rfind("EVENT:", 0) == 0) {
auto parts = split(msg, ':');
if (parts.size() < 5) continue; // EVENT:ID:TYPE:TIME:DATA...
int remoteId = std::stoi(parts[1]);
std::string subType = parts[2];
uint64_t sentTime = std::stoull(parts[3]);
ClientState remoteState;
remoteState.id = remoteId;
std::chrono::system_clock::time_point uptime_timepoint{ std::chrono::duration_cast<std::chrono::system_clock::time_point::duration>(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]);
int startFrom = 4;
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]);
}
{
std::lock_guard<std::mutex> pLock(playersMutex);
auto& rp = remotePlayers[remoteId];
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());
}
}
/*
std::lock_guard<std::mutex> 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<std::mutex> 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<std::string>& 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::system_clock::time_point::duration>(std::chrono::milliseconds(serverTime)) };
//std::cout << "PING Received discreteMag=" << rp.discreteMag << std::endl;
remoteState.lastUpdateServerTime = uptime_timepoint;
auto& rp = remotePlayers[id];
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;
auto ss = std::make_shared<std::string>(message);
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(); // Ïðîâåðÿåì ñëåäóþùåå
}
);
}
}