#ifdef NETWORK #include "WebSocketClientBase.h" #include #include // Вспомогательный split std::vector split(const std::string& s, char delimiter) { std::vector tokens; std::string token; std::istringstream tokenStream(s); while (std::getline(tokenStream, token, delimiter)) { tokens.push_back(token); } return tokens; } namespace ZL { void WebSocketClientBase::HandlePollMessage(const std::string& msg) { auto parts = split(msg, ':'); if (parts.empty()) return; if (parts[0] == "ID") { std::cout << "ID Message Received:" << msg << std::endl; clientId = std::stoi(parts[1]); if (parts.size() >= 3) { std::cout << "ID Message Received step 2" << std::endl; uint64_t serverTime = std::stoull(parts[2]); uint64_t localTime = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); std::cout << "ID Message Received localTime = " << localTime << std::endl; std::cout << "ID Message Received serverTime = " << serverTime << std::endl; // Вычисляем смещение timeOffset = static_cast(serverTime) - static_cast(localTime); std::cout << "Time synchronized. Offset: " << timeOffset << " ms" << std::endl; } return; } if (msg.rfind("BOXES:", 0) == 0) { std::string payload = msg.substr(6); std::vector> parsed; if (!payload.empty()) { auto items = split(payload, '|'); for (auto& item : items) { if (item.empty()) continue; auto parts = split(item, ':'); if (parts.size() < 9) { return; } try { int idx = std::stoi(parts[0]); float px = std::stof(parts[1]); float py = std::stof(parts[2]); float pz = std::stof(parts[3]); Eigen::Quaternionf q( std::stof(parts[4]), std::stof(parts[5]), std::stof(parts[6]), std::stof(parts[7]) ); bool destroyed = (std::stoi(parts[8]) != 0); Eigen::Matrix3f rot = q.toRotationMatrix(); parsed.emplace_back(idx, Eigen::Vector3f{ px, py, pz }, rot, destroyed); } catch (...) { return; } } } int maxIdx = -1; for (auto& t : parsed) { int idx = std::get<0>(t); if (idx > maxIdx) maxIdx = idx; } if (maxIdx < 0) { serverBoxes_.clear(); serverBoxesDestroyed_.clear(); return; } serverBoxes_.clear(); serverBoxes_.resize((size_t)maxIdx + 1); serverBoxesDestroyed_.clear(); serverBoxesDestroyed_.resize((size_t)maxIdx + 1, true); for (auto& t : parsed) { int idx = std::get<0>(t); const Eigen::Vector3f& pos = std::get<1>(t); const Eigen::Matrix3f& rot = std::get<2>(t); bool destroyed = std::get<3>(t); if (idx >= 0 && idx < serverBoxes_.size()) { serverBoxes_[idx] = { pos, rot }; serverBoxesDestroyed_[idx] = destroyed; } } return; } if (msg.rfind("RESPAWN_ACK:", 0) == 0) { //auto parts = split(msg, ':'); if (parts.size() >= 2) { try { int respawnedPlayerId = std::stoi(parts[1]); pendingRespawns_.push_back(respawnedPlayerId); std::cout << "Client: Received RESPAWN_ACK for player " << respawnedPlayerId << std::endl; } catch (...) {} } return; } if (msg.rfind("BOX_DESTROYED:", 0) == 0) { //auto parts = split(msg, ':'); if (parts.size() >= 7) { try { BoxDestroyedInfo destruction; destruction.boxIndex = std::stoi(parts[1]); destruction.serverTime = std::stoull(parts[2]); destruction.position = Eigen::Vector3f( std::stof(parts[3]), std::stof(parts[4]), std::stof(parts[5]) ); destruction.destroyedBy = std::stoi(parts[6]); pendingBoxDestructions_.push_back(destruction); std::cout << "Client: Received BOX_DESTROYED for box " << destruction.boxIndex << " destroyed by player " << destruction.destroyedBy << std::endl; } catch (const std::exception& e) { std::cerr << "Client: Error parsing BOX_DESTROYED: " << e.what() << std::endl; } } return; } if (msg.rfind("PROJECTILE:", 0) == 0) { //auto parts = split(msg, ':'); if (parts.size() >= 10) { 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]) ); Eigen::Quaternionf q( std::stof(parts[6]), std::stof(parts[7]), std::stof(parts[8]), std::stof(parts[9]) ); pi.rotation = q.toRotationMatrix(); pi.velocity = std::stof(parts[10]); pendingProjectiles_.push_back(pi); } catch (...) {} } return; } if (msg.rfind("DEAD:", 0) == 0) { //auto parts = split(msg, ':'); if (parts.size() >= 7) { try { DeathInfo di; di.serverTime = std::stoull(parts[1]); di.targetId = std::stoi(parts[2]); di.position = Eigen::Vector3f( std::stof(parts[3]), std::stof(parts[4]), std::stof(parts[5]) ); di.killerId = std::stoi(parts[6]); pendingDeaths_.push_back(di); } catch (...) {} } return; } if (msg.rfind("SPAWN:", 0) == 0) { // SPAWN:playerId:serverTime:<14 полей> if (parts.size() >= 3 + 14) { try { int pid = std::stoi(parts[1]); uint64_t serverTime = std::stoull(parts[2]); ClientState st; st.id = pid; std::chrono::system_clock::time_point tp{ std::chrono::milliseconds(serverTime) }; st.lastUpdateServerTime = tp; // данные начинаются с parts[3] st.handle_full_sync(parts, 3); pendingSpawns_.push_back(st); std::cout << "Client: SPAWN received for player " << pid << std::endl; } catch (...) {} } return; } if (msg.rfind("EVENT:", 0) == 0) { //auto parts = split(msg, ':'); if (parts.size() < 5) return; // 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::milliseconds(sentTime)) }; 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{ 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); } { auto& rp = remotePlayers[remoteId]; if (!rp.timedStates.empty()) { const ClientState& last = rp.timedStates.back(); remoteState.nickname = last.nickname; remoteState.shipType = last.shipType; } rp.add_state(remoteState); } } if (msg.rfind("SNAPSHOT:", 0) == 0) { auto mainParts = split(msg.substr(9), '|'); // Отсекаем "SNAPSHOT:" и делим по игрокам if (mainParts.empty()) return; uint64_t serverTimestamp = std::stoull(mainParts[0]); std::chrono::system_clock::time_point serverTime{ std::chrono::milliseconds(serverTimestamp) }; for (size_t i = 1; i < mainParts.size(); ++i) { auto playerParts = split(mainParts[i], ':'); if (playerParts.size() < 15) return; // ID + 14 полей ClientState int rId = std::stoi(playerParts[0]); if (rId == clientId) return; // Свое состояние игрок знает лучше всех, (Client Side Prediction) ClientState remoteState; remoteState.id = rId; remoteState.lastUpdateServerTime = serverTime; // Используем твой handle_full_sync, начиная со 2-го индекса (пропускаем ID в playerParts) remoteState.handle_full_sync(playerParts, 1); remotePlayers[rId].add_state(remoteState); } } if (msg.rfind("PLAYERINFO:", 0) == 0) { if (parts.size() >= 4) { try { int pid = std::stoi(parts[1]); if (pid == clientId) { return; } std::string nick = parts[2]; int st = std::stoi(parts[3]); auto it = remotePlayers.find(pid); if (it != remotePlayers.end() && !it->second.timedStates.empty()) { auto& states = it->second.timedStates; states.back().nickname = nick; states.back().shipType = st; } else { ClientState cs; cs.id = pid; cs.nickname = nick; cs.shipType = st; cs.lastUpdateServerTime = std::chrono::system_clock::now(); remotePlayers[pid].add_state(cs); } std::cout << "Client: PLAYERINFO received. id=" << pid << " nick=" << nick << " shipType=" << st << std::endl; } catch (...) { } } return; } } std::string WebSocketClientBase::SignMessage(const std::string& msg) { #ifdef ENABLE_NETWORK_CHECKSUM size_t hashValue = fnv1a_hash(msg + NET_SECRET); std::stringstream ss; ss << msg << "#hash:" << std::hex << hashValue; return ss.str(); #else return msg; #endif } std::vector WebSocketClientBase::getPendingProjectiles() { std::vector copy; copy.swap(pendingProjectiles_); return copy; } std::vector WebSocketClientBase::getPendingDeaths() { std::vector copy; copy.swap(pendingDeaths_); return copy; } std::vector WebSocketClientBase::getPendingRespawns() { std::vector copy; copy.swap(pendingRespawns_); return copy; } std::vector WebSocketClientBase::getPendingBoxDestructions() { std::vector copy; copy.swap(pendingBoxDestructions_); return copy; } std::vector WebSocketClientBase::getPendingSpawns() { std::vector copy; copy.swap(pendingSpawns_); return copy; } } #endif