From 67bfe93818e95b23d675870262d646988e470d04 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Sat, 14 Oct 2023 13:52:10 -0400 Subject: [PATCH 1/3] netplay: Handle NETplayerClientDisconnect on NETsend in a delayed action --- lib/netplay/netplay.cpp | 16 ++++++++++++++-- lib/netplay/netplay.h | 1 + lib/netplay/nettypes.cpp | 3 +++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/netplay/netplay.cpp b/lib/netplay/netplay.cpp index 3f8adc54f69..d9092d4f50a 100644 --- a/lib/netplay/netplay.cpp +++ b/lib/netplay/netplay.cpp @@ -1709,6 +1709,16 @@ size_t NETgetStatistic(NetStatisticType type, bool sent, bool isTotal) return nStatsLastSec.*statsType.*statisticType - nStatsSecondLastSec.*statsType.*statisticType; } +static std::list> netSendDelayedActions; + +void NETsendProcessDelayedActions() +{ + for (auto action : netSendDelayedActions) + { + action(); + } + netSendDelayedActions.clear(); +} // //////////////////////////////////////////////////////////////////////// // Send a message to a player, option to guarantee message @@ -1774,8 +1784,10 @@ bool NETsend(NETQUEUE queue, NetMessage const *message) debug(LOG_ERROR, "Failed to send message (type: %" PRIu8 ", rawLen: %zu, compressedRawLen: %zu) to %" PRIu8 ": %s", message->type, message->rawLen(), compressedRawLen, player, strSockError(getSockErr())); if (!isTmpQueue) { - NETlogEntry("client disconnect?", SYNC_FLAG, player); - NETplayerClientDisconnect(player); + netSendDelayedActions.push_back([player]() { + NETlogEntry("client disconnect?", SYNC_FLAG, player); + NETplayerClientDisconnect(player); + }); } } } diff --git a/lib/netplay/netplay.h b/lib/netplay/netplay.h index 07dce367019..1353c1287b9 100644 --- a/lib/netplay/netplay.h +++ b/lib/netplay/netplay.h @@ -371,6 +371,7 @@ extern bool netGameserverPortOverride; // = false; (for cli override) // functions available to you. int NETinit(bool bFirstCall); WZ_DECL_NONNULL(2) bool NETsend(NETQUEUE queue, NetMessage const *message); ///< send to player, or broadcast if player == NET_ALL_PLAYERS. +void NETsendProcessDelayedActions(); WZ_DECL_NONNULL(1, 2) bool NETrecvNet(NETQUEUE *queue, uint8_t *type); ///< recv a message from the net queues if possible. WZ_DECL_NONNULL(1, 2) bool NETrecvGame(NETQUEUE *queue, uint8_t *type); ///< recv a message from the game queues which is sceduled to execute by time, if possible. void NETflush(); ///< Flushes any data stuck in compression buffers. diff --git a/lib/netplay/nettypes.cpp b/lib/netplay/nettypes.cpp index 2518be5394d..5b7d7543878 100644 --- a/lib/netplay/nettypes.cpp +++ b/lib/netplay/nettypes.cpp @@ -727,6 +727,9 @@ bool NETend() // We have ended the serialisation, so mark the direction invalid NETsetPacketDir(PACKET_INVALID); + // Process any delayed actions from the NETsend call + NETsendProcessDelayedActions(); + if (queueInfo.queueType == QUEUE_GAME_FORCED) // If true, we must be the host, inserting a GAME_PLAYER_LEFT into the other player's game queue. Since they left, they're not around to complain about us messing with their queue, which would normally cause a desynch. { // Almost duplicate code from NETflushGameQueues() in here. From 6c63b9cd69cd95157a8a6c8da00185ed8503e3c5 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Sat, 14 Oct 2023 13:53:26 -0400 Subject: [PATCH 2/3] netplay: Avoid some message spam / redundant kickPlayer calls --- lib/netplay/netplay.cpp | 18 ++++++++++++++---- lib/netplay/netplay.h | 2 ++ src/multiplay.cpp | 7 +++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/netplay/netplay.cpp b/lib/netplay/netplay.cpp index d9092d4f50a..f14fb719a99 100644 --- a/lib/netplay/netplay.cpp +++ b/lib/netplay/netplay.cpp @@ -864,6 +864,13 @@ static void NET_DestroyPlayer(unsigned int index, bool suppressActivityUpdates = } } +bool NETplayerHasConnection(uint32_t index) +{ + ASSERT_HOST_ONLY(return false); + ASSERT_OR_RETURN(false, index < MAX_CONNECTED_PLAYERS, "Invalid index: %" PRIu32, index); + return connected_bsocket[index] != nullptr; +} + /** * @note Connection dropped. Handle it gracefully. * \param index @@ -2174,10 +2181,13 @@ static inline bool NETFilterMessageWhileSwappingPlayer(uint8_t sender, uint8_t t { // this client did not acknowledge the player index change before the timeout - kick them char msg[256] = {'\0'}; - ssprintf(msg, "Auto-kicking player %u, did not ack player index change within required timeframe.", (unsigned int)sender); - sendInGameSystemMessage(msg); - debug(LOG_INFO, "Client (player: %u) failed to ack player index swap (ignoring message type: %" PRIu8 ")", sender, type); - kickPlayer(sender, _("Client failed to ack player index swap"), ERROR_INVALID, false); + if (NETplayerHasConnection(sender) || NetPlay.players[sender].allocated) + { + ssprintf(msg, "Auto-kicking player %u, did not ack player index change within required timeframe.", (unsigned int)sender); + sendInGameSystemMessage(msg); + debug(LOG_INFO, "Client (player: %u) failed to ack player index swap (ignoring message type: %" PRIu8 ")", sender, type); + kickPlayer(sender, _("Client failed to ack player index swap"), ERROR_INVALID, false); + } return true; // filter original message, of course } diff --git a/lib/netplay/netplay.h b/lib/netplay/netplay.h index 1353c1287b9..1c029eb3b01 100644 --- a/lib/netplay/netplay.h +++ b/lib/netplay/netplay.h @@ -392,6 +392,8 @@ size_t NETgetStatistic(NetStatisticType type, bool sent, bool isTotal = false); void NETplayerKicked(UDWORD index); // Cleanup after player has been kicked +bool NETplayerHasConnection(uint32_t index); + bool NETcanOpenNewSpectatorSlot(); bool NETopenNewSpectatorSlot(); bool NETmovePlayerToSpectatorOnlySlot(uint32_t playerIdx, bool hostOverride = false); diff --git a/src/multiplay.cpp b/src/multiplay.cpp index 2f550d9e251..3d636072b70 100644 --- a/src/multiplay.cpp +++ b/src/multiplay.cpp @@ -1394,8 +1394,11 @@ void HandleBadParam(const char *msg, const int from, const int actual) NETlogEntry(buf, SYNC_FLAG, actual); if (NetPlay.isHost) { - ssprintf(buf, _("Auto kicking player %s, invalid command received."), NetPlay.players[actual].name); - sendInGameSystemMessage(buf); + if (NETplayerHasConnection(actual)) + { + ssprintf(buf, _("Auto kicking player %s, invalid command received."), NetPlay.players[actual].name); + sendInGameSystemMessage(buf); + } kickPlayer(actual, buf, KICK_TYPE, false); } } From 93deb3bfb0712a82b7432c48b274428b73bd911e Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Sat, 14 Oct 2023 13:54:09 -0400 Subject: [PATCH 3/3] quickchat: Do not locally output sent internal messages --- src/hci/quickchat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hci/quickchat.cpp b/src/hci/quickchat.cpp index 3af4f1c8f2f..3037c116e65 100644 --- a/src/hci/quickchat.cpp +++ b/src/hci/quickchat.cpp @@ -3001,7 +3001,7 @@ void sendQuickChat(WzQuickChatMessage message, uint32_t fromPlayer, WzQuickChatT NETend(); } - if (fromPlayer == selectedPlayer && (!recipients.empty() || !isInGame)) + if (fromPlayer == selectedPlayer && (!recipients.empty() || !isInGame) && !internalMessage) { if (isInGame) {