#include #include #include #include #include #include #include #include #include #define _USE_MATH_DEFINES #include #include "../src/network/ClientState.h" // Вспомогательный 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 beast = boost::beast; namespace http = beast::http; namespace websocket = beast::websocket; namespace net = boost::asio; using tcp = net::ip::tcp; class Session; std::vector> g_sessions; std::mutex g_sessions_mutex; class Session : public std::enable_shared_from_this { websocket::stream ws_; beast::flat_buffer buffer_; int id_; ClientState state_; void process_message(const std::string& msg) { auto now_server = std::chrono::steady_clock::now(); // Предположим формат сообщений: // ROT:ANGLE:MAG:TIMESTAMP // VEL:SELECTED_VEL:TIMESTAMP auto parts = split(msg, ':'); if (parts.empty()) return; std::cout << msg << std::endl; auto now_ms = std::chrono::duration_cast( now_server.time_since_epoch() ).count(); uint64_t clientTimestamp = std::stoull(parts[1]); // Сначала прогоняем стандартную симуляцию "до текущего момента сервера" float dt_server = 0.0f; if (state_.lastUpdateServerTime.time_since_epoch().count() > 0) { dt_server = (clientTimestamp - now_ms) * 1000.f; } if (dt_server > 0) state_.simulate_physics(dt_server); std::chrono::steady_clock::time_point uptime_timepoint{ std::chrono::duration_cast(std::chrono::milliseconds(clientTimestamp)) }; state_.lastUpdateServerTime = uptime_timepoint; // Теперь обрабатываем специфику сообщения и лаг if (parts[0] == "ROT") { state_.discreteAngle = std::stoi(parts[2]); state_.discreteMag = std::stof(parts[3]); state_.apply_lag_compensation(now_server); } else if (parts[0] == "VEL") { state_.selectedVelocity = std::stoi(parts[2]); state_.apply_lag_compensation(now_server); } else if (parts[0] == "PING") { state_.handle_full_sync(parts); state_.apply_lag_compensation(now_server); } } public: explicit Session(tcp::socket&& socket, int id) : ws_(std::move(socket)), id_(id) { } void init() { state_.lastUpdateServerTime = std::chrono::steady_clock::now(); } std::string get_state_string() { return state_.get_state_string(id_); } void run() { { std::lock_guard lock(g_sessions_mutex); g_sessions.push_back(shared_from_this()); } ws_.async_accept([self = shared_from_this()](beast::error_code ec) { if (ec) return; std::cout << "Client " << self->id_ << " connected\n"; self->init(); self->send_message("ID:" + std::to_string(self->id_)); self->do_read(); }); } void tick_physics_global(std::chrono::steady_clock::time_point now) { float dt = 0.0f; // Если это самый первый тик, просто запоминаем время if (state_.lastUpdateServerTime.time_since_epoch().count() > 0) { dt = std::chrono::duration(now - state_.lastUpdateServerTime).count(); } if (dt > 0.0001f) { state_.simulate_physics(dt); state_.lastUpdateServerTime = now; // Обновляем время после симуляции } } void tick_physics(float dt_s) { state_.simulate_physics(dt_s); } void send_message(std::string msg) { auto ss = std::make_shared(std::move(msg)); ws_.async_write(net::buffer(*ss), [ss](beast::error_code, std::size_t) {}); } private: void do_read() { ws_.async_read(buffer_, [self = shared_from_this()](beast::error_code ec, std::size_t) { if (ec) { std::lock_guard lock(g_sessions_mutex); g_sessions.erase(std::remove_if(g_sessions.begin(), g_sessions.end(), [self](const std::shared_ptr& session) { return session.get() == self.get(); }), g_sessions.end()); return; } std::string msg = beast::buffers_to_string(self->buffer_.data()); self->process_message(msg); self->buffer_.consume(self->buffer_.size()); self->do_read(); }); } }; void update_world(net::steady_timer& timer, net::io_context& ioc) { auto now = std::chrono::steady_clock::now(); { std::lock_guard lock(g_sessions_mutex); // 1. Симуляция физики для всех for (auto& session : g_sessions) { session->tick_physics_global(now); } static auto last_broadcast = now; if (std::chrono::duration(now - last_broadcast).count() >= 1.0f) { last_broadcast = now; auto now_ms = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch() ).count(); // Собираем данные всех игроков в один пакет std::string snapshot = "WORLD_UPDATE|"; snapshot += std::to_string(now_ms) + "|"; snapshot += std::to_string(g_sessions.size()) + "|"; for (size_t i = 0; i < g_sessions.size(); ++i) { snapshot += g_sessions[i]->get_state_string(); if (i < g_sessions.size() - 1) snapshot += ";"; // Разделитель между игроками } // Рассылаем всем for (auto& session : g_sessions) { session->send_message(snapshot); } } } // ВАЖНО: Тикаем часто (50мс), а шлем данные редко (1000мс выше) timer.expires_after(std::chrono::milliseconds(50)); timer.async_wait([&](const boost::system::error_code& ec) { if (!ec) update_world(timer, ioc); }); } int main() { try { net::io_context ioc; tcp::acceptor acceptor{ ioc, {tcp::v4(), 8080} }; int next_id = 1000; std::cout << "Server started on port 8080...\n"; auto do_accept = [&](auto& self_fn) -> void { acceptor.async_accept([&, self_fn](beast::error_code ec, tcp::socket socket) { if (!ec) { std::make_shared(std::move(socket), next_id++)->run(); } self_fn(self_fn); }); }; net::steady_timer timer(ioc); update_world(timer, ioc); do_accept(do_accept); ioc.run(); } catch (std::exception const& e) { std::cerr << "Error: " << e.what() << std::endl; } return 0; }