Skip to content

Commit 2efc80c

Browse files
authored
Merge pull request #626 from tintinhamans/arctic/go/fix/lobby-and-match-hotfix
fix(mesh): Keep matches connected and hosts in their lobby
2 parents 296db61 + cc132f0 commit 2efc80c

6 files changed

Lines changed: 102 additions & 16 deletions

File tree

‎GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/NetworkMesh.h‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ class NetworkMesh
218218
void SendACPacket(uint32_t userID, const void* pData, uint32_t dataLen);
219219

220220
void StartConnectionSignalling(const char* szMiddlewareID, int64_t remoteUserID, uint16_t preferredPort);
221+
222+
// A joiner gets its TURN credentials after its mesh exists; signalling waits for them
223+
void AwaitTurnCredentials();
224+
void SetTurnCredentials(const std::string& strUsername, const std::string& strToken);
225+
221226
void DisconnectUser(int64_t remoteUserID);
222227
void Disconnect();
223228

@@ -272,6 +277,15 @@ class NetworkMesh
272277
std::string m_strTurnTokenString;
273278
std::string m_strTurnServerList;
274279

280+
struct PendingSignalling
281+
{
282+
std::string strMiddlewareID;
283+
int64_t remoteUserID;
284+
uint16_t preferredPort;
285+
};
286+
bool m_bAwaitingTurnCredentials = false;
287+
std::vector<PendingSignalling> m_vecSignallingAwaitingTurn;
288+
275289
// k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_* for this lobby's connections.
276290
int m_iceEnable = 0;
277291
int m_iceImplementation = 2;

‎GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/PluginInterfaces.h‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include <atomic>
4+
35
enum class EConnectionState : uint8_t
46
{
57
NOT_CONNECTED,
@@ -55,7 +57,8 @@ enum class EAnticheatActionReason : int32_t
5557
class AnticheatPlugInterface
5658
{
5759
public:
58-
static bool g_bPendingExitLobby;
60+
// set from plugin threads
61+
static std::atomic<bool> g_bPendingExitLobby;
5962

6063
static void AC_NetworkMessageArrived(uint32_t goUserID, void* pData, uint32_t dataLen);
6164

@@ -207,7 +210,8 @@ extern HWND ApplicationHWnd;
207210
class AnticheatPlugInterface
208211
{
209212
public:
210-
static bool g_bPendingExitLobby;
213+
// set from plugin threads
214+
static std::atomic<bool> g_bPendingExitLobby;
211215

212216
static void AC_NetworkMessageArrived(uint32_t goUserID, void* pData, uint32_t dataLen)
213217
{

‎GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/NetworkMesh.cpp‎

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ void OnSteamNetConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t
112112
NGMP_OnlineServices_LobbyInterface* pJoinOrderLobby = NGMP_OnlineServicesManager::GetInterface<NGMP_OnlineServices_LobbyInterface>();
113113
const bool bWeJoinedLater = pJoinOrderLobby == nullptr || !pJoinOrderLobby->IsJoinOrderKnown() || pJoinOrderLobby->JoinedAfter(userID);
114114
const bool bPeerLeft = pJoinOrderLobby != nullptr && pJoinOrderLobby->IsJoinOrderKnown() && !pJoinOrderLobby->IsLobbyMember(userID);
115-
bool bShouldRetry = serviceConf.retry_signalling && ((!bWeJoinedLater && !bPeerLeft) || signallingAttemptsBeforeDisconnect < numSignallingAttempts);
115+
// a match can't leave its lobby, so keep repairing the link until the game drops the player
116+
const bool bInMatch = TheGameLogic != nullptr && TheGameLogic->isInInternetGame();
117+
bool bShouldRetry = serviceConf.retry_signalling && (bInMatch || (!bWeJoinedLater && !bPeerLeft) || signallingAttemptsBeforeDisconnect < numSignallingAttempts);
116118

117119
bool bWasError = pInfo->m_info.m_eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally || pInfo->m_info.m_eEndReason != k_ESteamNetConnectionEnd_App_Generic;
118120
plrConnection.SetDisconnected(bWasError, pMesh, bShouldRetry && bWasError);
@@ -173,7 +175,12 @@ void OnSteamNetConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t
173175
NetworkLog(ELogVerbosity::LOG_RELEASE, "[STEAM NETWORKING][DISCONNECT HANDLER] Not retrying, handling disconnect as failure...");
174176

175177
NGMP_OnlineServices_LobbyInterface* pLobbyInterface = NGMP_OnlineServicesManager::GetInterface<NGMP_OnlineServices_LobbyInterface>();
176-
if (pLobbyInterface != nullptr)
178+
if (pLobbyInterface != nullptr && pLobbyInterface->IsHost())
179+
{
180+
// the host keeps its lobby; the peer that can't connect is the one to go
181+
NetworkLog(ELogVerbosity::LOG_RELEASE, "[STEAM NETWORKING][DISCONNECT HANDLER] Not leaving, we host this lobby; dropping user %lld only", userID);
182+
}
183+
else if (pLobbyInterface != nullptr)
177184
{
178185
NetworkLog(ELogVerbosity::LOG_RELEASE, "[STEAM NETWORKING][DISCONNECT HANDLER] Performing local removal for user %lld from lobby due to failure to connect\n", userID);
179186

@@ -871,6 +878,14 @@ void NetworkMesh::StartConnectionSignalling(const char* szMiddlewareID, int64_t
871878
}
872879
else
873880
{
881+
// no relay without our TURN credentials
882+
if (m_bAwaitingTurnCredentials)
883+
{
884+
NetworkLog(ELogVerbosity::LOG_RELEASE, "[SIGNAL] Holding signalling with %lld until our TURN credentials arrive", remoteUserID);
885+
m_vecSignallingAwaitingTurn.push_back({ szMiddlewareID != nullptr ? szMiddlewareID : "", remoteUserID, preferredPort });
886+
return;
887+
}
888+
874889
// if we already have a connection to this use, drop it, having a single-direction connection will break signalling
875890
int previousAttempts = 0;
876891
auto it = m_mapConnections.find(remoteUserID);
@@ -992,7 +1007,44 @@ void NetworkMesh::StartConnectionSignalling(const char* szMiddlewareID, int64_t
9921007
m_mapConnections[remoteUserID].m_SignallingAttempts = previousAttempts + 1;
9931008
}
9941009
}
995-
1010+
1011+
}
1012+
1013+
void NetworkMesh::AwaitTurnCredentials()
1014+
{
1015+
std::lock_guard<std::recursive_mutex> lock(m_mapConnectionsMutex);
1016+
m_bAwaitingTurnCredentials = true;
1017+
}
1018+
1019+
void NetworkMesh::SetTurnCredentials(const std::string& strUsername, const std::string& strToken)
1020+
{
1021+
std::vector<PendingSignalling> vecPending;
1022+
{
1023+
std::lock_guard<std::recursive_mutex> lock(m_mapConnectionsMutex);
1024+
1025+
m_strTurnUsername = strUsername;
1026+
m_strTurnToken = strToken;
1027+
m_strTurnUsernameString = std::format("{},{}", m_strTurnUsername.c_str(), m_strTurnUsername.c_str());
1028+
m_strTurnTokenString = std::format("{},{}", m_strTurnToken.c_str(), m_strTurnToken.c_str());
1029+
1030+
// incoming connections use the listen socket's TURN settings
1031+
if (m_hListenSock != k_HSteamListenSocket_Invalid)
1032+
{
1033+
SteamNetworkingUtils()->SetConfigValue(k_ESteamNetworkingConfig_P2P_TURN_UserList, k_ESteamNetworkingConfig_ListenSocket,
1034+
(intptr_t)m_hListenSock, k_ESteamNetworkingConfig_String, m_strTurnUsernameString.c_str());
1035+
SteamNetworkingUtils()->SetConfigValue(k_ESteamNetworkingConfig_P2P_TURN_PassList, k_ESteamNetworkingConfig_ListenSocket,
1036+
(intptr_t)m_hListenSock, k_ESteamNetworkingConfig_String, m_strTurnTokenString.c_str());
1037+
}
1038+
1039+
m_bAwaitingTurnCredentials = false;
1040+
vecPending.swap(m_vecSignallingAwaitingTurn);
1041+
}
1042+
1043+
NetworkLog(ELogVerbosity::LOG_RELEASE, "[SIGNAL] Got TURN credentials, starting %d held signalling request(s)", (int)vecPending.size());
1044+
for (const PendingSignalling& pending : vecPending)
1045+
{
1046+
StartConnectionSignalling(pending.strMiddlewareID.c_str(), pending.remoteUserID, pending.preferredPort);
1047+
}
9961048
}
9971049

9981050

‎GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp‎

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,14 @@ void NGMP_OnlineServicesManager::GetAndParseServiceConfig(std::function<void(voi
8282
}
8383
else
8484
{
85-
// It's OK to fail, we'll just use the sensible defaults
86-
NetworkLog(ELogVerbosity::LOG_RELEASE, "[NGMP] Failed to get service config, using defaults. Status code: %d", statusCode);
87-
pMgr->m_ServiceConfig = ServiceConfig();
85+
// keep the last good config (defaults if there never was one); resetting would turn off retry_signalling
86+
NetworkLog(ELogVerbosity::LOG_RELEASE, "[NGMP] Failed to get service config, keeping the current one. Status code: %d", statusCode);
8887
}
8988

9089
}
9190
catch (...)
9291
{
93-
// It's OK to fail, we'll just use the sensible defaults
94-
NetworkLog(ELogVerbosity::LOG_RELEASE, "[NGMP] Failed to get service config, using defaults. Exception.");
95-
NGMP_OnlineServicesManager* pMgr = NGMP_OnlineServicesManager::GetInstance();
96-
if (pMgr != nullptr)
97-
{
98-
pMgr->m_ServiceConfig = ServiceConfig();
99-
}
92+
NetworkLog(ELogVerbosity::LOG_RELEASE, "[NGMP] Failed to parse service config, keeping the current one.");
10093
}
10194

10295
if (cbOnDone != nullptr)

‎GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,13 @@ void NGMP_OnlineServices_LobbyInterface::UpdateRoomDataCache(std::function<void(
891891
fnCallback(false);
892892
}
893893

894+
// the match runs on this lobby's mesh
895+
if (TheNGMPGame != nullptr && TheNGMPGame->isGameInProgress())
896+
{
897+
NetworkLog(ELogVerbosity::LOG_RELEASE, "[NGMP] Lobby lookup returned 404 during a match, keeping the match running");
898+
return;
899+
}
900+
894901
LeaveCurrentLobby();
895902
return;
896903
}
@@ -1191,6 +1198,9 @@ void NGMP_OnlineServices_LobbyInterface::JoinLobby(LobbyEntry lobbyInfo, std::st
11911198
m_pLobbyMesh = pNewMesh;
11921199
}
11931200

1201+
// TURN credentials arrive with the join response
1202+
m_pLobbyMesh->AwaitTurnCredentials();
1203+
11941204
// convert
11951205
NGMP_OnlineServicesManager::GetInstance()->GetHTTPManager()->SendPUTRequest(strURI.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, strPostData.c_str(), [=](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq)
11961206
{
@@ -1252,6 +1262,11 @@ void NGMP_OnlineServices_LobbyInterface::JoinLobby(LobbyEntry lobbyInfo, std::st
12521262

12531263
}
12541264

1265+
if (m_pLobbyMesh != nullptr)
1266+
{
1267+
m_pLobbyMesh->SetTurnCredentials(m_strTURNUsername, m_strTURNToken);
1268+
}
1269+
12551270
// for safety
12561271
if (TheNGMPGame != nullptr)
12571272
{
@@ -1548,6 +1563,11 @@ void NGMP_OnlineServices_LobbyInterface::CreateLobby(UnicodeString strLobbyName,
15481563
m_strTURNToken = resp.turn_token;
15491564
NetworkLog(ELogVerbosity::LOG_DEBUG, "Got TURN username: %s, token: %s", m_strTURNUsername.c_str(), m_strTURNToken.c_str());
15501565

1566+
// a mesh kept from a failed join has stale credentials
1567+
if (m_pLobbyMesh != nullptr)
1568+
{
1569+
m_pLobbyMesh->SetTurnCredentials(m_strTURNUsername, m_strTURNToken);
1570+
}
15511571

15521572
if (resp.result == ECreateLobbyResponseResult::SUCCEEDED)
15531573
{

‎GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/PluginInterfaces.cpp‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "../OnlineServices_Auth.h"
66
#include "../OnlineServices_LobbyInterface.h"
77

8-
bool AnticheatPlugInterface::g_bPendingExitLobby = false;
8+
std::atomic<bool> AnticheatPlugInterface::g_bPendingExitLobby = false;
99

1010
#if defined(GENERALS_ONLINE_USE_PLUGINS_INTERFACE)
1111

@@ -475,6 +475,9 @@ void AnticheatPlugInterface::BeginSession()
475475

476476
void AnticheatPlugInterface::EndSession()
477477
{
478+
// a stale exit request would eject us from the next lobby
479+
g_bPendingExitLobby = false;
480+
478481
#if defined(AC_ENABLED)
479482
if (IsPluginLoaded() && Functions.fnEndSession != nullptr)
480483
{

0 commit comments

Comments
 (0)