From 92718aa772d94146adb40da07455c76c25af8de5 Mon Sep 17 00:00:00 2001 From: usermicrodevices <29286243+usermicrodevices@users.noreply.github.com> Date: Sat, 28 Mar 2026 01:17:05 +0300 Subject: [PATCH 1/3] enables server to receive binary msgs over WebSocket --- include/network/WebSocketSession.hpp | 9 +++++++- src/network/GameServer.cpp | 3 +++ src/network/WebSocketSession.cpp | 34 +++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/include/network/WebSocketSession.hpp b/include/network/WebSocketSession.hpp index 4bdf0e5..bc30e5d 100644 --- a/include/network/WebSocketSession.hpp +++ b/include/network/WebSocketSession.hpp @@ -53,6 +53,10 @@ class WebSocketSession : public IConnection, public std::enable_shared_from_this std::string GetRemoteAddress() const override; + using BinaryMessageHandler = std::function&)>; + void SetBinaryMessageHandler(BinaryMessageHandler handler); + void SetDefaultBinaryMessageHandler(BinaryMessageHandler handler); + private: WebSocketProtocol::WebSocketConnection::Pointer wsConn_; uint64_t sessionId_; @@ -77,7 +81,10 @@ class WebSocketSession : public IConnection, public std::enable_shared_from_this mutable std::mutex propertiesMutex_; std::map properties_; + BinaryMessageHandler binary_handler_; + BinaryMessageHandler default_binary_handler_; + // Internal helpers void OnMessage(const WebSocketProtocol::WebSocketMessage& msg); void OnClose(uint16_t code, const std::string& reason); -}; \ No newline at end of file +}; diff --git a/src/network/GameServer.cpp b/src/network/GameServer.cpp index 9844e9e..f058c95 100644 --- a/src/network/GameServer.cpp +++ b/src/network/GameServer.cpp @@ -102,6 +102,9 @@ void GameServer::DoAccept() { if (webSocketFactory_) { auto wsConn = webSocketFactory_(std::move(socket), sslContext_); auto session = std::make_shared(wsConn); + session->SetBinaryMessageHandler([world](uint16_t type, const std::vector& data) { + world->HandleBinaryMessage(type, data); // Your world's binary handler + }); ConnectionManager::GetInstance().Start(session); session->Start(); } else { diff --git a/src/network/WebSocketSession.cpp b/src/network/WebSocketSession.cpp index 94947f3..e66f5c9 100644 --- a/src/network/WebSocketSession.cpp +++ b/src/network/WebSocketSession.cpp @@ -135,17 +135,31 @@ std::string WebSocketSession::GetRemoteAddress() const { } void WebSocketSession::OnMessage(const WebSocketProtocol::WebSocketMessage& msg) { - if (messageHandler_) { - if (msg.opcode == WebSocketProtocol::OP_TEXT) { + if (msg.opcode == WebSocketProtocol::OP_TEXT) { + if (messageHandler_) { try { auto json = nlohmann::json::parse(msg.GetText()); messageHandler_(json); } catch (const std::exception& e) { Logger::Error("WebSocketSession: invalid JSON: {}", e.what()); } - } else if (msg.opcode == WebSocketProtocol::OP_BINARY) { - // Binary message handling – can be extended to parse binary protocol - Logger::Debug("WebSocket binary message received, size: {}", msg.data.size()); + } + } else if (msg.opcode == WebSocketProtocol::OP_BINARY) { + // Deserialize as BinaryMessage + try { + auto binaryMsg = BinaryProtocol::BinaryMessage::Deserialize( + msg.data.data(), msg.data.size()); + if (binary_handler_) { + binary_handler_(binaryMsg.header.message_type, binaryMsg.data); + } else if (default_binary_handler_) { + default_binary_handler_(binaryMsg.header.message_type, binaryMsg.data); + } else { + Logger::Warn("WebSocketSession {}: no binary handler for message type {}", + sessionId_, binaryMsg.header.message_type); + } + } catch (const std::exception& e) { + Logger::Error("WebSocketSession {}: failed to deserialize binary message: {}", + sessionId_, e.what()); } } } @@ -155,4 +169,12 @@ void WebSocketSession::OnClose(uint16_t code, const std::string& reason) { if (closeHandler_) { closeHandler_(); } -} \ No newline at end of file +} + +void WebSocketSession::SetBinaryMessageHandler(BinaryMessageHandler handler) { + binary_handler_ = std::move(handler); +} + +void WebSocketSession::SetDefaultBinaryMessageHandler(BinaryMessageHandler handler) { + default_binary_handler_ = std::move(handler); +} From e696f2beb45e7175d87d0a408774bc4624bb4ad8 Mon Sep 17 00:00:00 2001 From: usermicrodevices <29286243+usermicrodevices@users.noreply.github.com> Date: Sat, 28 Mar 2026 03:01:35 +0300 Subject: [PATCH 2/3] receive entity spawn/update/despawn messages --- include/game/GameLogic.hpp | 17 +++-- include/game/Player.hpp | 6 ++ include/network/GameSession.hpp | 4 ++ src/game/GameLogic.cpp | 120 ++++++++++++++++++++++++++++++++ src/game/Player.cpp | 9 ++- src/main.cpp | 8 +++ src/network/GameSession.cpp | 24 +++++++ 7 files changed, 180 insertions(+), 8 deletions(-) diff --git a/include/game/GameLogic.hpp b/include/game/GameLogic.hpp index 6c1077c..c12e74f 100644 --- a/include/game/GameLogic.hpp +++ b/include/game/GameLogic.hpp @@ -2,20 +2,17 @@ #include #include +#include #include #include -// #include "config/ConfigManager.hpp" -// #include "logging/Logger.hpp" -// #include "network/ConnectionManager.hpp" -// #include "database/DbManager.hpp" +#include "network/PredictionSystem.hpp" #include "game/LogicCore.hpp" #include "game/PlayerManager.hpp" #include "game/InventorySystem.hpp" #include "game/LootTableManager.hpp" -//#include "game/MobSystem.hpp" #include "game/SkillSystem.hpp" #include "game/QuestManager.hpp" #include "game/EntityManager.hpp" @@ -109,6 +106,13 @@ class GameLogic : public LogicCore void BroadcastToAllPlayersBinary(uint16_t messageType, const std::vector& data); void BroadcastToPlayers(const std::vector& sessionIds, const nlohmann::json& message); + // Broadcast entity spawn to nearby players + void BroadcastEntitySpawn(uint64_t entityId, EntityType type, const glm::vec3& position, + float yaw, const std::string& name); + void SendPositionCorrection(uint64_t sessionId, const glm::vec3& position, const glm::vec3& velocity); + void BroadcastPlayerState(uint64_t playerId, const ServerState& state); + void BroadcastEntityDespawn(uint64_t entityId, const glm::vec3& position); + private: GameLogic(); ~GameLogic(); @@ -125,6 +129,9 @@ class GameLogic : public LogicCore // Connection manager for broadcasting std::shared_ptr connectionManager_; + std::unordered_map playerPrediction_; + std::mutex predictionMutex_; + // Thread functions void GameLoop(); void SpawnerLoop(); diff --git a/include/game/Player.hpp b/include/game/Player.hpp index 8456d1d..aef9a5a 100644 --- a/include/game/Player.hpp +++ b/include/game/Player.hpp @@ -329,6 +329,10 @@ class Player : public GameEntity { nlohmann::json JsonGetInventory() const; nlohmann::json ToJson() const; + // =============== Movement state getters/setters =============== + bool IsOnGround() const { return onGround_; } + void SetOnGround(bool g) { onGround_ = g; } + private: uint64_t id_; std::string username_; @@ -362,6 +366,8 @@ class Player : public GameEntity { std::unordered_map completed_quests_; std::vector achievements_; + bool onGround_{true}; + // Active buffs/debuffs struct ActiveBuff { std::string buff_id; diff --git a/include/network/GameSession.hpp b/include/network/GameSession.hpp index 12e6f6e..165009c 100644 --- a/include/network/GameSession.hpp +++ b/include/network/GameSession.hpp @@ -315,6 +315,8 @@ class GameSession : public IConnection, public std::enable_shared_from_this handler); + private: // Core networking asio::ip::tcp::socket socket_; @@ -399,6 +401,8 @@ class GameSession : public IConnection, public std::enable_shared_from_this player_state_handler_; + // Private methods void StartHeartbeat(); void CheckHeartbeat(); diff --git a/src/game/GameLogic.cpp b/src/game/GameLogic.cpp index cb8ee08..6a03ec0 100644 --- a/src/game/GameLogic.cpp +++ b/src/game/GameLogic.cpp @@ -410,8 +410,21 @@ void GameLogic::HandleGoldTransaction(uint64_t sessionId, const nlohmann::json& } } +void GameLogic::SendPositionCorrection(uint64_t sessionId, const glm::vec3& position, const glm::vec3& velocity) { + BinaryProtocol::BinaryWriter writer; + writer.WriteVector3(position); + writer.WriteVector3(velocity); + writer.WriteUInt64(GetCurrentTimestamp()); + SendBinaryToSession(sessionId, BinaryProtocol::MESSAGE_TYPE_PLAYER_POSITION_CORRECTION, writer.GetBuffer()); +} + // =============== World Message Handlers =============== void GameLogic::RegisterWorldHandlers() { + RegisterBinaryHandler(BinaryProtocol::MESSAGE_TYPE_PLAYER_STATE, + [this](uint64_t sessionId, uint16_t /*messageType*/, const std::vector& data) { + HandlePlayerState(sessionId, data); + }); + RegisterHandler("world_chunk_request", [this](uint64_t sessionId, const nlohmann::json& data) { HandleWorldChunkRequest(sessionId, data); }); @@ -507,6 +520,10 @@ void GameLogic::OnPlayerConnected(uint64_t sessionId, uint64_t playerId) { void GameLogic::OnPlayerDisconnected(uint64_t sessionId) { // Capture player ID before base class removes the mapping uint64_t playerId = GetPlayerIdBySession(sessionId); + { + std::lock_guard lock(predictionMutex_); + playerPrediction_.erase(playerId); + } Logger::Info("GameLogic: Player {} disconnected from session {}", playerId, sessionId); FirePythonEvent("player_disconnected", { {"sessionId", sessionId}, @@ -592,6 +609,77 @@ void GameLogic::HandlePlayerPositionUpdate(uint64_t sessionId, const nlohmann::j } } +void GameLogic::HandlePlayerState(uint64_t sessionId, const std::vector& data) { + try { + // Deserialize client input + ClientInput input = ClientInput::Deserialize(data.data(), data.size()); + if (!input.IsValid()) { + Logger::Warn("Invalid client input from session {}", sessionId); + return; + } + + uint64_t playerId = GetPlayerIdBySession(sessionId); + if (playerId == 0) { + Logger::Warn("No player for session {}", sessionId); + return; + } + + // Store input in the player's prediction system + { + std::lock_guard lock(predictionMutex_); + playerPrediction_[playerId].StoreClientInput(input); + } + + // Get authoritative player state + auto player = GetPlayer(playerId); + if (!player) return; + + // Simulate movement using the prediction system + // (We'll compute the authoritative state from the last confirmed state plus unprocessed inputs) + PredictionSystem* pred = &playerPrediction_[playerId]; + auto currentTime = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); + + // Get the last confirmed state from prediction system (or from player) + ServerState authState; + authState.last_processed_input = 0; // TODO: track last processed input + authState.timestamp = currentTime; + authState.position = player->GetPosition(); + authState.velocity = player->GetVelocity(); // assuming Player has velocity + authState.rotation = player->GetRotation(); // assuming Player has rotation + authState.on_ground = player->IsOnGround(); // assuming Player has onGround + + // Simulate with unprocessed inputs + auto unprocessed = pred->GetUnprocessedInputs(authState.last_processed_input); + if (!unprocessed.empty()) { + float deltaTime = 1.0f / 30.0f; // or compute based on time difference + authState = pred->SimulateMovement(authState, unprocessed, deltaTime); + } + + // Update authoritative player state + player->SetPosition(authState.position); + player->SetVelocity(authState.velocity); + player->SetRotation(authState.rotation); + player->SetOnGround(authState.on_ground); + + // Broadcast authoritative state to nearby players (optional) + BroadcastPlayerState(playerId, authState); + + // Check if client needs a correction (compare with client's last reported position) + // We need to store the client's last reported position; for simplicity we can compare + // with the position that the client sent (which is not directly available here, but we + // could pass it in the input). For now, we'll just send a correction if the simulation + // moved significantly from the last confirmed state. + static float correctionThreshold = 0.5f; // 0.5 meters + if (glm::distance(authState.position, player->GetPosition()) > correctionThreshold) { + SendPositionCorrection(sessionId, authState.position, authState.velocity); + } + + } catch (const std::exception& e) { + Logger::Error("HandlePlayerState error: {}", e.what()); + } +} + void GameLogic::HandleNPCInteraction(uint64_t sessionId, const nlohmann::json& data) { try { uint64_t npcId = data.value("npcId", 0ULL); @@ -774,6 +862,19 @@ void GameLogic::HandleEntitySpawnRequest(uint64_t sessionId, const nlohmann::jso } // =============== Broadcasting =============== +void GameLogic::BroadcastEntitySpawn(uint64_t entityId, EntityType type, const glm::vec3& position, + float yaw, const std::string& name) { + BinaryProtocol::BinaryWriter writer; + writer.WriteUInt64(entityId); + writer.WriteUInt8(static_cast(type)); + writer.WriteString(name); + writer.WriteVector3(position); + writer.WriteFloat(yaw); + writer.WriteUInt64(GetCurrentTimestamp()); + + BroadcastToNearbyPlayers(position, BinaryProtocol::MESSAGE_TYPE_ENTITY_SPAWN, writer.GetBuffer(), 100.0f); +} + void GameLogic::BroadcastBinaryToNearbyPlayers(const glm::vec3& position, uint16_t messageType, const std::vector& data, float radius) { if (!connectionManager_) return; @@ -1137,3 +1238,22 @@ void GameLogic::BroadcastToPlayers(const std::vector& sessionIds, cons Logger::Error("Error broadcasting to specific players: {}", e.what()); } } + +void GameLogic::BroadcastPlayerState(uint64_t playerId, const ServerState& state) { + // Create a binary update message (ENTITY_UPDATE or PLAYER_STATE) for other players + BinaryProtocol::BinaryWriter writer; + writer.WriteUInt64(playerId); + writer.WriteVector3(state.position); + writer.WriteVector3(state.rotation); + writer.WriteVector3(state.velocity); + writer.WriteUInt64(state.timestamp); + BroadcastToNearbyPlayers(state.position, BinaryProtocol::MESSAGE_TYPE_ENTITY_UPDATE, writer.GetBuffer(), 100.0f); +} + +void GameLogic::BroadcastEntityDespawn(uint64_t entityId, const glm::vec3& position) { + BinaryProtocol::BinaryWriter writer; + writer.WriteUInt64(entityId); + writer.WriteUInt64(GetCurrentTimestamp()); + + BroadcastToNearbyPlayers(position, BinaryProtocol::MESSAGE_TYPE_ENTITY_DESPAWN, writer.GetBuffer(), 100.0f); +} diff --git a/src/game/Player.cpp b/src/game/Player.cpp index bb82125..7d20c43 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -231,7 +231,8 @@ race_(PlayerRace::HUMAN), status_(PlayerStatus::IDLE), inventory_system_(InventorySystem::GetInstance()), skill_system_(SkillSystem::GetInstance()), -quest_manager_(QuestManager::GetInstance()) +quest_manager_(QuestManager::GetInstance()), +onGround_(true) { ApplyRaceBonuses(); ApplyClassBonuses(); @@ -250,7 +251,8 @@ race_(PlayerRace::HUMAN), status_(PlayerStatus::IDLE), inventory_system_(InventorySystem::GetInstance()), skill_system_(SkillSystem::GetInstance()), -quest_manager_(QuestManager::GetInstance()) +quest_manager_(QuestManager::GetInstance()), +onGround_(true) { ApplyRaceBonuses(); ApplyClassBonuses(); @@ -270,7 +272,8 @@ race_(race), status_(PlayerStatus::IDLE), inventory_system_(InventorySystem::GetInstance()), skill_system_(SkillSystem::GetInstance()), -quest_manager_(QuestManager::GetInstance()) +quest_manager_(QuestManager::GetInstance()), +onGround_(true) { ApplyRaceBonuses(); ApplyClassBonuses(); diff --git a/src/main.cpp b/src/main.cpp index 1efc947..9895816 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -113,6 +113,7 @@ void WorkerMain(int workerId, const WorkerGroupConfig& groupConfig, ProcessPool* auto session = std::make_shared(std::move(socket), sslCtx); Logger::Debug("Worker {} created new game session {}", workerId, session->GetSessionId()); + // Message handler - simplified for demonstration session->SetMessageHandler([session, workerId, processPool](const nlohmann::json& msg) { try { @@ -140,6 +141,13 @@ void WorkerMain(int workerId, const WorkerGroupConfig& groupConfig, ProcessPool* session->SendError("Internal server error", 500); } }); + + // --- Binary message handler --- + session->SetDefaultBinaryMessageHandler([session, workerId](uint16_t type, + const std::vector& data) { + GameLogic::GetInstance().HandleBinaryMessage(session->GetSessionId(), type, data); + }); + session->SetCloseHandler([session, workerId]() { // Close handler Logger::Info("Worker {} session {} closing", workerId, session->GetSessionId()); GameLogic::GetInstance().OnPlayerDisconnected(session->GetSessionId()); diff --git a/src/network/GameSession.cpp b/src/network/GameSession.cpp index c9667f3..04c8592 100644 --- a/src/network/GameSession.cpp +++ b/src/network/GameSession.cpp @@ -101,6 +101,9 @@ void GameSession::StartProtocolNegotiation() { // Send protocol capabilities to client SendProtocolCapabilities(); + // Setup default binary handlers + SetupDefaultHandlers(); + // Start reading messages DoBinaryRead(); @@ -1608,3 +1611,24 @@ void GameSession::HandleFamiliarCommand(const nlohmann::json& data) { handler_it->second(BinaryProtocol::MESSAGE_TYPE_NPC_INTERACTION, writer.GetBuffer()); } } + +void GameSession::SetPlayerStateHandler(std::function handler) { + player_state_handler_ = std::move(handler); +} + +void GameSession::SetupDefaultHandlers() { + SetBinaryMessageHandler(BinaryProtocol::MESSAGE_TYPE_PLAYER_STATE, + [this](uint16_t type, const std::vector& data) { + (void)type; + try { + ClientInput input = ClientInput::Deserialize(data.data(), data.size()); + if (player_state_handler_) { + player_state_handler_(input); + } else { + Logger::Warn("Session {}: no player state handler registered", sessionId_); + } + } catch (const std::exception& e) { + Logger::Error("Session {}: failed to deserialize player state: {}", sessionId_, e.what()); + } + }); +} From 99e4c6a6e9f131a56b831b3a5713773f91fa79a9 Mon Sep 17 00:00:00 2001 From: usermicrodevices <29286243+usermicrodevices@users.noreply.github.com> Date: Sat, 28 Mar 2026 04:23:53 +0300 Subject: [PATCH 3/3] fix build bugs --- include/game/GameLogic.hpp | 6 ++- include/game/Player.hpp | 4 +- include/network/GameServer.hpp | 2 + include/network/GameSession.hpp | 1 + include/network/PredictionSystem.hpp | 60 ++++++++++++++-------------- include/network/WebSocketSession.hpp | 1 + src/game/GameLogic.cpp | 53 ++++++++++++------------ src/game/Player.cpp | 12 +++--- src/network/GameServer.cpp | 4 +- 9 files changed, 73 insertions(+), 70 deletions(-) diff --git a/include/game/GameLogic.hpp b/include/game/GameLogic.hpp index c12e74f..116a959 100644 --- a/include/game/GameLogic.hpp +++ b/include/game/GameLogic.hpp @@ -87,6 +87,7 @@ class GameLogic : public LogicCore void HandleCollisionCheck(uint64_t sessionId, const nlohmann::json& data); void HandleEntitySpawnRequest(uint64_t sessionId, const nlohmann::json& data); void HandleFamiliarCommand(uint64_t sessionId, const nlohmann::json& data); + void HandlePlayerState(uint64_t sessionId, const std::vector& data); // Message handling void HandleMessage(uint64_t sessionId, const nlohmann::json& message); @@ -96,9 +97,10 @@ class GameLogic : public LogicCore void OnPlayerDisconnected(uint64_t sessionId); // Broadcasting - void BroadcastBinaryToNearbyPlayers(const glm::vec3& position, uint16_t messageType, + void BroadcastToNearbyPlayers(const glm::vec3& position, uint16_t messageType, + const std::vector& data, float radius = 50.0f); + void BroadcastToNearbyOnlinePlayers(const glm::vec3& position, uint16_t messageType, const std::vector& data, float radius = 50.0f); - void BroadcastToNearbyPlayers(const glm::vec3& position, const nlohmann::json& message, float radius = 50.0f); void SyncNearbyEntitiesToPlayer(uint64_t sessionId, const glm::vec3& position); // Helper broadcast methods diff --git a/include/game/Player.hpp b/include/game/Player.hpp index aef9a5a..ed081a7 100644 --- a/include/game/Player.hpp +++ b/include/game/Player.hpp @@ -337,6 +337,8 @@ class Player : public GameEntity { uint64_t id_; std::string username_; + bool onGround_{true}; + //struct Position {float x, y, z;} position_; std::chrono::system_clock::time_point last_movement_; @@ -366,8 +368,6 @@ class Player : public GameEntity { std::unordered_map completed_quests_; std::vector achievements_; - bool onGround_{true}; - // Active buffs/debuffs struct ActiveBuff { std::string buff_id; diff --git a/include/network/GameServer.hpp b/include/network/GameServer.hpp index 7e7e917..9186ab1 100644 --- a/include/network/GameServer.hpp +++ b/include/network/GameServer.hpp @@ -17,6 +17,8 @@ #include "network/WebSocketProtocol.hpp" #include "network/WebSocketSession.hpp" +#include "game/GameLogic.hpp" + class GameServer { public: GameServer(const WorkerGroupConfig& groupConfig, const ConfigManager& globalConfig); diff --git a/include/network/GameSession.hpp b/include/network/GameSession.hpp index 165009c..5a6e600 100644 --- a/include/network/GameSession.hpp +++ b/include/network/GameSession.hpp @@ -433,5 +433,6 @@ class GameSession : public IConnection, public std::enable_shared_from_this Serialize() const; static ClientInput Deserialize(const uint8_t* data, size_t length); - + // Validity check bool IsValid() const { return input_id > 0; } }; @@ -30,11 +30,11 @@ struct ServerState { glm::vec3 velocity; glm::vec3 rotation; bool on_ground{true}; - + // Serialization std::vector Serialize() const; static ServerState Deserialize(const uint8_t* data, size_t length); - + // Interpolation static ServerState Interpolate(const ServerState& a, const ServerState& b, float t); }; @@ -42,34 +42,34 @@ struct ServerState { class PredictionSystem { public: PredictionSystem(); - + // Client-side methods void StoreClientInput(const ClientInput& input); ServerState PredictPosition(uint64_t current_time) const; - + // Server-side methods void StoreServerState(const ServerState& state); std::vector GetUnprocessedInputs(uint32_t last_processed) const; - + // Reconciliation struct ReconciliationResult { bool needs_correction{false}; ServerState corrected_state; std::vector processed_inputs; }; - + ReconciliationResult ReconcileWithServer(const ServerState& server_state); - + // State management ServerState GetLastConfirmedState() const { return last_confirmed_state_; } ServerState GetLatestPredictedState() const { return latest_predicted_state_; } - + // Input history const std::deque& GetInputHistory() const { return input_history_; } - + // Clear history void Clear(); - + // Statistics struct PredictionStats { uint32_t total_predictions{0}; @@ -77,38 +77,38 @@ class PredictionSystem { uint32_t corrections_received{0}; float average_correction_distance{0.0f}; uint64_t last_correction_time{0}; - + void Reset(); std::string ToString() const; }; - + const PredictionStats& GetStats() const { return stats_; } - + + // Helper methods + ServerState SimulateMovement(const ServerState& start_state, + const std::vector& inputs, + float delta_time) const; + + ServerState ApplyInput(const ServerState& state, const ClientInput& input, float delta_time) const; + private: mutable std::mutex mutex_; - + // Input history (client-side) std::deque input_history_; static constexpr size_t MAX_INPUT_HISTORY = 1000; - + // Server state history std::deque server_state_history_; static constexpr size_t MAX_STATE_HISTORY = 100; - + // Current states ServerState last_confirmed_state_; mutable ServerState latest_predicted_state_; - + // Statistics PredictionStats stats_; - - // Helper methods - ServerState SimulateMovement(const ServerState& start_state, - const std::vector& inputs, - float delta_time) const; - - ServerState ApplyInput(const ServerState& state, const ClientInput& input, float delta_time) const; - + // Physics constants static constexpr float GRAVITY = -9.81f; static constexpr float MAX_SPEED = 10.0f; @@ -121,18 +121,18 @@ class PredictionSystem { class InputBuffer { public: InputBuffer(size_t max_size = 1000); - + void AddInput(const ClientInput& input); std::vector GetOrderedInputs() const; ClientInput GetNextInput() const; bool HasInputs() const { return !inputs_.empty(); } void Clear(); size_t Size() const { return inputs_.size(); } - + private: mutable std::mutex mutex_; std::vector inputs_; size_t max_size_; - + void SortAndTrim(); }; diff --git a/include/network/WebSocketSession.hpp b/include/network/WebSocketSession.hpp index bc30e5d..f9f231c 100644 --- a/include/network/WebSocketSession.hpp +++ b/include/network/WebSocketSession.hpp @@ -7,6 +7,7 @@ #include "logging/Logger.hpp" #include "network/IConnection.hpp" +#include "network/BinaryProtocol.hpp" #include "network/WebSocketProtocol.hpp" class WebSocketSession : public IConnection, public std::enable_shared_from_this { diff --git a/src/game/GameLogic.cpp b/src/game/GameLogic.cpp index 6a03ec0..7846ba5 100644 --- a/src/game/GameLogic.cpp +++ b/src/game/GameLogic.cpp @@ -592,7 +592,7 @@ void GameLogic::HandlePlayerPositionUpdate(uint64_t sessionId, const nlohmann::j positionWriter.WriteVector3(glm::vec3(0, 0, 0)); positionWriter.WriteUInt64(GetCurrentTimestamp()); - BroadcastBinaryToNearbyPlayers(position, BinaryProtocol::MESSAGE_TYPE_PLAYER_POSITION, + BroadcastToNearbyPlayers(position, BinaryProtocol::MESSAGE_TYPE_PLAYER_POSITION, positionWriter.GetBuffer(), 100.0f); FirePythonEvent("player_move_3d", { @@ -862,55 +862,52 @@ void GameLogic::HandleEntitySpawnRequest(uint64_t sessionId, const nlohmann::jso } // =============== Broadcasting =============== -void GameLogic::BroadcastEntitySpawn(uint64_t entityId, EntityType type, const glm::vec3& position, - float yaw, const std::string& name) { - BinaryProtocol::BinaryWriter writer; - writer.WriteUInt64(entityId); - writer.WriteUInt8(static_cast(type)); - writer.WriteString(name); - writer.WriteVector3(position); - writer.WriteFloat(yaw); - writer.WriteUInt64(GetCurrentTimestamp()); - - BroadcastToNearbyPlayers(position, BinaryProtocol::MESSAGE_TYPE_ENTITY_SPAWN, writer.GetBuffer(), 100.0f); -} - -void GameLogic::BroadcastBinaryToNearbyPlayers(const glm::vec3& position, uint16_t messageType, - const std::vector& data, float radius) { +void GameLogic::BroadcastToNearbyPlayers(const glm::vec3& position, uint16_t messageType, + const std::vector& data, float radius) { if (!connectionManager_) return; auto& pm = PlayerManager::GetInstance(); - auto onlinePlayers = pm.GetOnlinePlayers(); // returns vector of shared_ptr - for (const auto& player : onlinePlayers) { - if (glm::distance(player->GetPosition(), position) <= radius) { - uint64_t sessionId = GetSessionIdByPlayer(player->GetId()); - if (sessionId != 0) { - auto session = connectionManager_->GetSession(sessionId); - if (session && session->IsConnected()) { - session->SendBinary(messageType, data); - } + auto nearby = pm.GetPlayersInRadius(position, radius); + for (auto& player : nearby) { + uint64_t sessionId = pm.GetSessionIdByPlayerId(player->GetId()); + if (sessionId != 0) { + auto session = connectionManager_->GetSession(sessionId); + if (session && session->IsConnected()) { + session->SendBinary(messageType, data); } } } } -void GameLogic::BroadcastToNearbyPlayers(const glm::vec3& position, const nlohmann::json& message, float radius) { +void GameLogic::BroadcastToNearbyOnlinePlayers(const glm::vec3& position, uint16_t messageType, + const std::vector& data, float radius) { if (!connectionManager_) return; auto& pm = PlayerManager::GetInstance(); auto onlinePlayers = pm.GetOnlinePlayers(); - std::string serialized = message.dump(); for (const auto& player : onlinePlayers) { if (glm::distance(player->GetPosition(), position) <= radius) { uint64_t sessionId = GetSessionIdByPlayer(player->GetId()); if (sessionId != 0) { auto session = connectionManager_->GetSession(sessionId); if (session && session->IsConnected()) { - session->SendRaw(serialized); + session->SendBinary(messageType, data); } } } } } +void GameLogic::BroadcastEntitySpawn(uint64_t entityId, EntityType type, const glm::vec3& position, + float yaw, const std::string& name) { + BinaryProtocol::BinaryWriter writer; + writer.WriteUInt64(entityId); + writer.WriteUInt8(static_cast(type)); + writer.WriteString(name); + writer.WriteVector3(position); + writer.WriteFloat(yaw); + writer.WriteUInt64(GetCurrentTimestamp()); + BroadcastToNearbyPlayers(position, BinaryProtocol::MESSAGE_TYPE_ENTITY_SPAWN, writer.GetBuffer(), 100.0f); +} + void GameLogic::SyncNearbyEntitiesToPlayer(uint64_t sessionId, const glm::vec3& position) { // Send a batch of entity data (positions, types, etc.) to the player auto nearbyEntities = EntityManager::GetInstance().GetEntitiesInRadius(position, 100.0f); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 7d20c43..544ffb0 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -225,14 +225,14 @@ Player::Player(uint64_t id, const std::string& username) : GameEntity(EntityType::PLAYER, glm::vec3(0.0f, 0.0f, 0.0f)), id_(id), username_(username), +onGround_(true), last_movement_(std::chrono::system_clock::now()), player_class_(PlayerClass::WARRIOR), race_(PlayerRace::HUMAN), status_(PlayerStatus::IDLE), inventory_system_(InventorySystem::GetInstance()), skill_system_(SkillSystem::GetInstance()), -quest_manager_(QuestManager::GetInstance()), -onGround_(true) +quest_manager_(QuestManager::GetInstance()) { ApplyRaceBonuses(); ApplyClassBonuses(); @@ -245,14 +245,14 @@ Player::Player(const glm::vec3& position) : GameEntity(EntityType::PLAYER, position), id_(0), username_(""), +onGround_(true), last_movement_(std::chrono::system_clock::now()), player_class_(PlayerClass::WARRIOR), race_(PlayerRace::HUMAN), status_(PlayerStatus::IDLE), inventory_system_(InventorySystem::GetInstance()), skill_system_(SkillSystem::GetInstance()), -quest_manager_(QuestManager::GetInstance()), -onGround_(true) +quest_manager_(QuestManager::GetInstance()) { ApplyRaceBonuses(); ApplyClassBonuses(); @@ -266,14 +266,14 @@ Player::Player(const glm::vec3& position, PlayerClass player_class, PlayerRace r : GameEntity(EntityType::PLAYER, position), id_(0), username_(""), +onGround_(true), last_movement_(std::chrono::system_clock::now()), player_class_(player_class), race_(race), status_(PlayerStatus::IDLE), inventory_system_(InventorySystem::GetInstance()), skill_system_(SkillSystem::GetInstance()), -quest_manager_(QuestManager::GetInstance()), -onGround_(true) +quest_manager_(QuestManager::GetInstance()) { ApplyRaceBonuses(); ApplyClassBonuses(); diff --git a/src/network/GameServer.cpp b/src/network/GameServer.cpp index f058c95..c940381 100644 --- a/src/network/GameServer.cpp +++ b/src/network/GameServer.cpp @@ -102,8 +102,8 @@ void GameServer::DoAccept() { if (webSocketFactory_) { auto wsConn = webSocketFactory_(std::move(socket), sslContext_); auto session = std::make_shared(wsConn); - session->SetBinaryMessageHandler([world](uint16_t type, const std::vector& data) { - world->HandleBinaryMessage(type, data); // Your world's binary handler + session->SetBinaryMessageHandler([session](uint16_t type, const std::vector& data) { + GameLogic::GetInstance().HandleBinaryMessage(session->GetSessionId(), type, data); }); ConnectionManager::GetInstance().Start(session); session->Start();