diff --git a/soh/soh/Network/Anchor/Anchor.cpp b/soh/soh/Network/Anchor/Anchor.cpp index 5852e62e0..755f40e59 100644 --- a/soh/soh/Network/Anchor/Anchor.cpp +++ b/soh/soh/Network/Anchor/Anchor.cpp @@ -40,6 +40,26 @@ void Anchor::OnDisconnected() { RegisterHooks(); } +void Anchor::ProcessOutgoingPackets() { + // Copy all queued packets while holding the lock, then send them after releasing + std::queue packetsToSend; + { + std::lock_guard lock(outgoingPacketQueueMutex); + packetsToSend.swap(outgoingPacketQueue); + } + + // Send packets without holding the lock + while (!packetsToSend.empty()) { + nlohmann::json payload = packetsToSend.front(); + packetsToSend.pop(); + + if (!payload.contains("quiet")) { + SPDLOG_DEBUG("[Anchor] Sending payload:\n{}", payload.dump()); + } + Network::SendJsonToRemote(payload); + } +} + void Anchor::SendJsonToRemote(nlohmann::json payload) { if (!isConnected) { return; @@ -47,9 +67,17 @@ void Anchor::SendJsonToRemote(nlohmann::json payload) { payload["clientId"] = ownClientId; if (!payload.contains("quiet")) { - SPDLOG_DEBUG("[Anchor] Sending payload:\n{}", payload.dump()); + SPDLOG_DEBUG("[Anchor] Queuing payload:\n{}", payload.dump()); } - Network::SendJsonToRemote(payload); + + if (payload["type"] == HANDSHAKE) { + Network::SendJsonToRemote(payload); + return; + } + + // Queue the packet to be sent on the network thread + std::lock_guard lock(outgoingPacketQueueMutex); + outgoingPacketQueue.push(payload); } void Anchor::OnIncomingJson(nlohmann::json payload) { @@ -87,11 +115,17 @@ void Anchor::OnIncomingJson(nlohmann::json payload) { } void Anchor::ProcessIncomingPacketQueue() { - std::lock_guard lock(incomingPacketQueueMutex); + // Copy all queued packets while holding the lock, then process them after releasing + std::queue packetsToProcess; + { + std::lock_guard lock(incomingPacketQueueMutex); + packetsToProcess.swap(incomingPacketQueue); + } - while (!incomingPacketQueue.empty()) { - nlohmann::json payload = incomingPacketQueue.front(); - incomingPacketQueue.pop(); + // Process packets without holding the lock + while (!packetsToProcess.empty()) { + nlohmann::json payload = packetsToProcess.front(); + packetsToProcess.pop(); std::string packetType = payload["type"].get(); diff --git a/soh/soh/Network/Anchor/Anchor.h b/soh/soh/Network/Anchor/Anchor.h index 9eb103230..cce5e397a 100644 --- a/soh/soh/Network/Anchor/Anchor.h +++ b/soh/soh/Network/Anchor/Anchor.h @@ -76,6 +76,8 @@ class Anchor : public Network { bool isProcessingIncomingPacket = false; std::queue incomingPacketQueue; std::mutex incomingPacketQueueMutex; + std::queue outgoingPacketQueue; + std::mutex outgoingPacketQueueMutex; nlohmann::json PrepClientState(); nlohmann::json PrepRoomState(); @@ -143,6 +145,7 @@ class Anchor : public Network { void OnIncomingJson(nlohmann::json payload); void OnConnected(); void OnDisconnected(); + void ProcessOutgoingPackets(); void DrawMenu(); void ProcessIncomingPacketQueue(); void SendJsonToRemote(nlohmann::json packet); diff --git a/soh/soh/Network/Anchor/DummyPlayer.cpp b/soh/soh/Network/Anchor/DummyPlayer.cpp index 8e5df3c8e..9192c3c02 100644 --- a/soh/soh/Network/Anchor/DummyPlayer.cpp +++ b/soh/soh/Network/Anchor/DummyPlayer.cpp @@ -158,13 +158,13 @@ void DummyPlayer_Update(Actor* actor, PlayState* play) { player->actor.world.pos.y += diff.y * player->actor.scale.y; } - if (player->modelGroup != Player_ActionToModelGroup(player, player->itemAction)) { + if (player->modelGroup != client.modelGroup) { // Hack to account for usage of gSaveContext s32 originalAge = gSaveContext.linkAge; gSaveContext.linkAge = client.linkAge; u8 originalButtonItem0 = gSaveContext.equips.buttonItems[0]; gSaveContext.equips.buttonItems[0] = client.buttonItem0; - Player_SetModelGroup(player, Player_ActionToModelGroup(player, player->itemAction)); + Player_SetModelGroup(player, client.modelGroup); gSaveContext.linkAge = originalAge; gSaveContext.equips.buttonItems[0] = originalButtonItem0; } diff --git a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp index c05de0a23..d37dff64a 100644 --- a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp +++ b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp @@ -58,7 +58,7 @@ void Anchor::SendPacket_PlayerUpdate() { payload["currentShield"] = player->currentShield; payload["currentTunic"] = player->currentTunic; payload["stateFlags1"] = player->stateFlags1; - payload["stateFlags2"] = player->stateFlags2; + payload["stateFlags2"] = player->stateFlags2 & ~PLAYER_STATE2_DISABLE_DRAW; payload["buttonItem0"] = gSaveContext.equips.buttonItems[0]; payload["itemAction"] = player->itemAction; payload["heldItemAction"] = player->heldItemAction; diff --git a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp index 74bc44699..fb1c70dc9 100644 --- a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp +++ b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp @@ -4,6 +4,8 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/OTRGlobals.h" +static bool isResultOfHandling = false; + /** * SET_CHECK_STATUS * @@ -11,7 +13,7 @@ */ void Anchor::SendPacket_SetCheckStatus(RandomizerCheck rc) { - if (!IsSaveLoaded() || isProcessingIncomingPacket || !roomState.syncItemsAndFlags) { + if (!IsSaveLoaded() || isResultOfHandling) { return; } @@ -40,12 +42,16 @@ void Anchor::HandlePacket_SetCheckStatus(nlohmann::json payload) { RandomizerCheckStatus status = payload["status"].get(); bool skipped = payload["skipped"].get(); + isResultOfHandling = true; + if (randoContext->GetItemLocation(rc)->GetCheckStatus() != status) { randoContext->GetItemLocation(rc)->SetCheckStatus(status); } if (randoContext->GetItemLocation(rc)->GetIsSkipped() != skipped) { randoContext->GetItemLocation(rc)->SetIsSkipped(skipped); } + CheckTracker::RecalculateAllAreaTotals(); CheckTracker::RecalculateAvailableChecks(); + isResultOfHandling = false; } diff --git a/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp b/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp index 9c7c3309a..bacb0806e 100644 --- a/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp +++ b/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp @@ -291,6 +291,7 @@ void Anchor::HandlePacket_UpdateTeamState(nlohmann::json payload) { } if (payload.contains("queue")) { + std::lock_guard lock(incomingPacketQueueMutex); for (auto& item : payload["queue"]) { nlohmann::json itemPayload = nlohmann::json::parse(item.get()); incomingPacketQueue.push(itemPayload); diff --git a/soh/soh/Network/Network.cpp b/soh/soh/Network/Network.cpp index d9edb5550..6eae1f3f1 100644 --- a/soh/soh/Network/Network.cpp +++ b/soh/soh/Network/Network.cpp @@ -46,6 +46,9 @@ void Network::OnConnected() { void Network::OnDisconnected() { } +void Network::ProcessOutgoingPackets() { +} + void Network::SendDataToRemote(const char* payload) { #ifdef ENABLE_REMOTE_CONTROL SPDLOG_DEBUG("[Network] Sending data: {}", payload); @@ -68,6 +71,7 @@ void Network::ReceiveFromServer() { if (networkSocket) { isConnected = true; + receivedData.clear(); SPDLOG_INFO("[Network] Connection to server established!"); OnConnected(); @@ -90,7 +94,11 @@ void Network::ReceiveFromServer() { break; } + // Always process outgoing packets + ProcessOutgoingPackets(); + if (socketsReady == 0) { + // No incoming data continue; } @@ -119,9 +127,15 @@ void Network::ReceiveFromServer() { } } + if (socketSet) { + SDLNet_FreeSocketSet(socketSet); + } + if (isConnected) { SDLNet_TCP_Close(networkSocket); + networkSocket = nullptr; isConnected = false; + receivedData.clear(); OnDisconnected(); SPDLOG_INFO("[Network] Ending receiving thread..."); } diff --git a/soh/soh/Network/Network.h b/soh/soh/Network/Network.h index a04ece71e..f1ec42c02 100644 --- a/soh/soh/Network/Network.h +++ b/soh/soh/Network/Network.h @@ -44,6 +44,7 @@ class Network { virtual void OnIncomingJson(nlohmann::json payload); virtual void OnConnected(); virtual void OnDisconnected(); + virtual void ProcessOutgoingPackets(); void SendDataToRemote(const char* payload); virtual void SendJsonToRemote(nlohmann::json packet); };