From 140498490eb3838a2d04ac08ff824b5eb951dfac Mon Sep 17 00:00:00 2001 From: Fletcher Dunn Date: Mon, 2 Sep 2019 14:34:11 -0700 Subject: [PATCH] API to atomically set initial options. Added a mechanism to set initial options when creating a listen socket or connection. --- examples/example_chat.cpp | 4 +- include/steam/isteamnetworkingsockets.h | 39 +++++++-- include/steam/isteamnetworkingutils.h | 15 +++- include/steam/steamnetworkingsockets_flat.h | 12 +-- include/steam/steamnetworkingtypes.h | 45 +++++++++- .../clientlib/csteamnetworkingsockets.cpp | 39 ++------- .../clientlib/csteamnetworkingsockets.h | 4 +- .../steamnetworkingsockets_connections.cpp | 85 ++++++++++++++++++- .../steamnetworkingsockets_connections.h | 6 +- .../clientlib/steamnetworkingsockets_flat.cpp | 24 +++--- .../clientlib/steamnetworkingsockets_udp.cpp | 14 +-- .../clientlib/steamnetworkingsockets_udp.h | 4 +- tests/test_connection.cpp | 4 +- 13 files changed, 216 insertions(+), 79 deletions(-) diff --git a/examples/example_chat.cpp b/examples/example_chat.cpp index 93e9e92..cc4f83a 100644 --- a/examples/example_chat.cpp +++ b/examples/example_chat.cpp @@ -240,7 +240,7 @@ public: SteamNetworkingIPAddr serverLocalAddr; serverLocalAddr.Clear(); serverLocalAddr.m_port = nPort; - m_hListenSock = m_pInterface->CreateListenSocketIP( serverLocalAddr ); + m_hListenSock = m_pInterface->CreateListenSocketIP( serverLocalAddr, 0, nullptr ); if ( m_hListenSock == k_HSteamListenSocket_Invalid ) FatalError( "Failed to listen on port %d", nPort ); Printf( "Server listening on port %d\n", nPort ); @@ -533,7 +533,7 @@ public: char szAddr[ SteamNetworkingIPAddr::k_cchMaxString ]; serverAddr.ToString( szAddr, sizeof(szAddr), true ); Printf( "Connecting to chat server at %s", szAddr ); - m_hConnection = m_pInterface->ConnectByIPAddress( serverAddr ); + m_hConnection = m_pInterface->ConnectByIPAddress( serverAddr, 0, nullptr ); if ( m_hConnection == k_HSteamNetConnection_Invalid ) FatalError( "Failed to create connection" ); diff --git a/include/steam/isteamnetworkingsockets.h b/include/steam/isteamnetworkingsockets.h index 9f04efe..b87dc7c 100644 --- a/include/steam/isteamnetworkingsockets.h +++ b/include/steam/isteamnetworkingsockets.h @@ -48,15 +48,20 @@ public: /// You must select a specific local port to listen on and set it /// the port field of the local address. /// - /// Usually you wil set the IP portion of the address to zero, (SteamNetworkingIPAddr::Clear()). - /// This means that you will not bind to any particular local interface. In addition, - /// if possible the socket will be bound in "dual stack" mode, which means that it can - /// accept both IPv4 and IPv6 clients. If you wish to bind a particular interface, then - /// set the local address to the appropriate IPv4 or IPv6 IP. + /// Usually you will set the IP portion of the address to zero (SteamNetworkingIPAddr::Clear()). + /// This means that you will not bind to any particular local interface (i.e. the same + /// as INADDR_ANY in plain socket code). Furthermore, if possible the socket will be bound + /// in "dual stack" mode, which means that it can accept both IPv4 and IPv6 client connections. + /// If you really do wish to bind a particular interface, then set the local address to the + /// appropriate IPv4 or IPv6 IP. + /// + /// If you need to set any initial config options, pass them here. See + /// SteamNetworkingConfigValue_t for more about why this is preferable to + /// setting the options "immediately" after creation. /// /// When a client attempts to connect, a SteamNetConnectionStatusChangedCallback_t /// will be posted. The connection will be in the connecting state. - virtual HSteamListenSocket CreateListenSocketIP( const SteamNetworkingIPAddr &localAddress ) = 0; + virtual HSteamListenSocket CreateListenSocketIP( const SteamNetworkingIPAddr &localAddress, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) = 0; /// Creates a connection and begins talking to a "server" over UDP at the /// given IPv4 or IPv6 address. The remote host must be listening with a @@ -76,7 +81,11 @@ public: /// distributed through some other out-of-band mechanism), you don't have any /// way of knowing who is actually on the other end, and thus are vulnerable to /// man-in-the-middle attacks. - virtual HSteamNetConnection ConnectByIPAddress( const SteamNetworkingIPAddr &address ) = 0; + /// + /// If you need to set any initial config options, pass them here. See + /// SteamNetworkingConfigValue_t for more about why this is preferable to + /// setting the options "immediately" after creation. + virtual HSteamNetConnection ConnectByIPAddress( const SteamNetworkingIPAddr &address, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) = 0; #ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR /// P2P stfuf @@ -115,6 +124,12 @@ public: /// Returns k_EResultInvalidState if the connection is not in the appropriate state. /// (Remember that the connection state could change in between the time that the /// notification being posted to the queue and when it is received by the application.) + /// + /// A note about connection configuration options. If you need to set any configuration + /// options that are common to all connections accepted through a particular listen + /// socket, consider setting the options on the listen socket, since such options are + /// inherited automatically. If you really do need to set options that are connection + /// specific, it is safe to set them on the connection before accepting the connection. virtual EResult AcceptConnection( HSteamNetConnection hConn ) = 0; /// Disconnects from the remote host and invalidates the connection handle. @@ -345,6 +360,16 @@ public: #ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR /// Dedicated servers ervers hosted in known data centers + /// + /// If you need to set any initial config options, pass them here. See + /// SteamNetworkingConfigValue_t for more about why this is preferable to + /// setting the options "immediately" after creation. + virtual HSteamNetConnection ConnectToHostedDedicatedServer( const SteamNetworkingIdentity &identityTarget, int nVirtualPort ) = 0; + /// + /// If you need to set any initial config options, pass them here. See + /// SteamNetworkingConfigValue_t for more about why this is preferable to + /// setting the options "immediately" after creation. + virtual HSteamListenSocket CreateHostedDedicatedServerListenSocket( int nVirtualPort ) = 0; #endif // #ifndef STEAMNETWORKINGSOCKETS_ENABLE_SDR // Invoke all callbacks queued for this interface. diff --git a/include/steam/isteamnetworkingutils.h b/include/steam/isteamnetworkingutils.h index bdb29df..551fcc7 100644 --- a/include/steam/isteamnetworkingutils.h +++ b/include/steam/isteamnetworkingutils.h @@ -86,7 +86,7 @@ public: /// - eScope: Onto what type of object are you applying the setting? /// - scopeArg: Which object you want to change? (Ignored for global scope). E.g. connection handle, listen socket handle, interface pointer, etc. /// - eDataType: What type of data is in the buffer at pValue? This must match the type of the variable exactly! - /// - pArg: Value to set it to. You can pass NULL to remove a non-global sett at this scope, + /// - pArg: Value to set it to. You can pass NULL to remove a non-global setting at this scope, /// causing the value for that object to use global defaults. Or at global scope, passing NULL /// will reset any custom value and restore it to the system default. /// NOTE: When setting callback functions, do not pass the function pointer directly. @@ -94,6 +94,12 @@ public: virtual bool SetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj, ESteamNetworkingConfigDataType eDataType, const void *pArg ) = 0; + /// Set a configuration value, using a struct to pass the value. + /// (This is just a convenience shortcut; see below for the implementation and + /// a little insight into how SteamNetworkingConfigValue_t is used when + /// setting config options during listen socket and connection creation.) + bool SetConfigValueStruct( const SteamNetworkingConfigValue_t &opt, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj ); + /// Get a configuration value. /// - eValue: which value to fetch /// - eScopeType: query setting on what type of object @@ -148,6 +154,13 @@ inline bool ISteamNetworkingUtils::SetGlobalConfigValueString( ESteamNetworkingC inline bool ISteamNetworkingUtils::SetConnectionConfigValueInt32( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, int32 val ) { return SetConfigValue( eValue, k_ESteamNetworkingConfig_Connection, hConn, k_ESteamNetworkingConfig_Int32, &val ); } inline bool ISteamNetworkingUtils::SetConnectionConfigValueFloat( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, float val ) { return SetConfigValue( eValue, k_ESteamNetworkingConfig_Connection, hConn, k_ESteamNetworkingConfig_Float, &val ); } inline bool ISteamNetworkingUtils::SetConnectionConfigValueString( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, const char *val ) { return SetConfigValue( eValue, k_ESteamNetworkingConfig_Connection, hConn, k_ESteamNetworkingConfig_String, val ); } +inline bool ISteamNetworkingUtils::SetConfigValueStruct( const SteamNetworkingConfigValue_t &opt, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj ) +{ + // Locate the argument. Strings are a special case, since the + // "value" (the whole string buffer) doesn't fit in the struct + const void *pVal = ( opt.m_eDataType == k_ESteamNetworkingConfig_String ) ? (const void *)opt.m_val.m_string : (const void *)&opt.m_val; + return SetConfigValue( opt.m_eValue, eScopeType, scopeObj, opt.m_eDataType, pVal ); +} #if !defined( STEAMNETWORKINGSOCKETS_STATIC_LINK ) && defined( STEAMNETWORKINGSOCKETS_STEAMCLIENT ) inline void SteamNetworkingIPAddr::ToString( char *buf, size_t cbBuf, bool bWithPort ) const { SteamNetworkingUtils()->SteamNetworkingIPAddr_ToString( *this, buf, cbBuf, bWithPort ); } diff --git a/include/steam/steamnetworkingsockets_flat.h b/include/steam/steamnetworkingsockets_flat.h index 48aa941..c54cc6a 100644 --- a/include/steam/steamnetworkingsockets_flat.h +++ b/include/steam/steamnetworkingsockets_flat.h @@ -16,11 +16,11 @@ extern "C" { -STEAMNETWORKINGSOCKETS_INTERFACE HSteamListenSocket SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP( intptr_t instancePtr, const SteamNetworkingIPAddr *pAddress ); -STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectByIPAddress( intptr_t instancePtr, const SteamNetworkingIPAddr *pAddress ); +STEAMNETWORKINGSOCKETS_INTERFACE HSteamListenSocket SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP( intptr_t instancePtr, const SteamNetworkingIPAddr *pAddress, int nOptions, const SteamNetworkingConfigValue_t *pOptions ); +STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectByIPAddress( intptr_t instancePtr, const SteamNetworkingIPAddr *pAddress, int nOptions, const SteamNetworkingConfigValue_t *pOptions ); #ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR -STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_CreateListenSocketP2P( intptr_t instancePtr, int nVirtualPort ); -STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectP2P( intptr_t instancePtr, const SteamNetworkingIdentity *pIdentity, int nVirtualPort ); +STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_CreateListenSocketP2P( intptr_t instancePtr, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions ); +STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectP2P( intptr_t instancePtr, const SteamNetworkingIdentity *pIdentity, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions ); #endif STEAMNETWORKINGSOCKETS_INTERFACE EResult SteamAPI_ISteamNetworkingSockets_AcceptConnection( intptr_t instancePtr, HSteamNetConnection hConn ); STEAMNETWORKINGSOCKETS_INTERFACE bool SteamAPI_ISteamNetworkingSockets_CloseConnection( intptr_t instancePtr, HSteamNetConnection hPeer, int nReason, const char *pszDebug, bool bEnableLinger ); @@ -46,11 +46,11 @@ STEAMNETWORKINGSOCKETS_INTERFACE ESteamNetworkingAvailability SteamAPI_ISteamNet STEAMNETWORKINGSOCKETS_INTERFACE bool SteamAPI_ISteamNetworkingSockets_ReceivedRelayAuthTicket( intptr_t instancePtr, const void *pvTicket, int cbTicket, SteamDatagramRelayAuthTicket *pOutParsedTicket ); STEAMNETWORKINGSOCKETS_INTERFACE int SteamAPI_ISteamNetworkingSockets_FindRelayAuthTicketForServer( intptr_t instancePtr, const SteamNetworkingIdentity *pIdentityGameserver, int nVirtualPort, SteamDatagramRelayAuthTicket *pOutParsedTicket ); -STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectToHostedDedicatedServer( intptr_t instancePtr, const SteamNetworkingIdentity *pIdentityTarget, int nVirtualPort ); +STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectToHostedDedicatedServer( intptr_t instancePtr, const SteamNetworkingIdentity *pIdentityTarget, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions ); STEAMNETWORKINGSOCKETS_INTERFACE uint16 SteamAPI_ISteamNetworkingSockets_GetHostedDedicatedServerPort( intptr_t instancePtr ); STEAMNETWORKINGSOCKETS_INTERFACE SteamNetworkingPOPID SteamAPI_ISteamNetworkingSockets_GetHostedDedicatedServerPOPID( intptr_t instancePtr ); STEAMNETWORKINGSOCKETS_INTERFACE EResult SteamAPI_ISteamNetworkingSockets_GetHostedDedicatedServerAddress( intptr_t instancePtr, SteamDatagramHostedAddress *pRouting ); -STEAMNETWORKINGSOCKETS_INTERFACE HSteamListenSocket SteamAPI_ISteamNetworkingSockets_CreateHostedDedicatedServerListenSocket( intptr_t instancePtr, int nVirtualPort ); +STEAMNETWORKINGSOCKETS_INTERFACE HSteamListenSocket SteamAPI_ISteamNetworkingSockets_CreateHostedDedicatedServerListenSocket( intptr_t instancePtr, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions ); STEAMNETWORKINGSOCKETS_INTERFACE EResult SteamAPI_ISteamNetworkingSockets_GetGameCoordinatorServerLogin( intptr_t instancePtr, SteamDatagramGameCoordinatorServerLogin *pLoginInfo, int *pcbSignedBlob, void *pBlob ); #endif // #ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE diff --git a/include/steam/steamnetworkingtypes.h b/include/steam/steamnetworkingtypes.h index 02b9d2f..3ef6fac 100644 --- a/include/steam/steamnetworkingtypes.h +++ b/include/steam/steamnetworkingtypes.h @@ -11,6 +11,7 @@ #endif #include +#include //---------------------------------------- // SteamNetworkingSockets library config @@ -215,7 +216,11 @@ struct SteamNetworkingIPAddr bool operator==(const SteamNetworkingIPAddr &x ) const; }; -/// An abstract way to represent the identity of a network host +/// An abstract way to represent the identity of a network host. All identities can +/// be represented as simple string. Furthermore, this string representation is actually +/// used on the wire in several places, even though it is less efficient, in order to +/// facilitate forward compatibility. (Old client code can handle an identity type that +/// it doesn't understand.) struct SteamNetworkingIdentity { /// Type of identity. @@ -256,7 +261,11 @@ struct SteamNetworkingIdentity /// k_cchMaxString bytes big to avoid truncation. void ToString( char *buf, size_t cbBuf ) const; - /// Parse back a string that was generated using ToString + /// Parse back a string that was generated using ToString. If we don't understand the + /// string, but it looks "reasonable" (it matches the pattern type: and doesn't + /// have any funcky characters, etc), then we will return true, and the type is set to + /// k_ESteamNetworkingIdentityType_UnknownType. false will only be returned if the string + /// looks invalid. bool ParseString( const char *pszStr ); // Max sizes @@ -644,7 +653,7 @@ struct SteamNetworkingQuickConnectionStatus /// but has now been scheduled for re-transmission. Thus, it's possible to /// observe m_cbPendingReliable increasing between two checks, even if no /// calls were made to send reliable data between the checks. Data that is - /// awaiting the nagle delay will appear in these numbers. + /// awaiting the Nagle delay will appear in these numbers. int m_cbPendingUnreliable; int m_cbPendingReliable; @@ -1047,6 +1056,36 @@ enum ESteamNetworkingConfigValue k_ESteamNetworkingConfigValue__Force32Bit = 0x7fffffff }; +/// In a few places we need to set configuration options on listen sockets and connections, and +/// have them take effect *before* the listen socket or connection really starts doing anything. +/// Creating the object and then setting the options "immediately" after creation doesn't work +/// completely, because network packets could be received between the time the object is created and +/// when the options are applied. To set options at creation time in a reliable way, they must be +/// passed to the creation function. This structure is used to pass those options. +/// +/// For the meaning of these fields, see ISteamNetworkingUtils::SetConfigValue. Basically +/// when the object is created, we just iterate over the list of options and call +/// ISteamNetworkingUtils::SetConfigValueStruct, where the scope arguments are supplied by the +/// object being created. +struct SteamNetworkingConfigValue_t +{ + /// Which option is being set + ESteamNetworkingConfigValue m_eValue; + + /// Which field below did you fill in? + ESteamNetworkingConfigDataType m_eDataType; + + /// Option value + union + { + int32_t m_int32; + int64_t m_int64; + float m_float; + const char *m_string; // Points to your '\0'-terminated buffer + void *m_functionPtr; + } m_val; +}; + /// Return value of ISteamNetworkintgUtils::GetConfigValue enum ESteamNetworkingGetConfigValueResult { diff --git a/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.cpp b/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.cpp index 1e03227..cc3d3b6 100644 --- a/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.cpp +++ b/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.cpp @@ -234,27 +234,6 @@ static CSteamNetworkListenSocketBase *GetListenSocketByHandle( HSteamListenSocke return pResult; } -HSteamListenSocket AddListenSocket( CSteamNetworkListenSocketBase *pSock ) -{ - // We actually don't do map "lookups". We assume the number of listen sockets - // is going to be reasonably small. - static int s_nDummy; - ++s_nDummy; - int idx = g_mapListenSockets.Insert( s_nDummy, pSock ); - Assert( idx < 0x1000 ); - - // Use upper 16 bits as a connection sequence number, so that listen socket handles - // are not reused within a short time period. - static uint32 s_nUpperBits = 0; - s_nUpperBits += 0x10000; - if ( s_nUpperBits == 0 ) - s_nUpperBits = 0x10000; - - // Add it to our table of listen sockets - pSock->m_hListenSocketSelf = HSteamListenSocket( idx | s_nUpperBits ); - return pSock->m_hListenSocketSelf; -} - ///////////////////////////////////////////////////////////////////////////// // // CSteamSocketNetworkingBase @@ -534,11 +513,13 @@ ESteamNetworkingAvailability CSteamNetworkingSockets::GetAuthenticationStatus( S } #endif -HSteamListenSocket CSteamNetworkingSockets::CreateListenSocketIP( const SteamNetworkingIPAddr &localAddr ) +HSteamListenSocket CSteamNetworkingSockets::CreateListenSocketIP( const SteamNetworkingIPAddr &localAddr, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) { SteamDatagramTransportLock scopeLock( "CreateListenSocketIP" ); SteamDatagramErrMsg errMsg; + // FIXME probably should make it such that they can set the option + // in the option list? // Might we want a cert? If so, make sure async process to get one is in // progress (or try again if we tried earlier and failed) if ( m_connectionConfig.m_IP_AllowWithoutAuth.Get() == 0 ) @@ -554,24 +535,24 @@ HSteamListenSocket CSteamNetworkingSockets::CreateListenSocketIP( const SteamNet CSteamNetworkListenSocketDirectUDP *pSock = new CSteamNetworkListenSocketDirectUDP( this ); if ( !pSock ) return k_HSteamListenSocket_Invalid; - if ( !pSock->BInit( localAddr, errMsg ) ) + if ( !pSock->BInit( localAddr, nOptions, pOptions, errMsg ) ) { SpewError( "Cannot create listen socket. %s", errMsg ); delete pSock; return k_HSteamListenSocket_Invalid; } - return AddListenSocket( pSock ); + return pSock->m_hListenSocketSelf; } -HSteamNetConnection CSteamNetworkingSockets::ConnectByIPAddress( const SteamNetworkingIPAddr &address ) +HSteamNetConnection CSteamNetworkingSockets::ConnectByIPAddress( const SteamNetworkingIPAddr &address, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) { SteamDatagramTransportLock scopeLock( "ConnectByIPAddress" ); CSteamNetworkConnectionUDP *pConn = new CSteamNetworkConnectionUDP( this ); if ( !pConn ) return k_HSteamNetConnection_Invalid; SteamDatagramErrMsg errMsg; - if ( !pConn->BInitConnect( address, errMsg ) ) + if ( !pConn->BInitConnect( address, nOptions, pOptions, errMsg ) ) { SpewError( "Cannot create IPv4 connection. %s", errMsg ); pConn->FreeResources(); @@ -637,16 +618,10 @@ bool CSteamNetworkingSockets::CloseListenSocket( HSteamListenSocket hSocket ) CSteamNetworkListenSocketBase *pSock = GetListenSocketByHandle( hSocket ); if ( !pSock ) return false; - int idx = hSocket & 0xffff; - Assert( g_mapListenSockets.IsValidIndex( idx ) && g_mapListenSockets[ idx ] == pSock ); // Delete the socket itself // NOTE: If you change this, look at CSteamSocketNetworking::Kill()! pSock->Destroy(); - - // Remove from our data structures - g_mapListenSockets[ idx ] = nullptr; // Just for grins - g_mapListenSockets.RemoveAt( idx ); return true; } diff --git a/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.h b/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.h index 5bc297c..52e4693 100644 --- a/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.h +++ b/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.h @@ -73,8 +73,8 @@ public: } // Implements ISteamNetworkingSockets - virtual HSteamListenSocket CreateListenSocketIP( const SteamNetworkingIPAddr &localAddress ) override; - virtual HSteamNetConnection ConnectByIPAddress( const SteamNetworkingIPAddr &adress ) override; + virtual HSteamListenSocket CreateListenSocketIP( const SteamNetworkingIPAddr &localAddress, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) override; + virtual HSteamNetConnection ConnectByIPAddress( const SteamNetworkingIPAddr &adress, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) override; virtual EResult AcceptConnection( HSteamNetConnection hConn ) override; virtual bool CloseConnection( HSteamNetConnection hConn, int nReason, const char *pszDebug, bool bEnableLinger ) override; virtual bool CloseListenSocket( HSteamListenSocket hSocket ) override; diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp index e968d05..6c68ab3 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp @@ -250,6 +250,69 @@ CSteamNetworkListenSocketBase::CSteamNetworkListenSocketBase( CSteamNetworkingSo CSteamNetworkListenSocketBase::~CSteamNetworkListenSocketBase() { AssertMsg( m_mapChildConnections.Count() == 0 && !m_queueRecvMessages.m_pFirst && !m_queueRecvMessages.m_pLast, "Destroy() not used properly" ); + + // Remove us from global table, if we're in it + if ( m_hListenSocketSelf != k_HSteamListenSocket_Invalid ) + { + int idx = m_hListenSocketSelf & 0xffff; + if ( g_mapListenSockets.IsValidIndex( idx ) && g_mapListenSockets[ idx ] == this ) + { + g_mapListenSockets[ idx ] = nullptr; // Just for grins + g_mapListenSockets.RemoveAt( idx ); + } + else + { + AssertMsg( false, "Listen socket handle bookkeeping bug!" ); + } + + m_hListenSocketSelf = k_HSteamListenSocket_Invalid; + } +} + +bool CSteamNetworkListenSocketBase::BInitListenSocketCommon( int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg ) +{ + Assert( m_hListenSocketSelf == k_HSteamListenSocket_Invalid ); + + // Assign us a handle, and add us to the global table + { + // We actually don't do map "lookups". We assume the number of listen sockets + // is going to be reasonably small. + static int s_nDummy; + ++s_nDummy; + int idx = g_mapListenSockets.Insert( s_nDummy, this ); + Assert( idx < 0x1000 ); + + // Use upper 16 bits as a connection sequence number, so that listen socket handles + // are not reused within a short time period. + static uint32 s_nUpperBits = 0; + s_nUpperBits += 0x10000; + if ( s_nUpperBits == 0 ) + s_nUpperBits = 0x10000; + + // Add it to our table of listen sockets + m_hListenSocketSelf = HSteamListenSocket( idx | s_nUpperBits ); + } + + // Set options, if any + if ( pOptions ) + { + for ( int i = 0 ; i < nOptions ; ++i ) + { + if ( !m_pSteamNetworkingSocketsInterface->m_pSteamNetworkingUtils->SetConfigValueStruct( pOptions[i], k_ESteamNetworkingConfig_ListenSocket, m_hListenSocketSelf ) ) + { + V_sprintf_safe( errMsg, "Error setting option %d", pOptions[i].m_eValue ); + return false; + } + } + } + else if ( nOptions != 0 ) + { + V_strcpy_safe( errMsg, "Options list is NULL, but nOptions != 0?" ); + return false; + } + + // OK + return true; } void CSteamNetworkListenSocketBase::Destroy() @@ -446,7 +509,7 @@ void CSteamNetworkConnectionBase::FreeResources() } } -bool CSteamNetworkConnectionBase::BInitConnection( SteamNetworkingMicroseconds usecNow, SteamDatagramErrMsg &errMsg ) +bool CSteamNetworkConnectionBase::BInitConnection( SteamNetworkingMicroseconds usecNow, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg ) { // Make sure MTU values are initialized UpdateMTUFromConfig(); @@ -518,6 +581,24 @@ bool CSteamNetworkConnectionBase::BInitConnection( SteamNetworkingMicroseconds u // Add it to our table of active sockets. g_mapConnections.Insert( int16( m_hConnectionSelf ), this ); + // Set options, if any + if ( pOptions ) + { + for ( int i = 0 ; i < nOptions ; ++i ) + { + if ( !m_pSteamNetworkingSocketsInterface->m_pSteamNetworkingUtils->SetConfigValueStruct( pOptions[i], k_ESteamNetworkingConfig_Connection, m_hConnectionSelf ) ) + { + V_sprintf_safe( errMsg, "Error setting option %d", pOptions[i].m_eValue ); + return false; + } + } + } + else if ( nOptions != 0 ) + { + V_strcpy_safe( errMsg, "Options list is NULL, but nOptions != 0?" ); + return false; + } + // Make sure a description has been set for debugging purposes SetDescription(); @@ -2151,7 +2232,7 @@ failed: // Do generic base class initialization for ( int i = 0 ; i < 2 ; ++i ) { - if ( !pConn[i]->BInitConnection( usecNow, errMsg ) ) + if ( !pConn[i]->BInitConnection( usecNow, 0, nullptr, errMsg ) ) { AssertMsg1( false, "CSteamNetworkConnectionPipe::BInitConnection failed. %s", errMsg ); goto failed; diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h index 90851ed..b49ad3f 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h @@ -256,6 +256,8 @@ public: protected: CSteamNetworkListenSocketBase( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface ); virtual ~CSteamNetworkListenSocketBase(); // hidden destructor, don't call directly. Use Destroy() + + bool BInitListenSocketCommon( int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg ); }; ///////////////////////////////////////////////////////////////////////////// @@ -470,7 +472,7 @@ protected: virtual ~CSteamNetworkConnectionBase(); // hidden destructor, don't call directly. Use Destroy() /// Initialize connection bookkeeping - bool BInitConnection( SteamNetworkingMicroseconds usecNow, SteamDatagramErrMsg &errMsg ); + bool BInitConnection( SteamNetworkingMicroseconds usecNow, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg ); /// Called from BInitConnection, to start obtaining certs, etc virtual void InitConnectionCrypto( SteamNetworkingMicroseconds usecNow ); @@ -762,8 +764,6 @@ inline CSteamNetworkConnectionBase *FindConnectionByLocalID( uint32 nLocalConnec return GetConnectionByHandle( HSteamNetConnection( nLocalConnectionID ) ); } -extern HSteamListenSocket AddListenSocket( CSteamNetworkListenSocketBase *pSock ); - } // namespace SteamNetworkingSocketsLib #endif // STEAMNETWORKINGSOCKETS_CONNECTIONS_H diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_flat.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_flat.cpp index 8ae1a14..90606b4 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_flat.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_flat.cpp @@ -6,25 +6,25 @@ extern "C" { -STEAMNETWORKINGSOCKETS_INTERFACE HSteamListenSocket SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP( intptr_t instancePtr, const SteamNetworkingIPAddr *pAddress ) +STEAMNETWORKINGSOCKETS_INTERFACE HSteamListenSocket SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP( intptr_t instancePtr, const SteamNetworkingIPAddr *pAddress, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) { - return ((ISteamNetworkingSockets*)instancePtr)->CreateListenSocketIP( *pAddress ); + return ((ISteamNetworkingSockets*)instancePtr)->CreateListenSocketIP( *pAddress, nOptions, pOptions ); } -STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectByIPAddress( intptr_t instancePtr, const SteamNetworkingIPAddr *pAddress ) +STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectByIPAddress( intptr_t instancePtr, const SteamNetworkingIPAddr *pAddress, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) { - return ((ISteamNetworkingSockets*)instancePtr)->ConnectByIPAddress( *pAddress ); + return ((ISteamNetworkingSockets*)instancePtr)->ConnectByIPAddress( *pAddress, nOptions, pOptions ); } #ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR -STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_CreateListenSocketP2P( intptr_t instancePtr, int nVirtualPort ) +STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_CreateListenSocketP2P( intptr_t instancePtr, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) { - return ((ISteamNetworkingSockets*)instancePtr)->CreateListenSocketP2P( nVirtualPort ); + return ((ISteamNetworkingSockets*)instancePtr)->CreateListenSocketP2P( nVirtualPort, nOptions, pOptions ); } -STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectP2P( intptr_t instancePtr, const SteamNetworkingIdentity *pIdentity, int nVirtualPort ) +STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectP2P( intptr_t instancePtr, const SteamNetworkingIdentity *pIdentity, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) { - return ((ISteamNetworkingSockets*)instancePtr)->ConnectP2P( *pIdentity, nVirtualPort ); + return ((ISteamNetworkingSockets*)instancePtr)->ConnectP2P( *pIdentity, nVirtualPort, nOptions, pOptions ); } #endif @@ -134,9 +134,9 @@ STEAMNETWORKINGSOCKETS_INTERFACE int SteamAPI_ISteamNetworkingSockets_FindRelayA return ((ISteamNetworkingSockets*)instancePtr)->FindRelayAuthTicketForServer( *pIdentityGameserver, nVirtualPort, pOutParsedTicket ); } -STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectToHostedDedicatedServer( intptr_t instancePtr, const SteamNetworkingIdentity *pIdentityTarget, int nVirtualPort ) +STEAMNETWORKINGSOCKETS_INTERFACE HSteamNetConnection SteamAPI_ISteamNetworkingSockets_ConnectToHostedDedicatedServer( intptr_t instancePtr, const SteamNetworkingIdentity *pIdentityTarget, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) { - return ((ISteamNetworkingSockets*)instancePtr)->ConnectToHostedDedicatedServer( *pIdentityTarget, nVirtualPort ); + return ((ISteamNetworkingSockets*)instancePtr)->ConnectToHostedDedicatedServer( *pIdentityTarget, nVirtualPort, nOptions, pOptions ); } STEAMNETWORKINGSOCKETS_INTERFACE uint16 SteamAPI_ISteamNetworkingSockets_GetHostedDedicatedServerPort( intptr_t instancePtr ) @@ -154,9 +154,9 @@ STEAMNETWORKINGSOCKETS_INTERFACE EResult SteamAPI_ISteamNetworkingSockets_GetHos return ((ISteamNetworkingSockets*)instancePtr)->GetHostedDedicatedServerAddress( pRouting ); } -STEAMNETWORKINGSOCKETS_INTERFACE HSteamListenSocket SteamAPI_ISteamNetworkingSockets_CreateHostedDedicatedServerListenSocket( intptr_t instancePtr, int nVirtualPort ) +STEAMNETWORKINGSOCKETS_INTERFACE HSteamListenSocket SteamAPI_ISteamNetworkingSockets_CreateHostedDedicatedServerListenSocket( intptr_t instancePtr, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions ) { - return ((ISteamNetworkingSockets*)instancePtr)->CreateHostedDedicatedServerListenSocket( nVirtualPort ); + return ((ISteamNetworkingSockets*)instancePtr)->CreateHostedDedicatedServerListenSocket( nVirtualPort, nOptions, pOptions ); } STEAMNETWORKINGSOCKETS_INTERFACE EResult SteamAPI_ISteamNetworkingSockets_GetGameCoordinatorServerLogin( intptr_t instancePtr, SteamDatagramGameCoordinatorServerLogin *pLoginInfo, int *pcbSignedBlob, void *pBlob ) { diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.cpp index 0346d5f..ae689f2 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.cpp @@ -130,7 +130,7 @@ CSteamNetworkListenSocketDirectUDP::~CSteamNetworkListenSocketDirectUDP() } } -bool CSteamNetworkListenSocketDirectUDP::BInit( const SteamNetworkingIPAddr &localAddr, SteamDatagramErrMsg &errMsg ) +bool CSteamNetworkListenSocketDirectUDP::BInit( const SteamNetworkingIPAddr &localAddr, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg ) { Assert( m_pSock == nullptr ); @@ -140,6 +140,10 @@ bool CSteamNetworkListenSocketDirectUDP::BInit( const SteamNetworkingIPAddr &loc return false; } + // Set options, add us to the global table + if ( !BInitListenSocketCommon( nOptions, pOptions, errMsg ) ) + return false; + m_pSock = new CSharedSocket; if ( !m_pSock->BInit( localAddr, CRecvPacketCallback( ReceivedFromUnknownHost, this ), errMsg ) ) { @@ -734,7 +738,7 @@ int CSteamNetworkConnectionUDP::SendEncryptedDataChunk( const void *pChunk, int return cbSend; } -bool CSteamNetworkConnectionUDP::BInitConnect( const SteamNetworkingIPAddr &addressRemote, SteamDatagramErrMsg &errMsg ) +bool CSteamNetworkConnectionUDP::BInitConnect( const SteamNetworkingIPAddr &addressRemote, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg ) { AssertMsg( !m_pSocket, "Trying to connect when we already have a socket?" ); @@ -781,7 +785,7 @@ bool CSteamNetworkConnectionUDP::BInitConnect( const SteamNetworkingIPAddr &addr // Let base class do some common initialization SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp(); - if ( !CSteamNetworkConnectionBase::BInitConnection( usecNow, errMsg ) ) + if ( !CSteamNetworkConnectionBase::BInitConnection( usecNow, nOptions, pOptions, errMsg ) ) { m_pSocket->Close(); m_pSocket = nullptr; @@ -895,7 +899,7 @@ bool CSteamNetworkConnectionUDP::BBeginAccept( // Let base class do some common initialization SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp(); - if ( !CSteamNetworkConnectionBase::BInitConnection( usecNow, errMsg ) ) + if ( !CSteamNetworkConnectionBase::BInitConnection( usecNow, 0, nullptr, errMsg ) ) { m_pSocket->Close(); m_pSocket = nullptr; @@ -1791,7 +1795,7 @@ failed: for ( int i = 0 ; i < 2 ; ++i ) { pConn[i]->m_pSocket = sock[i]; - if ( !pConn[i]->BInitConnection( usecNow, errMsg ) ) + if ( !pConn[i]->BInitConnection( usecNow, 0, nullptr, errMsg ) ) { AssertMsg1( false, "CSteamNetworkConnectionlocalhostLoopback::BInitConnection failed. %s", errMsg ); goto failed; diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.h b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.h index 360791f..f6c8be0 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.h +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_udp.h @@ -25,7 +25,7 @@ public: virtual bool APIGetAddress( SteamNetworkingIPAddr *pAddress ) override; /// Setup - bool BInit( const SteamNetworkingIPAddr &localAddr, SteamDatagramErrMsg &errMsg ); + bool BInit( const SteamNetworkingIPAddr &localAddr, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg ); private: @@ -84,7 +84,7 @@ public: virtual EUnsignedCert AllowLocalUnsignedCert() override; /// Initiate a connection - bool BInitConnect( const SteamNetworkingIPAddr &addressRemote, SteamDatagramErrMsg &errMsg ); + bool BInitConnect( const SteamNetworkingIPAddr &addressRemote, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg ); /// Accept a connection that has passed the handshake phase bool BBeginAccept( diff --git a/tests/test_connection.cpp b/tests/test_connection.cpp index 33d74f3..5611d18 100644 --- a/tests/test_connection.cpp +++ b/tests/test_connection.cpp @@ -545,8 +545,8 @@ static void RunSteamDatagramConnectionTest() //} // Initiate connection - g_hSteamListenSocket = pSteamSocketNetworking->CreateListenSocketIP( bindServerAddress ); - g_peerClient.m_hSteamNetConnection = pSteamSocketNetworking->ConnectByIPAddress( connectToServerAddress ); + g_hSteamListenSocket = pSteamSocketNetworking->CreateListenSocketIP( bindServerAddress, 0, nullptr ); + g_peerClient.m_hSteamNetConnection = pSteamSocketNetworking->ConnectByIPAddress( connectToServerAddress, 0, nullptr ); pSteamSocketNetworking->SetConnectionName( g_peerClient.m_hSteamNetConnection, "Client" ); // // Send a few random message, before we get connected, just to test that case