Restore as it was before

This commit is contained in:
Vladislav Khorev 2026-02-22 14:08:12 +03:00
parent 84d8ee7eee
commit efad2dde3e

View File

@ -41,42 +41,6 @@ std::vector<std::string> split(const std::string& s, char delimiter) {
return tokens; return tokens;
} }
// Вспомогательная функция для проверки столкновения снаряда с объектом-сферой
bool checkSegmentSphereCollision(
int x,
const Eigen::Vector3f& pStart,
const Eigen::Vector3f& pEnd,
const Eigen::Vector3f& targetCenter,
float combinedRadius)
{
Eigen::Vector3f segment = pEnd - pStart;
Eigen::Vector3f toTarget = targetCenter - pStart;
float segmentLenSq = segment.squaredNorm();
if (segmentLenSq < 1e-6f) {
return toTarget.norm() <= combinedRadius;
}
// Находим проекцию точки targetCenter на прямую, содержащую отрезок
// t — это нормализованный параметр вдоль отрезка (от 0 до 1)
float t = toTarget.dot(segment) / segmentLenSq;
// Ограничиваем t, чтобы найти ближайшую точку именно на ОТРЕЗКЕ
t = std::max(0.0f, std::min(1.0f, t));
// Ближайшая точка на отрезке к центру цели
Eigen::Vector3f closestPoint = pStart + t * segment;
/*
std::cout << "Collision for box: " << x << " pStart: " << pStart
<< " pEnd: " << pEnd
<< " targetCenter: " << targetCenter
<< " closestPoint: " << closestPoint
<< " t: " << t << std::endl;
*/
// Проверяем расстояние от ближайшей точки до центра цели
return (targetCenter - closestPoint).squaredNorm() <= (combinedRadius * combinedRadius);
}
struct ServerBox { struct ServerBox {
Eigen::Vector3f position; Eigen::Vector3f position;
Eigen::Matrix3f rotation; Eigen::Matrix3f rotation;
@ -99,6 +63,9 @@ struct BoxDestroyedInfo {
int destroyedBy = -1; int destroyedBy = -1;
}; };
std::vector<BoxDestroyedInfo> g_boxDestructions;
std::mutex g_boxDestructions_mutex;
std::vector<ServerBox> g_serverBoxes; std::vector<ServerBox> g_serverBoxes;
std::mutex g_boxes_mutex; std::mutex g_boxes_mutex;
@ -217,15 +184,12 @@ public:
timer->expires_after(std::chrono::milliseconds(100)); timer->expires_after(std::chrono::milliseconds(100));
timer->async_wait([self = shared_from_this(), timer](const boost::system::error_code& ec) { timer->async_wait([self = shared_from_this(), timer](const boost::system::error_code& ec) {
if (!ec) { if (!ec) {
int64_t serverNow = std::chrono::duration_cast<std::chrono::milliseconds>( self->send_message("ID:" + std::to_string(self->id_));
std::chrono::system_clock::now().time_since_epoch()).count();
self->send_message("ID:" + std::to_string(self->id_) + ":" + std::to_string(serverNow));
self->do_read(); self->do_read();
} }
}); });
} }
ClientState get_latest_state(std::chrono::system_clock::time_point now) { ClientState get_latest_state(std::chrono::system_clock::time_point now) {
if (timedClientStates.timedStates.empty()) { if (timedClientStates.timedStates.empty()) {
return {}; return {};
@ -295,8 +259,6 @@ private:
} }
std::string cleanMessage = msg.substr(0, msg.find("#hash:")); std::string cleanMessage = msg.substr(0, msg.find("#hash:"));
std::cout << "Received from player " << id_ << ": " << cleanMessage << std::endl; std::cout << "Received from player " << id_ << ": " << cleanMessage << std::endl;
auto parts = split(cleanMessage, ':'); auto parts = split(cleanMessage, ':');
@ -427,61 +389,240 @@ void broadcastToAll(const std::string& message) {
} }
} }
void checkShipBoxCollisions(std::chrono::system_clock::time_point now, uint64_t now_ms, std::vector<BoxDestroyedInfo>& boxDestructions) { void update_world(net::steady_timer& timer, net::io_context& ioc) {
// Внимание: Мьютексы g_boxes_mutex и g_sessions_mutex должны быть захвачены
// внешним кодом в update_world перед вызовом этой функции.
const float shipCollisionRadius = 15.0f; static auto last_snapshot_time = std::chrono::steady_clock::now();
const float boxCollisionRadius = 2.0f; auto now = std::chrono::steady_clock::now();
const float thresh = shipCollisionRadius + boxCollisionRadius; /*static uint64_t lastTickCount = 0;
const float threshSq = thresh * thresh;
for (size_t bi = 0; bi < g_serverBoxes.size(); ++bi) { if (lastTickCount == 0) {
if (g_serverBoxes[bi].destroyed) continue; //lastTickCount = SDL_GetTicks64();
lastTickCount = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count();
// Центр ящика в мировых координатах lastTickCount = (lastTickCount / 50) * 50;
Eigen::Vector3f boxWorld = g_serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
for (auto& session : g_sessions) { return;
int playerId = session->get_id();
// Пропускаем мертвых игроков
{
// Если g_dead_mutex не захвачен глобально в update_world, раскомментируйте:
// std::lock_guard<std::mutex> gd(g_dead_mutex);
if (g_dead_players.count(playerId)) continue;
} }
ClientState shipState;
// Получаем состояние игрока на текущий момент времени сервера
if (!session->fetchStateAtTime(now, shipState)) continue;
Eigen::Vector3f diff = shipState.position - boxWorld; auto newTickCount = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count();
// Проверка столкновения сфер newTickCount = (newTickCount / 50) * 50;
if (diff.squaredNorm() <= threshSq) {
g_serverBoxes[bi].destroyed = true;
// Регистрируем уничтожение ящика int64_t deltaMs = static_cast<int64_t>(newTickCount - lastTickCount);
BoxDestroyedInfo destruction;
destruction.boxIndex = static_cast<int>(bi);
destruction.serverTime = now_ms;
destruction.position = boxWorld;
destruction.destroyedBy = playerId;
boxDestructions.push_back(destruction); 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
std::cout << "Server: Box " << bi << " smashed by player " << playerId << std::endl;
// Один ящик не может быть уничтожен дважды за один проход
// Рассылка Snapshot раз в 1000мс
/*
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_snapshot_time).count() >= 1000) {
last_snapshot_time = now;
auto system_now = std::chrono::system_clock::now();
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);
// Формируем общую строку состояний всех игроков
for (auto& session : g_sessions) {
ClientState st = session->get_latest_state(system_now);
snapshot_msg += "|" + std::to_string(session->get_id()) + ":" + st.formPingMessageContent();
}
for (auto& session : g_sessions) {
session->send_message(snapshot_msg);
}
}*/
const std::chrono::milliseconds interval(50);
timer.expires_after(interval);
timer.async_wait([&](const boost::system::error_code& ec) {
if (ec) return;
auto now = std::chrono::system_clock::now();
uint64_t now_ms = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
std::vector<DeathInfo> deathEvents;
{
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; break;
} }
} }
} }
}
void dispatchEvents(const std::vector<DeathInfo>& deathEvents, const std::vector<BoxDestroyedInfo>& boxDestructions) { if (hitDetected) continue;
// 1. Рассылка событий смерти игроков }
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);
}
}
}
}
{
std::lock_guard<std::mutex> bm(g_boxes_mutex);
const float projectileHitRadius = 1.5f;
const float boxCollisionRadius = 2.0f;
std::vector<std::pair<size_t, size_t>> boxProjectileCollisions;
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, 6.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) {
g_serverBoxes[boxIdx].destroyed = true;
Eigen::Vector3f boxWorld = g_serverBoxes[boxIdx].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
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);
}
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) {
{
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;
}
}
}
}
if (!deathEvents.empty()) {
for (const auto& death : deathEvents) { for (const auto& death : deathEvents) {
std::string deadMsg = "DEAD:" + std::string deadMsg = "DEAD:" +
std::to_string(death.serverTime) + ":" + std::to_string(death.serverTime) + ":" +
@ -496,10 +637,11 @@ void dispatchEvents(const std::vector<DeathInfo>& deathEvents, const std::vector
std::cout << "Server: Sent DEAD event - Player " << death.targetId std::cout << "Server: Sent DEAD event - Player " << death.targetId
<< " killed by " << death.killerId << std::endl; << " killed by " << death.killerId << std::endl;
} }
}
// 2. Рассылка событий разрушения ящиков {
std::lock_guard<std::mutex> dm(g_boxDestructions_mutex);
for (const auto& destruction : boxDestructions) { for (const auto& destruction : g_boxDestructions) {
std::string boxMsg = "BOX_DESTROYED:" + std::string boxMsg = "BOX_DESTROYED:" +
std::to_string(destruction.boxIndex) + ":" + std::to_string(destruction.boxIndex) + ":" +
std::to_string(destruction.serverTime) + ":" + std::to_string(destruction.serverTime) + ":" +
@ -509,105 +651,11 @@ void dispatchEvents(const std::vector<DeathInfo>& deathEvents, const std::vector
std::to_string(destruction.destroyedBy); std::to_string(destruction.destroyedBy);
broadcastToAll(boxMsg); broadcastToAll(boxMsg);
std::cout << "Server: Broadcasted BOX_DESTROYED for box " << destruction.boxIndex << std::endl;
std::cout << "Server: Broadcasted BOX_DESTROYED for box "
<< destruction.boxIndex << std::endl;
} }
} g_boxDestructions.clear();
void update_world(net::steady_timer& timer, net::io_context& ioc) {
const std::chrono::milliseconds interval(50);
timer.expires_after(interval);
timer.async_wait([&](const boost::system::error_code& ec) {
if (ec) return;
auto now = std::chrono::system_clock::now();
uint64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
float dt = 50.0f / 1000.0f;
std::vector<DeathInfo> deathEvents;
std::vector<BoxDestroyedInfo> boxDestructions;
std::vector<int> projectilesToRemove;
{
// Захватываем необходимые данные под мьютексами один раз
std::lock_guard<std::mutex> pl(g_projectiles_mutex);
std::lock_guard<std::mutex> bm(g_boxes_mutex);
std::lock_guard<std::mutex> sm(g_sessions_mutex);
std::lock_guard<std::mutex> gm(g_dead_mutex);
for (size_t i = 0; i < g_projectiles.size(); ++i) {
auto& pr = g_projectiles[i];
Eigen::Vector3f oldPos = pr.pos;
pr.pos += pr.vel * dt;
Eigen::Vector3f newPos = pr.pos;
// 1. Проверка времени жизни снаряда
if (now_ms > pr.spawnMs + static_cast<uint64_t>(pr.lifeMs)) {
projectilesToRemove.push_back(static_cast<int>(i));
continue;
} }
bool hitDetected = false;
// 2. Проверка коллизий снаряда с игроками (Ray-cast)
for (auto& session : g_sessions) {
int targetId = session->get_id();
if (targetId == pr.shooterId || g_dead_players.count(targetId)) continue;
ClientState targetState;
if (!session->fetchStateAtTime(now, targetState)) continue;
if (checkSegmentSphereCollision(0, oldPos, newPos, targetState.position, 15.0f + 1.5f)) {
deathEvents.push_back({ targetId, now_ms, newPos, pr.shooterId });
g_dead_players.insert(targetId);
hitDetected = true;
break;
}
}
if (hitDetected) {
projectilesToRemove.push_back(static_cast<int>(i));
continue;
}
// 3. Проверка коллизий снаряда с ящиками (Ray-cast)
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);
if (checkSegmentSphereCollision(bi, oldPos, newPos, boxWorld, 2.0f + 1.5f)) {
g_serverBoxes[bi].destroyed = true;
boxDestructions.push_back({ static_cast<int>(bi), now_ms, boxWorld, pr.shooterId });
hitDetected = true;
break;
}
}
if (hitDetected) {
projectilesToRemove.push_back(static_cast<int>(i));
}
}
// Удаляем отработавшие снаряды (с конца)
std::sort(projectilesToRemove.rbegin(), projectilesToRemove.rend());
for (int idx : projectilesToRemove) {
g_projectiles.erase(g_projectiles.begin() + idx);
}
}
// 4. Отдельная проверка столкновения кораблей с ящиками (Point-Sphere)
// Эту логику оставляем отдельно, так как она не привязана к снарядам
checkShipBoxCollisions(now, now_ms, boxDestructions);
// Рассылка событий
dispatchEvents(deathEvents, boxDestructions);
update_world(timer, ioc); update_world(timer, ioc);
}); });
} }