added sync projectiles/boxes
This commit is contained in:
parent
cacc18dc7e
commit
e8fb14b809
@ -56,33 +56,76 @@ class Session : public std::enable_shared_from_this<Session> {
|
|||||||
|
|
||||||
void process_message(const std::string& msg) {
|
void process_message(const std::string& msg) {
|
||||||
auto now_server = std::chrono::system_clock::now();
|
auto now_server = std::chrono::system_clock::now();
|
||||||
|
|
||||||
auto parts = split(msg, ':');
|
auto parts = split(msg, ':');
|
||||||
|
|
||||||
if (parts.size() < 16)
|
if (parts.empty()) {
|
||||||
{
|
std::cerr << "Empty message received\n";
|
||||||
throw std::runtime_error("Unknown message type received, too small");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t clientTimestamp = std::stoull(parts[1]);
|
std::string type = parts[0];
|
||||||
|
|
||||||
ClientState receivedState;
|
if (type == "UPD") {
|
||||||
|
if (parts.size() < 16) {
|
||||||
receivedState.id = id_;
|
std::cerr << "Invalid UPD message: too few parts (" << parts.size() << ")\n";
|
||||||
|
return;
|
||||||
std::chrono::system_clock::time_point uptime_timepoint{ std::chrono::duration_cast<std::chrono::system_clock::time_point::duration>(std::chrono::milliseconds(clientTimestamp)) };
|
}
|
||||||
receivedState.lastUpdateServerTime = uptime_timepoint;
|
uint64_t clientTimestamp = std::stoull(parts[1]);
|
||||||
|
ClientState receivedState;
|
||||||
if (parts[0] == "UPD") {
|
receivedState.id = id_;
|
||||||
|
std::chrono::system_clock::time_point uptime_timepoint{
|
||||||
|
std::chrono::milliseconds(clientTimestamp)
|
||||||
|
};
|
||||||
|
receivedState.lastUpdateServerTime = uptime_timepoint;
|
||||||
receivedState.handle_full_sync(parts, 2);
|
receivedState.handle_full_sync(parts, 2);
|
||||||
retranslateMessage(msg);
|
retranslateMessage(msg);
|
||||||
|
timedClientStates.add_state(receivedState);
|
||||||
}
|
}
|
||||||
else
|
else if (parts[0] == "FIRE") {
|
||||||
{
|
if (parts.size() < 8) {
|
||||||
throw std::runtime_error("Unknown message type received: " + parts[0]);
|
std::cerr << "Invalid FIRE: too few parts\n";
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
timedClientStates.add_state(receivedState);
|
uint64_t clientTime = std::stoull(parts[1]);
|
||||||
|
Eigen::Vector3f pos{
|
||||||
|
std::stof(parts[2]), std::stof(parts[3]), std::stof(parts[4])
|
||||||
|
};
|
||||||
|
Eigen::Vector3f dir{
|
||||||
|
std::stof(parts[5]), std::stof(parts[6]), std::stof(parts[7])
|
||||||
|
};
|
||||||
|
|
||||||
|
int shotCount = 1;
|
||||||
|
if (parts.size() >= 9) {
|
||||||
|
try {
|
||||||
|
shotCount = std::stoi(parts[8]);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
shotCount = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string broadcast = "PROJECTILE:" +
|
||||||
|
std::to_string(id_) + ":" +
|
||||||
|
std::to_string(clientTime) + ":" +
|
||||||
|
std::to_string(pos.x()) + ":" +
|
||||||
|
std::to_string(pos.y()) + ":" +
|
||||||
|
std::to_string(pos.z()) + ":" +
|
||||||
|
std::to_string(dir.x()) + ":" +
|
||||||
|
std::to_string(dir.y()) + ":" +
|
||||||
|
std::to_string(dir.z()) + ":" +
|
||||||
|
std::to_string(shotCount);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
||||||
|
std::cout << "Player " << id_ << " fired " << shotCount << " shots → broadcasting\n";
|
||||||
|
|
||||||
|
for (auto& session : g_sessions) {
|
||||||
|
session->send_message(broadcast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "Unknown message type: " << type << "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void retranslateMessage(const std::string& msg)
|
void retranslateMessage(const std::string& msg)
|
||||||
|
|||||||
65
src/Game.cpp
65
src/Game.cpp
@ -232,7 +232,24 @@ namespace ZL
|
|||||||
uint64_t now = SDL_GetTicks64();
|
uint64_t now = SDL_GetTicks64();
|
||||||
if (now - lastProjectileFireTime >= static_cast<uint64_t>(projectileCooldownMs)) {
|
if (now - lastProjectileFireTime >= static_cast<uint64_t>(projectileCooldownMs)) {
|
||||||
lastProjectileFireTime = now;
|
lastProjectileFireTime = now;
|
||||||
fireProjectiles();
|
|
||||||
|
Eigen::Vector3f localForward = { 0, 0, -1 };
|
||||||
|
Eigen::Vector3f worldForward = (Environment::shipState.rotation * localForward).normalized();
|
||||||
|
|
||||||
|
Eigen::Vector3f centerPos = Environment::shipState.position +
|
||||||
|
Environment::shipState.rotation * Vector3f{ 0, 0.9f, 5.0f };
|
||||||
|
|
||||||
|
std::string fireMsg = "FIRE:" +
|
||||||
|
std::to_string(now) + ":" +
|
||||||
|
std::to_string(centerPos.x()) + ":" +
|
||||||
|
std::to_string(centerPos.y()) + ":" +
|
||||||
|
std::to_string(centerPos.z()) + ":" +
|
||||||
|
std::to_string(worldForward.x()) + ":" +
|
||||||
|
std::to_string(worldForward.y()) + ":" +
|
||||||
|
std::to_string(worldForward.z()) + ":" +
|
||||||
|
"2";
|
||||||
|
|
||||||
|
networkClient->Send(fireMsg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -582,7 +599,7 @@ namespace ZL
|
|||||||
auto sboxes = networkClient->getServerBoxes();
|
auto sboxes = networkClient->getServerBoxes();
|
||||||
if (!sboxes.empty()) {
|
if (!sboxes.empty()) {
|
||||||
boxCoordsArr.clear();
|
boxCoordsArr.clear();
|
||||||
for (auto &b : sboxes) {
|
for (auto& b : sboxes) {
|
||||||
BoxCoords bc;
|
BoxCoords bc;
|
||||||
bc.pos = b.first;
|
bc.pos = b.first;
|
||||||
bc.m = b.second;
|
bc.m = b.second;
|
||||||
@ -607,7 +624,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClientState playerState = remotePlayer.fetchClientStateAtTime(now);
|
ClientState playerState = remotePlayer.fetchClientStateAtTime(now);
|
||||||
|
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
@ -704,7 +721,7 @@ namespace ZL
|
|||||||
float radians = atan2f(diffy, diffx);
|
float radians = atan2f(diffy, diffx);
|
||||||
discreteAngle = static_cast<int>(radians * 180.0f / M_PI);
|
discreteAngle = static_cast<int>(radians * 180.0f / M_PI);
|
||||||
if (discreteAngle < 0) discreteAngle += 360;
|
if (discreteAngle < 0) discreteAngle += 360;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -718,7 +735,7 @@ namespace ZL
|
|||||||
discreteMag = 0.0f;
|
discreteMag = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (discreteAngle != Environment::shipState.discreteAngle || discreteMag != Environment::shipState.discreteMag) {
|
if (discreteAngle != Environment::shipState.discreteAngle || discreteMag != Environment::shipState.discreteMag) {
|
||||||
Environment::shipState.discreteAngle = discreteAngle;
|
Environment::shipState.discreteAngle = discreteAngle;
|
||||||
Environment::shipState.discreteMag = discreteMag;
|
Environment::shipState.discreteMag = discreteMag;
|
||||||
@ -1097,7 +1114,7 @@ namespace ZL
|
|||||||
{
|
{
|
||||||
if (event.key.keysym.sym == SDLK_i)
|
if (event.key.keysym.sym == SDLK_i)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
#endif
|
#endif
|
||||||
@ -1105,6 +1122,42 @@ namespace ZL
|
|||||||
render();
|
render();
|
||||||
mainThreadHandler.processMainThreadTasks();
|
mainThreadHandler.processMainThreadTasks();
|
||||||
networkClient->Poll();
|
networkClient->Poll();
|
||||||
|
|
||||||
|
if (networkClient) {
|
||||||
|
auto pending = networkClient->getPendingProjectiles();
|
||||||
|
if (!pending.empty()) {
|
||||||
|
const float projectileSpeed = 60.0f;
|
||||||
|
const float lifeMs = 5000.0f;
|
||||||
|
const float size = 0.5f;
|
||||||
|
|
||||||
|
for (const auto& pi : pending) {
|
||||||
|
Eigen::Vector3f dir = pi.direction;
|
||||||
|
float len = dir.norm();
|
||||||
|
if (len <= 1e-6f) continue;
|
||||||
|
dir /= len;
|
||||||
|
Eigen::Vector3f baseVel = dir * projectileSpeed;
|
||||||
|
|
||||||
|
int shotCount = 1;
|
||||||
|
shotCount = 2;
|
||||||
|
|
||||||
|
std::vector<Eigen::Vector3f> localOffsets = {
|
||||||
|
{-1.5f, 0.9f, 5.0f},
|
||||||
|
{ 1.5f, 0.9f, 5.0f}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < shotCount; ++i) {
|
||||||
|
Eigen::Vector3f shotPos = pi.position + localOffsets[i];
|
||||||
|
|
||||||
|
for (auto& p : projectiles) {
|
||||||
|
if (!p->isActive()) {
|
||||||
|
p->init(shotPos, baseVel, lifeMs, size, projectileTexture, renderer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::handleDown(int mx, int my)
|
void Game::handleDown(int mx, int my)
|
||||||
|
|||||||
@ -4,15 +4,17 @@
|
|||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
void LocalClient::Connect(const std::string& host, uint16_t port) {
|
void LocalClient::Connect(const std::string& host, uint16_t port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalClient::Poll() {
|
void LocalClient::Poll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalClient::Send(const std::string& message) {
|
void LocalClient::Send(const std::string& message) {
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
std::vector<ProjectileInfo> LocalClient::getPendingProjectiles() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -17,6 +17,7 @@ namespace ZL {
|
|||||||
|
|
||||||
bool IsConnected() const override { return true; }
|
bool IsConnected() const override { return true; }
|
||||||
int GetClientId() const { return 1; }
|
int GetClientId() const { return 1; }
|
||||||
|
std::vector<ProjectileInfo> getPendingProjectiles();
|
||||||
|
|
||||||
std::unordered_map<int, ClientStateInterval> getRemotePlayers() override {
|
std::unordered_map<int, ClientStateInterval> getRemotePlayers() override {
|
||||||
return std::unordered_map<int, ClientStateInterval>();
|
return std::unordered_map<int, ClientStateInterval>();
|
||||||
|
|||||||
@ -7,6 +7,13 @@
|
|||||||
// NetworkInterface.h - »нтерфейс дл¤ разных типов соединений
|
// NetworkInterface.h - »нтерфейс дл¤ разных типов соединений
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
|
struct ProjectileInfo {
|
||||||
|
int shooterId = -1;
|
||||||
|
uint64_t clientTime = 0;
|
||||||
|
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
||||||
|
Eigen::Vector3f direction = Eigen::Vector3f::Zero();
|
||||||
|
};
|
||||||
|
|
||||||
class INetworkClient {
|
class INetworkClient {
|
||||||
public:
|
public:
|
||||||
virtual ~INetworkClient() = default;
|
virtual ~INetworkClient() = default;
|
||||||
@ -17,5 +24,7 @@ namespace ZL {
|
|||||||
virtual std::unordered_map<int, ClientStateInterval> getRemotePlayers() = 0;
|
virtual std::unordered_map<int, ClientStateInterval> getRemotePlayers() = 0;
|
||||||
|
|
||||||
virtual std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() = 0;
|
virtual std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() = 0;
|
||||||
|
|
||||||
|
virtual std::vector<ProjectileInfo> getPendingProjectiles() = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,206 +6,239 @@
|
|||||||
|
|
||||||
// Вспомогательный split
|
// Вспомогательный split
|
||||||
std::vector<std::string> split(const std::string& s, char delimiter) {
|
std::vector<std::string> split(const std::string& s, char delimiter) {
|
||||||
std::vector<std::string> tokens;
|
std::vector<std::string> tokens;
|
||||||
std::string token;
|
std::string token;
|
||||||
std::istringstream tokenStream(s);
|
std::istringstream tokenStream(s);
|
||||||
while (std::getline(tokenStream, token, delimiter)) {
|
while (std::getline(tokenStream, token, delimiter)) {
|
||||||
tokens.push_back(token);
|
tokens.push_back(token);
|
||||||
}
|
}
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
void WebSocketClient::Connect(const std::string& host, uint16_t port) {
|
void WebSocketClient::Connect(const std::string& host, uint16_t port) {
|
||||||
try {
|
try {
|
||||||
boost::asio::ip::tcp::resolver resolver(ioc_);
|
boost::asio::ip::tcp::resolver resolver(ioc_);
|
||||||
auto const results = resolver.resolve(host, std::to_string(port));
|
auto const results = resolver.resolve(host, std::to_string(port));
|
||||||
|
|
||||||
ws_ = std::make_unique<boost::beast::websocket::stream<boost::beast::tcp_stream>>(ioc_);
|
ws_ = std::make_unique<boost::beast::websocket::stream<boost::beast::tcp_stream>>(ioc_);
|
||||||
|
|
||||||
// Выполняем синхронный коннект и handshake для простоты старта
|
// Выполняем синхронный коннект и handshake для простоты старта
|
||||||
boost::beast::get_lowest_layer(*ws_).connect(results);
|
boost::beast::get_lowest_layer(*ws_).connect(results);
|
||||||
ws_->handshake(host, "/");
|
ws_->handshake(host, "/");
|
||||||
|
|
||||||
connected = true;
|
connected = true;
|
||||||
|
|
||||||
// Запускаем асинхронное чтение в пуле потоков TaskManager
|
// Запускаем асинхронное чтение в пуле потоков TaskManager
|
||||||
startAsyncRead();
|
startAsyncRead();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (std::exception& e) {
|
catch (std::exception& e) {
|
||||||
std::cerr << "Network Error: " << e.what() << std::endl;
|
std::cerr << "Network Error: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketClient::startAsyncRead() {
|
void WebSocketClient::startAsyncRead() {
|
||||||
ws_->async_read(buffer_, [this](boost::beast::error_code ec, std::size_t bytes) {
|
ws_->async_read(buffer_, [this](boost::beast::error_code ec, std::size_t bytes) {
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
std::string msg = boost::beast::buffers_to_string(buffer_.data());
|
std::string msg = boost::beast::buffers_to_string(buffer_.data());
|
||||||
buffer_.consume(bytes);
|
buffer_.consume(bytes);
|
||||||
processIncomingMessage(msg);
|
processIncomingMessage(msg);
|
||||||
startAsyncRead();
|
startAsyncRead();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
connected = false;
|
connected = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketClient::processIncomingMessage(const std::string& msg) {
|
void WebSocketClient::processIncomingMessage(const std::string& msg) {
|
||||||
// Логика парсинга...
|
// Логика парсинга...
|
||||||
if (msg.rfind("ID:", 0) == 0) {
|
if (msg.rfind("ID:", 0) == 0) {
|
||||||
clientId = std::stoi(msg.substr(3));
|
clientId = std::stoi(msg.substr(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Безопасно кладем в очередь для главного потока
|
// Безопасно кладем в очередь для главного потока
|
||||||
std::lock_guard<std::mutex> lock(queueMutex);
|
std::lock_guard<std::mutex> lock(queueMutex);
|
||||||
messageQueue.push(msg);
|
messageQueue.push(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketClient::Poll() {
|
std::vector<ProjectileInfo> WebSocketClient::getPendingProjectiles() {
|
||||||
std::lock_guard<std::mutex> lock(queueMutex);
|
std::lock_guard<std::mutex> lock(projMutex_);
|
||||||
|
auto copy = pendingProjectiles_;
|
||||||
|
pendingProjectiles_.clear();
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
while (!messageQueue.empty()) {
|
void WebSocketClient::Poll() {
|
||||||
|
std::lock_guard<std::mutex> lock(queueMutex);
|
||||||
|
|
||||||
auto nowTime = std::chrono::system_clock::now();
|
while (!messageQueue.empty()) {
|
||||||
|
|
||||||
//Apply server delay:
|
auto nowTime = std::chrono::system_clock::now();
|
||||||
nowTime -= std::chrono::milliseconds(CLIENT_DELAY);
|
|
||||||
|
|
||||||
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
//Apply server delay:
|
||||||
nowTime.time_since_epoch()
|
nowTime -= std::chrono::milliseconds(CLIENT_DELAY);
|
||||||
).count();
|
|
||||||
|
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
nowTime.time_since_epoch()
|
||||||
|
).count();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string msg = messageQueue.front();
|
std::string msg = messageQueue.front();
|
||||||
messageQueue.pop();
|
messageQueue.pop();
|
||||||
|
|
||||||
// Обработка списка коробок от сервера
|
// Обработка списка коробок от сервера
|
||||||
if (msg.rfind("BOXES:", 0) == 0) {
|
if (msg.rfind("BOXES:", 0) == 0) {
|
||||||
std::string payload = msg.substr(6); // после "BOXES:"
|
std::string payload = msg.substr(6); // после "BOXES:"
|
||||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> parsedBoxes;
|
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> parsedBoxes;
|
||||||
if (!payload.empty()) {
|
if (!payload.empty()) {
|
||||||
auto items = split(payload, '|');
|
auto items = split(payload, '|');
|
||||||
for (auto& item : items) {
|
for (auto& item : items) {
|
||||||
if (item.empty()) continue;
|
if (item.empty()) continue;
|
||||||
auto parts = split(item, ':');
|
auto parts = split(item, ':');
|
||||||
if (parts.size() < 7) continue;
|
if (parts.size() < 7) continue;
|
||||||
try {
|
try {
|
||||||
float px = std::stof(parts[0]);
|
float px = std::stof(parts[0]);
|
||||||
float py = std::stof(parts[1]);
|
float py = std::stof(parts[1]);
|
||||||
float pz = std::stof(parts[2]);
|
float pz = std::stof(parts[2]);
|
||||||
Eigen::Quaternionf q(
|
Eigen::Quaternionf q(
|
||||||
std::stof(parts[3]),
|
std::stof(parts[3]),
|
||||||
std::stof(parts[4]),
|
std::stof(parts[4]),
|
||||||
std::stof(parts[5]),
|
std::stof(parts[5]),
|
||||||
std::stof(parts[6])
|
std::stof(parts[6])
|
||||||
);
|
);
|
||||||
Eigen::Matrix3f rot = q.toRotationMatrix();
|
Eigen::Matrix3f rot = q.toRotationMatrix();
|
||||||
parsedBoxes.emplace_back(Eigen::Vector3f{ px, py, pz }, rot);
|
parsedBoxes.emplace_back(Eigen::Vector3f{ px, py, pz }, rot);
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
// пропускаем некорректную запись
|
// пропускаем некорректную запись
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> bLock(boxesMutex);
|
std::lock_guard<std::mutex> bLock(boxesMutex);
|
||||||
serverBoxes_ = std::move(parsedBoxes);
|
serverBoxes_ = std::move(parsedBoxes);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.rfind("EVENT:", 0) == 0) {
|
if (msg.rfind("PROJECTILE:", 0) == 0) {
|
||||||
auto parts = split(msg, ':');
|
auto parts = split(msg, ':');
|
||||||
if (parts.size() < 5) continue; // EVENT:ID:TYPE:TIME:DATA...
|
if (parts.size() >= 9) {
|
||||||
|
try {
|
||||||
|
ProjectileInfo pi;
|
||||||
|
pi.shooterId = std::stoi(parts[1]);
|
||||||
|
pi.clientTime = std::stoull(parts[2]);
|
||||||
|
pi.position = Eigen::Vector3f(
|
||||||
|
std::stof(parts[3]),
|
||||||
|
std::stof(parts[4]),
|
||||||
|
std::stof(parts[5])
|
||||||
|
);
|
||||||
|
pi.direction = Eigen::Vector3f(
|
||||||
|
std::stof(parts[6]),
|
||||||
|
std::stof(parts[7]),
|
||||||
|
std::stof(parts[8])
|
||||||
|
);
|
||||||
|
std::lock_guard<std::mutex> pl(projMutex_);
|
||||||
|
pendingProjectiles_.push_back(pi);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int remoteId = std::stoi(parts[1]);
|
if (msg.rfind("EVENT:", 0) == 0) {
|
||||||
std::string subType = parts[2];
|
auto parts = split(msg, ':');
|
||||||
uint64_t sentTime = std::stoull(parts[3]);
|
if (parts.size() < 5) continue; // EVENT:ID:TYPE:TIME:DATA...
|
||||||
|
|
||||||
ClientState remoteState;
|
int remoteId = std::stoi(parts[1]);
|
||||||
|
std::string subType = parts[2];
|
||||||
|
uint64_t sentTime = std::stoull(parts[3]);
|
||||||
|
|
||||||
|
ClientState remoteState;
|
||||||
remoteState.id = remoteId;
|
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)) };
|
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;
|
remoteState.lastUpdateServerTime = uptime_timepoint;
|
||||||
|
|
||||||
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(
|
|
||||||
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{
|
if (subType == "UPD") {
|
||||||
std::stof(parts[startFrom + 7]),
|
int startFrom = 4;
|
||||||
std::stof(parts[startFrom + 8]),
|
remoteState.position = { std::stof(parts[startFrom]), std::stof(parts[startFrom + 1]), std::stof(parts[startFrom + 2]) };
|
||||||
std::stof(parts[startFrom + 9]) };
|
Eigen::Quaternionf q(
|
||||||
remoteState.velocity = std::stof(parts[startFrom + 10]);
|
std::stof(parts[startFrom + 3]),
|
||||||
remoteState.selectedVelocity = std::stoi(parts[startFrom + 11]);
|
std::stof(parts[startFrom + 4]),
|
||||||
remoteState.discreteMag = std::stof(parts[startFrom + 12]);
|
std::stof(parts[startFrom + 5]),
|
||||||
remoteState.discreteAngle = std::stoi(parts[startFrom + 13]);
|
std::stof(parts[startFrom + 6]));
|
||||||
}
|
remoteState.rotation = q.toRotationMatrix();
|
||||||
else
|
|
||||||
{
|
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
|
||||||
|
{
|
||||||
throw std::runtime_error("Unknown EVENT subtype: " + subType);
|
throw std::runtime_error("Unknown EVENT subtype: " + subType);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> pLock(playersMutex);
|
std::lock_guard<std::mutex> pLock(playersMutex);
|
||||||
auto& rp = remotePlayers[remoteId];
|
auto& rp = remotePlayers[remoteId];
|
||||||
|
|
||||||
rp.add_state(remoteState);
|
rp.add_state(remoteState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void WebSocketClient::Send(const std::string& message) {
|
void WebSocketClient::Send(const std::string& message) {
|
||||||
if (!connected) return;
|
if (!connected) return;
|
||||||
|
|
||||||
auto ss = std::make_shared<std::string>(message);
|
auto ss = std::make_shared<std::string>(message);
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||||
writeQueue_.push(ss);
|
writeQueue_.push(ss);
|
||||||
|
|
||||||
// Если сейчас ничего не записывается, инициируем первую запись
|
// Если сейчас ничего не записывается, инициируем первую запись
|
||||||
if (!isWriting_) {
|
if (!isWriting_) {
|
||||||
doWrite();
|
doWrite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketClient::doWrite() {
|
void WebSocketClient::doWrite() {
|
||||||
// Эта функция всегда вызывается под мьютексом или из колбэка
|
// Эта функция всегда вызывается под мьютексом или из колбэка
|
||||||
if (writeQueue_.empty()) {
|
if (writeQueue_.empty()) {
|
||||||
isWriting_ = false;
|
isWriting_ = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isWriting_ = true;
|
isWriting_ = true;
|
||||||
auto message = writeQueue_.front();
|
auto message = writeQueue_.front();
|
||||||
|
|
||||||
// Захватываем self (shared_from_this), чтобы объект не удалился во время записи
|
// Захватываем self (shared_from_this), чтобы объект не удалился во время записи
|
||||||
ws_->async_write(
|
ws_->async_write(
|
||||||
boost::asio::buffer(*message),
|
boost::asio::buffer(*message),
|
||||||
[this, message](boost::beast::error_code ec, std::size_t) {
|
[this, message](boost::beast::error_code ec, std::size_t) {
|
||||||
if (ec) {
|
if (ec) {
|
||||||
connected = false;
|
connected = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||||
writeQueue_.pop(); // Удаляем отправленное сообщение
|
writeQueue_.pop(); // Удаляем отправленное сообщение
|
||||||
doWrite(); // Проверяем следующее
|
doWrite(); // Проверяем следующее
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -38,6 +38,9 @@ namespace ZL {
|
|||||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> serverBoxes_;
|
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> serverBoxes_;
|
||||||
std::mutex boxesMutex;
|
std::mutex boxesMutex;
|
||||||
|
|
||||||
|
std::vector<ProjectileInfo> pendingProjectiles_;
|
||||||
|
std::mutex projMutex_;
|
||||||
|
|
||||||
void startAsyncRead();
|
void startAsyncRead();
|
||||||
void processIncomingMessage(const std::string& msg);
|
void processIncomingMessage(const std::string& msg);
|
||||||
|
|
||||||
@ -63,6 +66,8 @@ namespace ZL {
|
|||||||
std::lock_guard<std::mutex> lock(boxesMutex);
|
std::lock_guard<std::mutex> lock(boxesMutex);
|
||||||
return serverBoxes_;
|
return serverBoxes_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<ProjectileInfo> getPendingProjectiles() override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user