ValveSoftware / GameNetworkingSockets

Reliable & unreliable messages over UDP. Robust message fragmentation & reassembly. P2P networking / NAT traversal. Encryption.
BSD 3-Clause "New" or "Revised" License
8.26k stars 620 forks source link

Any example code implementation for Relay Fallback (TURN) on client? #189

Closed LeventSevgili closed 3 years ago

LeventSevgili commented 3 years ago

Hey,

We already setup a TURN server for test, just wondering if it is straightforward to configure the GNS for Relay Fallback on client? Or any example code? Any help on that would be appreciated. Thank you very much!

[UPDATE] I think the place is already reserved at CConnectionTransportP2PICE class as:

// Get the TURN server list
if ( P2P_Transport_ICE_Enable & k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Relay )
{
// FIXME
//cfg.m_nCandidateTypes = m_nAllowedCandidateTypes;
}

It seems it requires a setting variable at struct ConnectionConfig something like k_ESteamNetworkingConfig_P2P_TURN_ServerList = 108 and it needs to be passed to the ICE part within void CConnectionTransportP2PICE::Init() method at above? Well of course i may be missing something :)

LeventSevgili commented 3 years ago

Okay i implemented as below and it worked quite nicely. We tested a connection from a client to a game server which is behind a VPN, without TURN, it was not working. Now with TURN, it is connected and stable.

Usage:

// comma seperated setting lists
const char *stunList = "stun.123.456.45.456:3478";
const char *turnList = "turn:123.45.45:3478";
const char *userList = "levent";
const char *passList = "pass";

SteamNetworkingUtils()->SetGlobalConfigValueString(k_ESteamNetworkingConfig_P2P_STUN_ServerList, stunList); 
SteamNetworkingUtils()->SetGlobalConfigValueString(k_ESteamNetworkingConfig_P2P_TURN_ServerList, turnList);
SteamNetworkingUtils()->SetGlobalConfigValueString(k_ESteamNetworkingConfig_P2P_TURN_UserList, userList);
SteamNetworkingUtils()->SetGlobalConfigValueString(k_ESteamNetworkingConfig_P2P_TURN_PassList, passList);

Implementation: File: steamnetworkingtypes.h Added 3 more configuration for P2P

k_ESteamNetworkingConfig_P2P_TURN_ServerList = 107,
k_ESteamNetworkingConfig_P2P_TURN_UserList = 108,
k_ESteamNetworkingConfig_P2P_TURN_PassList = 109,

File: ice_session.cpp Added break to the switch statement.

bool CICESession::BInitializeOnSocketThread( const ICESessionConfig &cfg )
{
    .
      switch( pTurn->m_protocolType ) {
      case k_EProtocolTypeUDP:
      case k_EProtocolTypeTCP:
      case k_EProtocolTypeSSLTCP:
      case k_EProtocolTypeTLS:
          break; // Added this break
      default:
      m_pDelegate->Log( IICESessionDelegate::k_ELogPriorityError, "Invalid Turn server protocol type '%d'\n", (int) pTurn->m_protocolType );
      return false;
      }
    .
}

File: steamnetworkingsockets_internal.h Added 3 config value for TURN.

#ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE
        .
        .
      ConfigValue<std::string> m_P2P_TURN_ServerList;
      ConfigValue<std::string> m_P2P_TURN_UserList;
      ConfigValue<std::string> m_P2P_TURN_PassList;
#endif

File: csteamnetworkingsockets.cpp Add default values for new TURN configs

DEFINE_CONNECTON_DEFAULT_CONFIGVAL( std::string, P2P_TURN_ServerList, "" );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( std::string, P2P_TURN_UserList, "" );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( std::string, P2P_TURN_PassList, "" );

File: steamnetworkingsockets_p2p_ice.cpp Updated the Init method of CConnectionTransportP2PICE

void CConnectionTransportP2PICE::Init()
{
      .
      // Get the TURN server list
      std_vector<std::string> vecTurnServers;
      std_vector<std::string> vecTurnUsers;
      std_vector<std::string> vecTurnPasses;
      std_vector<ICESessionConfig::TurnServer*> vecTurnServersPsz;
      if ( P2P_Transport_ICE_Enable & k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Relay )
      {
                    m_nAllowedCandidateTypes |= k_EICECandidate_Any_HostPublic | k_EICECandidate_Any_Reflexive;

              {
                  CUtlVectorAutoPurge<char*> tempTurnServers;
                  V_AllocAndSplitString(m_connection.m_connectionConfig.m_P2P_TURN_ServerList.Get().c_str(), ",", tempTurnServers);
                  for (const char* pszAddress : tempTurnServers)
                  {
                      std::string server;

                      // Add prefix, unless they already supplied it
                      if (V_strnicmp(pszAddress, "turn:", 5) != 0)
                          server = "turn:";
                      server.append(pszAddress);

                      vecTurnServers.push_back(std::move(server));
                  }

                  // populate usernames
                  CUtlVectorAutoPurge<char*> tempTurnUsers;
                  V_AllocAndSplitString(m_connection.m_connectionConfig.m_P2P_TURN_UserList.Get().c_str(), ",", tempTurnUsers);
                  for (const char* user : tempTurnServers)
                  {
                      std::string server;               
                      server.append(user);
                      vecTurnUsers.push_back(std::move(server));
                  }

                  // populate passwords
                  CUtlVectorAutoPurge<char*> tempTurnPasses;
                  V_AllocAndSplitString(m_connection.m_connectionConfig.m_P2P_TURN_PassList.Get().c_str(), ",", tempTurnPasses);
                  for (const char* pass : tempTurnPasses)
                  {
                      std::string server;
                      server.append(pass);
                      vecTurnPasses.push_back(std::move(server));
                  }
            }
            if (vecTurnServers.empty())
            SpewWarningGroup(LogLevel_P2PRendezvous(), "[%s] Reflexive candidates enabled by P2P_Transport_ICE_Enable, but P2P_STUN_ServerList is empty\n", ConnectionDescription());
            else
            SpewVerboseGroup(LogLevel_P2PRendezvous(), "[%s] Using STUN server list: %s\n", ConnectionDescription(), m_connection.m_connectionConfig.m_P2P_TURN_ServerList.Get().c_str());

             Assert(vecTurnServers.size() == vecTurnUsers.size() == vecTurnPasses.size());
        }
        else
        {
              SpewVerboseGroup(LogLevel_P2PRendezvous(), "[%s] Not using STUN servers as per P2P_Transport_ICE_Enable\n", ConnectionDescription());
        }

        // Populate vecTurnServersPsz
        for (int i = 0; i < vecTurnServers.size(); i++)
        {
            ICESessionConfig::TurnServer* turn = new ICESessionConfig::TurnServer();
            turn->m_pszHost = vecTurnServers[i].c_str();
            turn->m_pszPwd = vecTurnUsers[i].c_str();
            turn->m_pszUsername = vecTurnPasses[i].c_str();
            vecTurnServersPsz.push_back(turn);
        }

        // Set the Turn config
        if (vecTurnServers.size() > 0)
        {
            cfg.m_nTurnServers = len(vecTurnServers);
            cfg.m_pTurnServers = *vecTurnServersPsz.data();
        }
.
}

It is a pretty straightforward implementation. Working for me. Hope it helps someone :)

zpostfacto commented 3 years ago

That looks like the right approach. Want to submit a PR?

LeventSevgili commented 3 years ago

Gladly! I'll submit shortly. Thanks.

LeventSevgili commented 3 years ago

PR #192 Thanks!

zpostfacto commented 3 years ago

I pulled #192