Working on server optimization

This commit is contained in:
Vladislav Khorev 2026-02-26 22:26:43 +03:00
parent e8f1786a2a
commit be2aa76f8b
3 changed files with 204 additions and 231 deletions

View File

@ -323,7 +323,6 @@ private:
receivedState.shipType = this->shipType; receivedState.shipType = this->shipType;
timedClientStates.add_state(receivedState); timedClientStates.add_state(receivedState);
retranslateMessage(cleanMessage);
} }
else if (type == "RESPAWN") { else if (type == "RESPAWN") {
{ {
@ -433,16 +432,6 @@ private:
} }
} }
void retranslateMessage(const std::string& msg) {
std::string event_msg = "EVENT:" + std::to_string(id_) + ":" + msg;
std::lock_guard<std::mutex> lock(g_sessions_mutex);
for (auto& session : g_sessions) {
if (session->get_id() != id_) {
session->send_message(event_msg);
}
}
}
}; };
void broadcastToAll(const std::string& message) { void broadcastToAll(const std::string& message) {
@ -454,273 +443,257 @@ void broadcastToAll(const std::string& message) {
void update_world(net::steady_timer& timer, net::io_context& ioc) { void update_world(net::steady_timer& timer, net::io_context& ioc) {
static auto last_snapshot_time = std::chrono::steady_clock::now(); static auto last_snapshot_time = std::chrono::system_clock::now();
auto now = std::chrono::steady_clock::now();
/*static uint64_t lastTickCount = 0;
if (lastTickCount == 0) { auto now = std::chrono::system_clock::now();
//lastTickCount = SDL_GetTicks64(); uint64_t now_ms = static_cast<uint64_t>(
lastTickCount = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
std::chrono::system_clock::now().time_since_epoch()
).count();
lastTickCount = (lastTickCount / 50) * 50; // --- Snapshot every 500ms ---
/*if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_snapshot_time).count() >= 500) {
return;
}
auto newTickCount = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count();
newTickCount = (newTickCount / 50) * 50;
int64_t deltaMs = static_cast<int64_t>(newTickCount - lastTickCount);
std::chrono::system_clock::time_point nowRounded = std::chrono::system_clock::time_point(std::chrono::milliseconds(newTickCount));
*/
// For each player
// Get letest state + add time (until newTickCount)
// Calculate if collisions with boxes
// Рассылка Snapshot раз в 1000мс
/*
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_snapshot_time).count() >= 1000) {
last_snapshot_time = now; last_snapshot_time = now;
auto system_now = std::chrono::system_clock::now(); std::string snapshot_msg = "SNAPSHOT:" + std::to_string(now_ms);
std::string snapshot_msg = "SNAPSHOT:" + std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(
system_now.time_since_epoch()).count()
);
std::lock_guard<std::mutex> lock(g_sessions_mutex); std::lock_guard<std::mutex> lock(g_sessions_mutex);
// Формируем общую строку состояний всех игроков
for (auto& session : g_sessions) { for (auto& session : g_sessions) {
ClientState st = session->get_latest_state(system_now); ClientState st = session->get_latest_state(now);
snapshot_msg += "|" + std::to_string(session->get_id()) + ":" + st.formPingMessageContent(); snapshot_msg += "|" + std::to_string(session->get_id()) + ":" + st.formPingMessageContent();
} }
for (auto& session : g_sessions) { for (auto& session : g_sessions) {
session->send_message(snapshot_msg); session->send_message(snapshot_msg);
} }
}*/ }*/
const std::chrono::milliseconds interval(50); // --- Tick: broadcast each player's latest state to all others (20Hz) ---
timer.expires_after(interval); // Send the raw last-known state with its original timestamp, NOT an extrapolated one.
// Extrapolating here causes snap-back: if a player stops rotating, the server would
// keep sending over-rotated positions until the new state arrives, then B snaps back.
{
std::lock_guard<std::mutex> lock(g_sessions_mutex);
for (auto& sender : g_sessions) {
if (sender->timedClientStates.timedStates.empty()) continue;
timer.async_wait([&](const boost::system::error_code& ec) { const ClientState& st = sender->timedClientStates.timedStates.back();
if (ec) return; uint64_t stateTime = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(
st.lastUpdateServerTime.time_since_epoch()).count());
auto now = std::chrono::system_clock::now(); std::string event_msg = "EVENT:" + std::to_string(sender->get_id()) +
uint64_t now_ms = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count()); ":UPD:" + std::to_string(stateTime) + ":" + st.formPingMessageContent();
std::vector<DeathInfo> deathEvents; for (auto& receiver : g_sessions) {
if (receiver->get_id() != sender->get_id()) {
{ receiver->send_message(event_msg);
std::lock_guard<std::mutex> pl(g_projectiles_mutex);
std::vector<int> indicesToRemove;
float dt = 50.0f / 1000.0f;
for (size_t i = 0; i < g_projectiles.size(); ++i) {
auto& pr = g_projectiles[i];
pr.pos += pr.vel * dt;
if (now_ms > pr.spawnMs + static_cast<uint64_t>(pr.lifeMs)) {
indicesToRemove.push_back(static_cast<int>(i));
continue;
}
bool hitDetected = false;
{
std::lock_guard<std::mutex> lm(g_sessions_mutex);
std::lock_guard<std::mutex> gd(g_dead_mutex);
for (auto& session : g_sessions) {
int targetId = session->get_id();
if (targetId == pr.shooterId) continue;
if (g_dead_players.find(targetId) != g_dead_players.end()) continue;
ClientState targetState;
if (!session->fetchStateAtTime(now, targetState)) continue;
Eigen::Vector3f diff = pr.pos - targetState.position;
const float shipRadius = 15.0f;
const float projectileRadius = 1.5f;
float combinedRadius = shipRadius + projectileRadius;
if (diff.squaredNorm() <= combinedRadius * combinedRadius) {
DeathInfo death;
death.targetId = targetId;
death.serverTime = now_ms;
death.position = pr.pos;
death.killerId = pr.shooterId;
deathEvents.push_back(death);
g_dead_players.insert(targetId);
indicesToRemove.push_back(static_cast<int>(i));
hitDetected = true;
std::cout << "Server: *** HIT DETECTED! ***" << std::endl;
std::cout << "Server: Projectile at ("
<< pr.pos.x() << ", " << pr.pos.y() << ", " << pr.pos.z()
<< ") hit player " << targetId << std::endl;
break;
}
}
}
if (hitDetected) continue;
}
if (!indicesToRemove.empty()) {
std::sort(indicesToRemove.rbegin(), indicesToRemove.rend());
for (int idx : indicesToRemove) {
if (idx >= 0 && idx < (int)g_projectiles.size()) {
g_projectiles.erase(g_projectiles.begin() + idx);
}
} }
} }
} }
}
{ // --- Tick: projectile movement and hit detection ---
std::lock_guard<std::mutex> bm(g_boxes_mutex); const float dt = 50.0f / 1000.0f;
//const float projectileHitRadius = 1.5f; std::vector<DeathInfo> deathEvents;
const float projectileHitRadius = 5.0f;
const float boxCollisionRadius = 2.0f;
std::vector<std::pair<size_t, size_t>> boxProjectileCollisions; {
std::lock_guard<std::mutex> pl(g_projectiles_mutex);
std::vector<int> indicesToRemove;
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) { for (size_t i = 0; i < g_projectiles.size(); ++i) {
if (g_serverBoxes[bi].destroyed) continue; auto& pr = g_projectiles[i];
Eigen::Vector3f boxWorld = g_serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f); pr.pos += pr.vel * dt;
for (size_t pi = 0; pi < g_projectiles.size(); ++pi) { if (now_ms > pr.spawnMs + static_cast<uint64_t>(pr.lifeMs)) {
const auto& pr = g_projectiles[pi]; indicesToRemove.push_back(static_cast<int>(i));
Eigen::Vector3f diff = pr.pos - boxWorld; continue;
//std::cout << "diff norm is " << diff.norm() << std::endl;
float thresh = boxCollisionRadius + projectileHitRadius;
if (diff.squaredNorm() <= thresh * thresh) {
boxProjectileCollisions.push_back({ bi, pi });
}
}
} }
for (const auto& [boxIdx, projIdx] : boxProjectileCollisions) { bool hitDetected = false;
g_serverBoxes[boxIdx].destroyed = true;
Eigen::Vector3f boxWorld = g_serverBoxes[boxIdx].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f); {
std::lock_guard<std::mutex> lm(g_sessions_mutex);
BoxDestroyedInfo destruction; std::lock_guard<std::mutex> gd(g_dead_mutex);
destruction.boxIndex = static_cast<int>(boxIdx);
destruction.serverTime = now_ms;
destruction.position = boxWorld;
destruction.destroyedBy = g_projectiles[projIdx].shooterId;
{
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex);
g_boxDestructions.push_back(destruction);
}
std::cout << "Server: Box " << boxIdx << " destroyed by projectile from player "
<< g_projectiles[projIdx].shooterId << std::endl;
}
}
{
std::lock_guard<std::mutex> bm(g_boxes_mutex);
std::lock_guard<std::mutex> lm(g_sessions_mutex);
const float shipCollisionRadius = 15.0f;
const float boxCollisionRadius = 2.0f;
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) {
if (g_serverBoxes[bi].destroyed) continue;
Eigen::Vector3f boxWorld = g_serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
for (auto& session : g_sessions) { for (auto& session : g_sessions) {
{ int targetId = session->get_id();
std::lock_guard<std::mutex> gd(g_dead_mutex);
if (g_dead_players.find(session->get_id()) != g_dead_players.end()) {
continue;
}
}
ClientState shipState; if (targetId == pr.shooterId) continue;
if (!session->fetchStateAtTime(now, shipState)) continue; if (g_dead_players.find(targetId) != g_dead_players.end()) continue;
Eigen::Vector3f diff = shipState.position - boxWorld; ClientState targetState;
float thresh = shipCollisionRadius + boxCollisionRadius; if (!session->fetchStateAtTime(now, targetState)) continue;
if (diff.squaredNorm() <= thresh * thresh) { Eigen::Vector3f diff = pr.pos - targetState.position;
g_serverBoxes[bi].destroyed = true; const float shipRadius = 15.0f;
const float projectileRadius = 1.5f;
float combinedRadius = shipRadius + projectileRadius;
BoxDestroyedInfo destruction; if (diff.squaredNorm() <= combinedRadius * combinedRadius) {
destruction.boxIndex = static_cast<int>(bi); DeathInfo death;
destruction.serverTime = now_ms; death.targetId = targetId;
destruction.position = boxWorld; death.serverTime = now_ms;
destruction.destroyedBy = session->get_id(); death.position = pr.pos;
death.killerId = pr.shooterId;
{ deathEvents.push_back(death);
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex); g_dead_players.insert(targetId);
g_boxDestructions.push_back(destruction); indicesToRemove.push_back(static_cast<int>(i));
} hitDetected = true;
std::cout << "Server: Box " << bi << " destroyed by ship collision with player " std::cout << "Server: *** HIT DETECTED! ***" << std::endl;
<< session->get_id() << std::endl; std::cout << "Server: Projectile at ("
<< pr.pos.x() << ", " << pr.pos.y() << ", " << pr.pos.z()
<< ") hit player " << targetId << std::endl;
break; break;
} }
} }
} }
if (hitDetected) continue;
} }
if (!deathEvents.empty()) { if (!indicesToRemove.empty()) {
for (const auto& death : deathEvents) { std::sort(indicesToRemove.rbegin(), indicesToRemove.rend());
std::string deadMsg = "DEAD:" + for (int idx : indicesToRemove) {
std::to_string(death.serverTime) + ":" + if (idx >= 0 && idx < (int)g_projectiles.size()) {
std::to_string(death.targetId) + ":" + g_projectiles.erase(g_projectiles.begin() + idx);
std::to_string(death.position.x()) + ":" + }
std::to_string(death.position.y()) + ":" + }
std::to_string(death.position.z()) + ":" + }
std::to_string(death.killerId); }
broadcastToAll(deadMsg); // --- Tick: box-projectile collisions ---
{
std::lock_guard<std::mutex> bm(g_boxes_mutex);
const float projectileHitRadius = 5.0f;
const float boxCollisionRadius = 2.0f;
std::cout << "Server: Sent DEAD event - Player " << death.targetId std::vector<std::pair<size_t, size_t>> boxProjectileCollisions;
<< " killed by " << death.killerId << std::endl;
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) {
if (g_serverBoxes[bi].destroyed) continue;
Eigen::Vector3f boxWorld = g_serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
for (size_t pi = 0; pi < g_projectiles.size(); ++pi) {
const auto& pr = g_projectiles[pi];
Eigen::Vector3f diff = pr.pos - boxWorld;
float thresh = boxCollisionRadius + projectileHitRadius;
if (diff.squaredNorm() <= thresh * thresh) {
boxProjectileCollisions.push_back({ bi, pi });
}
} }
} }
{ for (const auto& [boxIdx, projIdx] : boxProjectileCollisions) {
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex); g_serverBoxes[boxIdx].destroyed = true;
for (const auto& destruction : g_boxDestructions) {
std::string boxMsg = "BOX_DESTROYED:" +
std::to_string(destruction.boxIndex) + ":" +
std::to_string(destruction.serverTime) + ":" +
std::to_string(destruction.position.x()) + ":" +
std::to_string(destruction.position.y()) + ":" +
std::to_string(destruction.position.z()) + ":" +
std::to_string(destruction.destroyedBy);
broadcastToAll(boxMsg); Eigen::Vector3f boxWorld = g_serverBoxes[boxIdx].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
std::cout << "Server: Broadcasted BOX_DESTROYED for box " << destruction.boxIndex << std::endl;
BoxDestroyedInfo destruction;
destruction.boxIndex = static_cast<int>(boxIdx);
destruction.serverTime = now_ms;
destruction.position = boxWorld;
destruction.destroyedBy = g_projectiles[projIdx].shooterId;
{
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex);
g_boxDestructions.push_back(destruction);
} }
g_boxDestructions.clear();
}
std::cout << "Server: Box " << boxIdx << " destroyed by projectile from player "
<< g_projectiles[projIdx].shooterId << std::endl;
}
}
// --- Tick: box-ship collisions ---
{
std::lock_guard<std::mutex> bm(g_boxes_mutex);
std::lock_guard<std::mutex> lm(g_sessions_mutex);
const float shipCollisionRadius = 15.0f;
const float boxCollisionRadius = 2.0f;
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) {
if (g_serverBoxes[bi].destroyed) continue;
Eigen::Vector3f boxWorld = g_serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
for (auto& session : g_sessions) {
{
std::lock_guard<std::mutex> gd(g_dead_mutex);
if (g_dead_players.find(session->get_id()) != g_dead_players.end()) {
continue;
}
}
ClientState shipState;
if (!session->fetchStateAtTime(now, shipState)) continue;
Eigen::Vector3f diff = shipState.position - boxWorld;
float thresh = shipCollisionRadius + boxCollisionRadius;
if (diff.squaredNorm() <= thresh * thresh) {
g_serverBoxes[bi].destroyed = true;
BoxDestroyedInfo destruction;
destruction.boxIndex = static_cast<int>(bi);
destruction.serverTime = now_ms;
destruction.position = boxWorld;
destruction.destroyedBy = session->get_id();
{
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex);
g_boxDestructions.push_back(destruction);
}
std::cout << "Server: Box " << bi << " destroyed by ship collision with player "
<< session->get_id() << std::endl;
break;
}
}
}
}
// --- Broadcast deaths ---
for (const auto& death : deathEvents) {
std::string deadMsg = "DEAD:" +
std::to_string(death.serverTime) + ":" +
std::to_string(death.targetId) + ":" +
std::to_string(death.position.x()) + ":" +
std::to_string(death.position.y()) + ":" +
std::to_string(death.position.z()) + ":" +
std::to_string(death.killerId);
broadcastToAll(deadMsg);
std::cout << "Server: Sent DEAD event - Player " << death.targetId
<< " killed by " << death.killerId << std::endl;
}
// --- Broadcast box destructions ---
{
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex);
for (const auto& destruction : g_boxDestructions) {
std::string boxMsg = "BOX_DESTROYED:" +
std::to_string(destruction.boxIndex) + ":" +
std::to_string(destruction.serverTime) + ":" +
std::to_string(destruction.position.x()) + ":" +
std::to_string(destruction.position.y()) + ":" +
std::to_string(destruction.position.z()) + ":" +
std::to_string(destruction.destroyedBy);
broadcastToAll(boxMsg);
std::cout << "Server: Broadcasted BOX_DESTROYED for box " << destruction.boxIndex << std::endl;
}
g_boxDestructions.clear();
}
// --- Schedule next tick in 50ms ---
timer.expires_after(std::chrono::milliseconds(50));
timer.async_wait([&timer, &ioc](const boost::system::error_code& ec) {
if (ec) return;
update_world(timer, ioc); update_world(timer, ioc);
}); });
} }

View File

@ -169,7 +169,7 @@ void ClientState::apply_lag_compensation(std::chrono::system_clock::time_point n
while (deltaMsLeftover > 0) while (deltaMsLeftover > 0)
{ {
long long miniDelta = 50; long long miniDelta = std::min(50LL, deltaMsLeftover);
simulate_physics(miniDelta); simulate_physics(miniDelta);
deltaMsLeftover -= miniDelta; deltaMsLeftover -= miniDelta;
} }
@ -207,7 +207,7 @@ void ClientState::handle_full_sync(const std::vector<std::string>& parts, int st
discreteAngle = std::stoi(parts[startFrom + 13]); discreteAngle = std::stoi(parts[startFrom + 13]);
} }
std::string ClientState::formPingMessageContent() std::string ClientState::formPingMessageContent() const
{ {
Eigen::Quaternionf q(rotation); Eigen::Quaternionf q(rotation);

View File

@ -50,7 +50,7 @@ struct ClientState {
void handle_full_sync(const std::vector<std::string>& parts, int startFrom); void handle_full_sync(const std::vector<std::string>& parts, int startFrom);
std::string formPingMessageContent(); std::string formPingMessageContent() const;
}; };
struct ClientStateInterval struct ClientStateInterval