#include "csteamnetworkingsockets.h"
#include "steamnetworkingsockets_lowlevel.h"
#include "steamnetworkingsockets_connections.h"
#include "steamnetworkingsockets_udp.h"
#include "../steamnetworkingsockets_certstore.h"
#include "crypto.h"
#ifdef STEAMNETWORKINGSOCKETS_STANDALONELIB
#include <steam/steamnetworkingsockets.h>
#endif
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
#include "csteamnetworkingmessages.h"
#endif
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_FAKEIP
#include <steam/steamnetworkingfakeip.h>
#endif
#if defined(__APPLE__)
#include "AvailabilityMacros.h"
#include "TargetConditionals.h"
#endif
#include "tier0/memdbgon.h"
ISteamNetworkingSockets::~ISteamNetworkingSockets() {}
ISteamNetworkingUtils::~ISteamNetworkingUtils() {}
namespace SteamNetworkingSocketsLib {
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketLoss_Send, 0.0f, 0.0f, 100.0f );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketLoss_Recv, 0.0f, 0.0f, 100.0f );
DEFINE_GLOBAL_CONFIGVAL( int32, FakePacketLag_Send, 0, 0, 5000 );
DEFINE_GLOBAL_CONFIGVAL( int32, FakePacketLag_Recv, 0, 0, 5000 );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketReorder_Send, 0.0f, 0.0f, 100.0f );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketReorder_Recv, 0.0f, 0.0f, 100.0f );
DEFINE_GLOBAL_CONFIGVAL( int32, FakePacketReorder_Time, 15, 0, 5000 );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketDup_Send, 0.0f, 0.0f, 100.0f );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketDup_Recv, 0.0f, 0.0f, 100.0f );
DEFINE_GLOBAL_CONFIGVAL( int32, FakePacketDup_TimeMax, 10, 0, 5000 );
DEFINE_GLOBAL_CONFIGVAL( int32, PacketTraceMaxBytes, -1, -1, 99999 );
DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Send_Rate, 0, 0, 1024*1024*1024 );
DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Send_Burst, 16*1024, 0, 1024*1024 );
DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Recv_Rate, 0, 0, 1024*1024*1024 );
DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Recv_Burst, 16*1024, 0, 1024*1024 );
DEFINE_GLOBAL_CONFIGVAL( int32, OutOfOrderCorrectionWindowMicroseconds, 1000, 0, 50*1000 );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Send_Avg, 0.0f, 0.0f, 2000.0f );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Send_Max, 100.0f, 0.0f, 5000.0f );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Send_Pct, 75.0f, 0.0f, 100.0f );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Recv_Avg, 0.0f, 0.0f, 2000.0f );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Recv_Max, 100.0f, 0.0f, 5000.0f );
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Recv_Pct, 75.0f, 0.0f, 100.0f );
DEFINE_GLOBAL_CONFIGVAL( void *, Callback_AuthStatusChanged, nullptr );
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
DEFINE_GLOBAL_CONFIGVAL( void*, Callback_MessagesSessionRequest, nullptr );
DEFINE_GLOBAL_CONFIGVAL( void*, Callback_MessagesSessionFailed, nullptr );
#endif
DEFINE_GLOBAL_CONFIGVAL( void *, Callback_CreateConnectionSignaling, nullptr );
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_FAKEIP
DEFINE_GLOBAL_CONFIGVAL( void *, Callback_FakeIPResult, nullptr );
#endif
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, TimeoutInitial, 10000, 0, INT32_MAX );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, TimeoutConnected, 10000, 0, INT32_MAX );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, SendBufferSize, 512*1024, 4*1024, 0x10000000 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, RecvBufferSize, 1024*1024, 4*1024, 0x10000000 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, RecvBufferMessages, 1000, 2, 0x10000000 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, RecvMaxMessageSize, 512*1024, 64, 0x10000000 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, RecvMaxSegmentsPerPacket, k_cbSteamNetworkingSocketsMaxUDPMsgLen, 1, k_cbSteamNetworkingSocketsMaxUDPMsgLen );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int64, ConnectionUserData, -1 ); DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, SendRateMin, 256*1024, 1024, 0x10000000 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, SendRateMax, 256*1024, 1024, 0x10000000 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, NagleTime, 5000, 0, 20000 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, MTU_PacketSize, 1300, k_cbSteamNetworkingSocketsMinMTUPacketSize, k_cbSteamNetworkingSocketsMaxUDPMsgLen );
#ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, IP_AllowWithoutAuth, 2, 0, 2 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, IPLocalHost_AllowWithoutAuth, 2, 0, 2 );
#else
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, IP_AllowWithoutAuth, 0, 0, 2 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, IPLocalHost_AllowWithoutAuth, 0, 0, 2 );
#endif
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, Unencrypted, 0, 0, 3 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, SymmetricConnect, 0, 0, 1 );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LocalVirtualPort, -1, -1, INT32_MAX );
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_DUALWIFI
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, DualWifi_Enable, 1, 0, k_nDualWifiEnable_MAX );
#endif
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_AckRTT, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_PacketDecode, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_Message, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_PacketGaps, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_P2PRendezvous, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( void *, Callback_ConnectionStatusChanged, nullptr );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, SendTimeSincePreviousPacket, -1, -1, 1 );
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, EnableDiagnosticsUI, 1, 0, 1 );
#endif
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( std::string, P2P_STUN_ServerList, "" );
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, "" );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, P2P_Transport_ICE_Implementation, 0, 0, 2 );
COMPILE_TIME_ASSERT( k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Default == -1 );
COMPILE_TIME_ASSERT( k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Disable == 0 );
#ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, P2P_Transport_ICE_Enable, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_All, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Disable, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_All );
#else
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, P2P_Transport_ICE_Enable, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Default, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Default, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_All );
#endif
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, P2P_Transport_ICE_Penalty, 0, 0, INT_MAX );
#endif
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( std::string, SDRClient_DevTicket, "" );
DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, P2P_Transport_SDR_Penalty, 0, 0, INT_MAX );
#endif
#if PlatformCanSendECN()
DEFINE_GLOBAL_CONFIGVAL( int32, ECN, -1, -1, 2 );
#endif
static GlobalConfigValueEntry *s_pFirstGlobalConfigEntry = nullptr;
static bool s_bConfigValueTableInitted = false;
static std::vector<GlobalConfigValueEntry *> s_vecConfigValueTable; static std::vector<GlobalConfigValueEntry *> s_vecConnectionConfigValueTable;
GlobalConfigValueEntry::GlobalConfigValueEntry(
ESteamNetworkingConfigValue eValue,
const char *pszName,
ESteamNetworkingConfigDataType eDataType,
ESteamNetworkingConfigScope eScope,
int cbOffsetOf
) : m_eValue{ eValue }
, m_pszName{ pszName }
, m_eDataType{ eDataType }
, m_eScope{ eScope }
, m_cbOffsetOf{cbOffsetOf}
, m_pNextEntry( s_pFirstGlobalConfigEntry )
{
s_pFirstGlobalConfigEntry = this;
AssertMsg( !s_bConfigValueTableInitted, "Attempt to register more config values after table is already initialized" );
s_bConfigValueTableInitted = false;
}
static void EnsureConfigValueTableInitted()
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "EnsureConfigValueTableInitted" );
if ( s_bConfigValueTableInitted )
return;
for ( GlobalConfigValueEntry *p = s_pFirstGlobalConfigEntry ; p ; p = p->m_pNextEntry )
{
s_vecConfigValueTable.push_back( p );
if ( p->m_eScope == k_ESteamNetworkingConfig_Connection )
s_vecConnectionConfigValueTable.push_back( p );
}
std::sort( s_vecConfigValueTable.begin(), s_vecConfigValueTable.end(),
[]( GlobalConfigValueEntry *a, GlobalConfigValueEntry *b ) { return a->m_eValue < b->m_eValue; } );
std::sort( s_vecConnectionConfigValueTable.begin(), s_vecConnectionConfigValueTable.end(),
[]( GlobalConfigValueEntry *a, GlobalConfigValueEntry *b ) { return a->m_cbOffsetOf < b->m_cbOffsetOf; } );
const int N = len( s_vecConfigValueTable );
for ( int i = 1 ; i < N ; ++i )
{
s_vecConfigValueTable[i-1]->m_pNextEntry = s_vecConfigValueTable[i];
AssertFatalMsg( s_vecConfigValueTable[i-1]->m_eValue < s_vecConfigValueTable[i]->m_eValue, "Registered duplicate config value %d", s_vecConfigValueTable[i]->m_eValue );
}
s_vecConfigValueTable[N-1]->m_pNextEntry = nullptr;
s_pFirstGlobalConfigEntry = nullptr;
s_bConfigValueTableInitted = true; }
static GlobalConfigValueEntry *FindConfigValueEntry( ESteamNetworkingConfigValue eSearchVal )
{
Assert( s_bConfigValueTableInitted );
int l = 0;
int r = len( s_vecConfigValueTable )-1;
Assert( r > 0 ); while ( l <= r )
{
int m = (l+r)>>1;
GlobalConfigValueEntry *mp = s_vecConfigValueTable[m];
if ( eSearchVal < mp->m_eValue )
r = m-1;
else if ( eSearchVal > mp->m_eValue )
l = m+1;
else
return mp;
}
return nullptr;
}
void ConnectionConfig::Init( ConnectionConfig *pInherit )
{
Assert( s_bConfigValueTableInitted );
for ( GlobalConfigValueEntry *pEntry : s_vecConnectionConfigValueTable )
{
ConfigValueBase *pVal = (ConfigValueBase *)((intptr_t)this + pEntry->m_cbOffsetOf );
if ( pInherit )
{
pVal->m_pInherit = (ConfigValueBase *)((intptr_t)pInherit + pEntry->m_cbOffsetOf );
}
else
{
pVal->m_pInherit = &( static_cast< GlobalConfigValueBase<int32> * >( pEntry ) )->m_value;
}
}
}
CUtlHashMap<uint16, CSteamNetworkConnectionBase *, std::equal_to<uint16>, Identity<uint16> > g_mapConnections;
CUtlHashMap<int, CSteamNetworkPollGroup *, std::equal_to<int>, Identity<int> > g_mapPollGroups;
TableLock g_tables_lock;
CUtlHashMap<int, CSteamNetworkListenSocketBase *, std::equal_to<int>, Identity<int> > g_mapListenSockets;
static bool BConnectionStateExistsToAPI( ESteamNetworkingConnectionState eState )
{
switch ( eState )
{
default:
Assert( false );
return false;
case k_ESteamNetworkingConnectionState_None:
case k_ESteamNetworkingConnectionState_Dead:
case k_ESteamNetworkingConnectionState_FinWait:
case k_ESteamNetworkingConnectionState_Linger:
return false;
case k_ESteamNetworkingConnectionState_Connecting:
case k_ESteamNetworkingConnectionState_FindingRoute:
case k_ESteamNetworkingConnectionState_Connected:
case k_ESteamNetworkingConnectionState_ClosedByPeer:
case k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
return true;
}
}
static CSteamNetworkConnectionBase *InternalGetConnectionByHandle( HSteamNetConnection sock, ConnectionScopeLock &scopeLock, const char *pszLockTag, bool bForAPI )
{
if ( sock == 0 )
return nullptr;
for (;;)
{
TableScopeLock tableScopeLock;
if ( !tableScopeLock.TryLock( g_tables_lock, 1, nullptr ) )
continue;
int idx = g_mapConnections.Find( uint16( sock ) );
if ( idx == g_mapConnections.InvalidIndex() )
break;
CSteamNetworkConnectionBase *pResult = g_mapConnections[ idx ];
if ( !pResult )
{
AssertMsg( false, "g_mapConnections corruption!" );
break;
}
if ( uint16( pResult->m_hConnectionSelf ) != uint16( sock ) )
{
AssertMsg( false, "Connection map corruption!" );
break;
}
ESteamNetworkingConnectionState s = pResult->GetState();
if ( s == k_ESteamNetworkingConnectionState_Dead )
break;
if ( bForAPI )
{
if ( !BConnectionStateExistsToAPI( s ) )
break;
}
if ( !scopeLock.TryLock( *pResult->m_pLock, 1, pszLockTag ) )
continue;
s = pResult->GetState();
if ( s != k_ESteamNetworkingConnectionState_Dead )
{
if ( !bForAPI || BConnectionStateExistsToAPI( s ) )
{
return pResult;
}
}
scopeLock.Unlock();
break;
}
return nullptr;
}
CSteamNetworkConnectionBase *GetConnectionByHandle( HSteamNetConnection sock, ConnectionScopeLock &scopeLock )
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
return InternalGetConnectionByHandle( sock, scopeLock, nullptr, false );
}
inline CSteamNetworkConnectionBase *GetConnectionByHandleForAPI( HSteamNetConnection sock, ConnectionScopeLock &scopeLock, const char *pszLockTag )
{
return InternalGetConnectionByHandle( sock, scopeLock, pszLockTag, true );
}
static CSteamNetworkListenSocketBase *GetListenSocketByHandle( HSteamListenSocket sock )
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread(); if ( sock == k_HSteamListenSocket_Invalid )
return nullptr;
AssertMsg( !(sock & 0x80000000), "A poll group handle was used where a listen socket handle was expected" );
int idx = sock & 0xffff;
if ( !g_mapListenSockets.IsValidIndex( idx ) )
return nullptr;
CSteamNetworkListenSocketBase *pResult = g_mapListenSockets[ idx ];
if ( pResult->m_hListenSocketSelf != sock )
{
return nullptr;
}
return pResult;
}
CSteamNetworkPollGroup *GetPollGroupByHandle( HSteamNetPollGroup hPollGroup, PollGroupScopeLock &scopeLock, const char *pszLockTag )
{
if ( hPollGroup == k_HSteamNetPollGroup_Invalid )
return nullptr;
AssertMsg( (hPollGroup & 0x80000000), "A listen socket handle was used where a poll group handle was expected" );
int idx = hPollGroup & 0xffff;
TableScopeLock tableScopeLock( g_tables_lock );
if ( !g_mapPollGroups.IsValidIndex( idx ) )
return nullptr;
CSteamNetworkPollGroup *pResult = g_mapPollGroups[ idx ];
while ( pResult->m_hPollGroupSelf == hPollGroup )
{
if ( scopeLock.TryLock( pResult->m_lock, 1, pszLockTag ) )
return pResult;
}
return nullptr;
}
std::vector<CSteamNetworkingSockets *> CSteamNetworkingSockets::s_vecSteamNetworkingSocketsInstances;
CSteamNetworkingSockets::CSteamNetworkingSockets( CSteamNetworkingUtils *pSteamNetworkingUtils )
: m_bHaveLowLevelRef( false )
, m_pSteamNetworkingUtils( pSteamNetworkingUtils )
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
, m_pSteamNetworkingMessages( nullptr )
#endif
#ifdef STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT
, m_scheduleCheckRenewCert( this, &CSteamNetworkingSockets::CheckAuthenticationPrerequisites )
#endif
, m_bEverTriedToGetCert( false )
, m_bEverGotCert( false )
, m_mutexPendingCallbacks( "pending_callbacks", LockDebugInfo::k_nOrder_Max ) {
m_connectionConfig.Init( nullptr );
InternalClearIdentity();
}
void CSteamNetworkingSockets::InternalClearIdentity()
{
m_identity.Clear();
m_msgSignedCert.Clear();
m_msgCert.Clear();
m_keyPrivateKey.Wipe();
#ifdef STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT
m_CertStatus.m_eAvail = k_ESteamNetworkingAvailability_NeverTried;
m_CertStatus.m_debugMsg[0] = '\0';
#else
m_CertStatus.m_eAvail = k_ESteamNetworkingAvailability_CannotTry;
V_strcpy_safe( m_CertStatus.m_debugMsg, "No certificate authority" );
#endif
m_AuthenticationStatus = m_CertStatus;
m_bEverTriedToGetCert = false;
m_bEverGotCert = false;
}
void CSteamNetworkingSockets::InternalOnGotIdentity( int nIdentitySetFlags )
{
}
CSteamNetworkingSockets::~CSteamNetworkingSockets()
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
Assert( !m_bHaveLowLevelRef ); }
#ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
bool CSteamNetworkingSockets::BInitGameNetworkingSockets( const SteamNetworkingIdentity *pIdentity, SteamDatagramErrMsg &errMsg )
{
AssertMsg( !m_bHaveLowLevelRef, "Initted interface twice?" );
if ( !BInitLowLevel( errMsg ) )
return false;
if ( pIdentity )
m_identity = *pIdentity;
else
CacheIdentity();
return true;
}
#endif
bool CSteamNetworkingSockets::BInitLowLevel( SteamNetworkingErrMsg &errMsg )
{
if ( m_bHaveLowLevelRef )
return true;
if ( !BSteamNetworkingSocketsLowLevelAddRef( errMsg) )
return false;
if ( !has_element( s_vecSteamNetworkingSocketsInstances, this ) )
s_vecSteamNetworkingSocketsInstances.push_back( this );
m_bHaveLowLevelRef = true;
return true;
}
void CSteamNetworkingSockets::KillConnections()
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "CSteamNetworkingSockets::KillConnections" );
TableScopeLock tableScopeLock( g_tables_lock );
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
FOR_EACH_HASHMAP( m_mapMessagesEndpointByVirtualPort, idx )
{
m_mapMessagesEndpointByVirtualPort[idx]->DestroyMessagesEndPoint();
Assert( !m_mapMessagesEndpointByVirtualPort.IsValidIndex( idx ) ); }
Assert( m_pSteamNetworkingMessages == nullptr );
Assert( m_mapMessagesEndpointByVirtualPort.Count() == 0 );
m_pSteamNetworkingMessages = nullptr; m_mapMessagesEndpointByVirtualPort.Purge();
#endif
CSteamNetworkConnectionBase::ProcessDeletionList();
FOR_EACH_HASHMAP( g_mapConnections, idx )
{
CSteamNetworkConnectionBase *pConn = g_mapConnections[idx];
if ( pConn->m_pSteamNetworkingSocketsInterface == this )
{
ConnectionScopeLock connectionLock( *pConn );
if ( pConn->BStateIsActive() )
{
SpewMsg( "[%s] Cleaning up open connection on system shutdown", pConn->GetDescription() );
pConn->APICloseConnection( k_ESteamNetConnectionEnd_AppException_Max, "SteamNetworkingSockets shutdown", false );
}
pConn->ConnectionQueueDestroy();
}
}
CSteamNetworkConnectionBase::ProcessDeletionList();
FOR_EACH_HASHMAP( g_mapListenSockets, idx )
{
CSteamNetworkListenSocketBase *pSock = g_mapListenSockets[idx];
if ( pSock->m_pSteamNetworkingSocketsInterface == this )
{
DbgVerify( CloseListenSocket( pSock->m_hListenSocketSelf ) );
Assert( !g_mapListenSockets.IsValidIndex( idx ) );
}
}
FOR_EACH_HASHMAP( g_mapPollGroups, idx )
{
CSteamNetworkPollGroup *pPollGroup = g_mapPollGroups[idx];
if ( pPollGroup->m_pSteamNetworkingSocketsInterface == this )
{
DbgVerify( DestroyPollGroup( pPollGroup->m_hPollGroupSelf ) );
Assert( !g_mapPollGroups.IsValidIndex( idx ) );
}
}
}
void CSteamNetworkingSockets::Destroy()
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "CSteamNetworkingSockets::Destroy" );
FreeResources();
find_and_remove_element( s_vecSteamNetworkingSocketsInstances, this );
delete this;
}
void CSteamNetworkingSockets::FreeResources()
{
KillConnections();
InternalClearIdentity();
if ( m_bHaveLowLevelRef )
{
m_bHaveLowLevelRef = false;
SteamNetworkingSocketsLowLevelDecRef();
}
}
bool CSteamNetworkingSockets::BHasAnyConnections() const
{
TableScopeLock tableScopeLock( g_tables_lock );
for ( CSteamNetworkConnectionBase *pConn: g_mapConnections.IterValues() )
{
if ( pConn->m_pSteamNetworkingSocketsInterface == this )
return true;
}
return false;
}
bool CSteamNetworkingSockets::BHasAnyListenSockets() const
{
TableScopeLock tableScopeLock( g_tables_lock );
for ( CSteamNetworkListenSocketBase *pSock: g_mapListenSockets.IterValues() )
{
if ( pSock->m_pSteamNetworkingSocketsInterface == this )
return true;
}
return false;
}
bool CSteamNetworkingSockets::GetIdentity( SteamNetworkingIdentity *pIdentity )
{
SteamNetworkingGlobalLock scopeLock( "GetIdentity" );
InternalGetIdentity();
if ( pIdentity )
*pIdentity = m_identity;
return !m_identity.IsInvalid();
}
int CSteamNetworkingSockets::GetSecondsUntilCertExpiry() const
{
if ( !m_msgSignedCert.has_cert() )
return INT_MIN;
Assert( m_msgSignedCert.has_ca_signature() ); Assert( m_msgCert.has_key_data() );
Assert( m_msgCert.has_time_expiry() );
int nSeconduntilExpiry = (long)m_msgCert.time_expiry() - (long)m_pSteamNetworkingUtils->GetTimeSecure();
return nSeconduntilExpiry;
}
bool CSteamNetworkingSockets::GetCertificateRequest( int *pcbBlob, void *pBlob, SteamNetworkingErrMsg &errMsg )
{
SteamNetworkingGlobalLock scopeLock( "GetCertificateRequest" );
CECSigningPublicKey pubKey;
if ( m_keyPrivateKey.IsValid() )
{
DbgVerify( m_keyPrivateKey.GetPublicKey( &pubKey ) );
}
else
{
CCrypto::GenerateSigningKeyPair( &pubKey, &m_keyPrivateKey );
}
CMsgSteamDatagramCertificateRequest msgRequest;
CMsgSteamDatagramCertificate &msgCert =*msgRequest.mutable_cert();
msgCert.set_key_type( CMsgSteamDatagramCertificate_EKeyType_ED25519 );
DbgVerify( pubKey.GetRawDataAsStdString( msgCert.mutable_key_data() ) );
InternalGetIdentity();
if ( !m_identity.IsInvalid() && !m_identity.IsLocalHost() )
{
SteamNetworkingIdentityToProtobuf( m_identity, msgCert, identity_string, legacy_identity_binary, legacy_steam_id );
}
SteamNetworkingPOPID popid = GetHostedDedicatedServerPOPID();
if ( popid )
msgCert.add_gameserver_datacenter_ids( popid );
int cb = ProtoMsgByteSize( msgRequest );
if ( !pBlob )
{
*pcbBlob = cb;
return true;
}
if ( cb > *pcbBlob )
{
*pcbBlob = cb;
V_sprintf_safe( errMsg, "%d byte buffer not big enough; %d bytes required", *pcbBlob, cb );
return false;
}
*pcbBlob = cb;
uint8 *p = (uint8 *)pBlob;
DbgVerify( msgRequest.SerializeWithCachedSizesToArray( p ) == p + cb );
return true;
}
bool CSteamNetworkingSockets::SetCertificate( const void *pCertificate, int cbCertificate, SteamNetworkingErrMsg &errMsg )
{
SteamNetworkingGlobalLock scopeLock( "SetCertificate" );
int nIdentitySetFlags = 0; return InternalSetCertificate( pCertificate, cbCertificate, errMsg, nIdentitySetFlags );
}
bool CSteamNetworkingSockets::InternalSetCertificate( const void *pCertificate, int cbCertificate, SteamNetworkingErrMsg &errMsg, int nIdentitySetFlags )
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "InternalSetCertificate" );
CMsgSteamDatagramCertificateSigned msgCertSigned;
if ( !msgCertSigned.ParseFromArray( pCertificate, cbCertificate ) )
{
V_strcpy_safe( errMsg, "CMsgSteamDatagramCertificateSigned failed protobuf parse" );
return false;
}
CMsgSteamDatagramCertificate msgCert;
time_t authTime = m_pSteamNetworkingUtils->GetTimeSecure();
const CertAuthScope *pAuthScope = CertStore_CheckCert( msgCertSigned, msgCert, authTime, errMsg );
if ( !pAuthScope )
{
SpewWarning( "SetCertificate: We are not currently able to verify our own cert! %s. Continuing anyway!", errMsg );
}
SteamNetworkingErrMsg tempErrMsg;
SteamNetworkingIdentity certIdentity;
int r = SteamNetworkingIdentityFromCert( certIdentity, msgCert, tempErrMsg );
if ( r < 0 )
{
V_sprintf_safe( errMsg, "Cert has invalid identity. %s", tempErrMsg );
return false;
}
bool bSetIdentity = false;
if ( m_identity.IsInvalid() || m_identity.IsLocalHost() )
{
bSetIdentity = true;
}
else if ( !( m_identity == certIdentity ) )
{
V_sprintf_safe( errMsg, "Cert is for identity '%s'. We are '%s'", SteamNetworkingIdentityRender( certIdentity ).c_str(), SteamNetworkingIdentityRender( m_identity ).c_str() );
return false;
}
if ( msgCert.key_type() != CMsgSteamDatagramCertificate_EKeyType_ED25519 || msgCert.key_data().size() != 32 )
{
V_strcpy_safe( errMsg, "Cert has invalid public key" );
return false;
}
if ( msgCertSigned.has_private_key_data() )
{
const std::string &private_key_data = msgCertSigned.private_key_data();
if ( m_keyPrivateKey.IsValid() )
{
if ( !m_keyPrivateKey.BMatchesRawData( private_key_data.data(), private_key_data.length() ) )
{
V_strcpy_safe( errMsg, "Private key mismatch" );
return false;
}
}
else
{
if ( !m_keyPrivateKey.SetRawDataFromStdString( private_key_data ) )
{
V_strcpy_safe( errMsg, "Invalid private key" );
return false;
}
}
}
else if ( !m_keyPrivateKey.IsValid() )
{
V_strcpy_safe( errMsg, "Cannot set cert. No private key?" );
return false;
}
if ( memcmp( msgCert.key_data().c_str(), m_keyPrivateKey.GetPublicKeyRawData(), 32 ) != 0 )
{
V_strcpy_safe( errMsg, "Cert public key does not match our private key" );
return false;
}
AppId_t nAppID = m_pSteamNetworkingUtils->GetAppID();
if ( !CheckCertAppID( msgCert, pAuthScope, nAppID, tempErrMsg ) )
{
V_sprintf_safe( errMsg, "Cert does not authorize us for App %u", nAppID );
return false;
}
if ( bSetIdentity )
{
m_identity = certIdentity;
SpewMsg( "Local identity established from certificate. We are '%s'\n", SteamNetworkingIdentityRender( m_identity ).c_str() );
}
m_msgSignedCert = std::move( msgCertSigned );
m_msgCert = std::move( msgCert );
AssertMsg( GetSecondsUntilCertExpiry() > 0, "Cert already invalid / expired?" );
if ( bSetIdentity )
InternalOnGotIdentity( nIdentitySetFlags | k_nIdentitySetFlag_NoLoadCert );
SetCertStatus( k_ESteamNetworkingAvailability_Current, "OK" );
AuthenticationNeeded();
return true;
}
void CSteamNetworkingSockets::ResetIdentity( const SteamNetworkingIdentity *pIdentity )
{
SteamNetworkingGlobalLock scopeLock( "ResetIdentity" );
KillConnections();
InternalClearIdentity();
if ( pIdentity )
{
m_identity = *pIdentity;
if ( !m_identity.IsInvalid() && !m_identity.IsLocalHost() )
{
int nIdentitySetFlags = k_nIdentitySetFlag_NoSave; InternalOnGotIdentity( nIdentitySetFlags );
}
}
}
ESteamNetworkingAvailability CSteamNetworkingSockets::InitAuthentication()
{
SteamNetworkingGlobalLock scopeLock( "InitAuthentication" );
AuthenticationNeeded();
return m_AuthenticationStatus.m_eAvail;
}
void CSteamNetworkingSockets::CheckAuthenticationPrerequisites( SteamNetworkingMicroseconds usecNow )
{
#ifdef STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT
SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
if ( !BCanRequestCert() )
return;
static bool s_bRecursionCheck = false;
if ( s_bRecursionCheck )
{
m_scheduleCheckRenewCert.EnsureMinScheduleTime( SteamNetworkingSockets_GetLocalTimestamp() + k_nMillion*10 );
return;
}
s_bRecursionCheck = true;
RunCodeAtScopeExit( s_bRecursionCheck = false );
bool bInFlight = BCertRequestInFlight();
if ( m_msgSignedCert.has_cert() )
{
int nSeconduntilExpiry = GetSecondsUntilCertExpiry();
if ( nSeconduntilExpiry < 0 )
{
SpewMsg( "Cert expired %d seconds ago. Discarding and requesting another\n", -nSeconduntilExpiry );
m_msgSignedCert.Clear();
m_msgCert.Clear();
m_keyPrivateKey.Wipe();
SetCertStatus( k_ESteamNetworkingAvailability_Previously, "Expired" );
}
else
{
if ( bInFlight )
return;
int nSecondsUntilRenew = nSeconduntilExpiry - k_nSecCertExpirySeekRenew;
if ( nSecondsUntilRenew > 0 )
{
constexpr SteamNetworkingMicroseconds kFudge = k_nMillion*3/2;
SteamNetworkingMicroseconds usecTargetCheck = std::min( usecNow + nSecondsUntilRenew*k_nMillion + kFudge, usecNow + 600*k_nMillion );
SteamNetworkingMicroseconds usecScheduledCheck = m_scheduleCheckRenewCert.GetScheduleTime();
if ( usecScheduledCheck <= usecTargetCheck + kFudge*2 )
{
}
else
{
m_scheduleCheckRenewCert.Schedule( usecTargetCheck );
long long expiry = m_msgCert.time_expiry(); Assert( expiry > 0 );
long long now = m_pSteamNetworkingUtils->GetTimeSecure();
long long nSecondsUntilExpiry = expiry - now;
SpewMsg( "Certificate expires in %lldh%02lldm at %lld (current time %lld), will renew in %dh%02dm\n",
nSecondsUntilExpiry/3600, ( nSecondsUntilExpiry/60 ) % 60, expiry, now,
nSecondsUntilRenew/3600, (nSecondsUntilRenew/60)%60 );
}
return;
}
SpewMsg( "Cert expires in %d seconds. Requesting another, but keeping current cert in case request fails\n", nSeconduntilExpiry );
}
}
if ( bInFlight )
return;
BeginFetchCertAsync();
#endif
}
void CSteamNetworkingSockets::SetCertStatus( ESteamNetworkingAvailability eAvail, const char *pszFmt, ... )
{
char msg[ sizeof(m_CertStatus.m_debugMsg) ];
va_list ap;
va_start( ap, pszFmt );
V_vsprintf_safe( msg, pszFmt, ap );
va_end( ap );
if ( eAvail == k_ESteamNetworkingAvailability_Current )
m_bEverGotCert = true;
if ( eAvail == k_ESteamNetworkingAvailability_Attempting || eAvail == k_ESteamNetworkingAvailability_Retrying )
m_bEverTriedToGetCert = true;
if ( eAvail == k_ESteamNetworkingAvailability_Failed && m_bEverGotCert )
eAvail = k_ESteamNetworkingAvailability_Previously;
if ( m_CertStatus.m_eAvail == eAvail && V_stricmp( m_CertStatus.m_debugMsg, msg ) == 0 )
return;
m_CertStatus.m_eAvail = eAvail;
V_strcpy_safe( m_CertStatus.m_debugMsg, msg );
DeduceAuthenticationStatus();
}
void CSteamNetworkingSockets::DeduceAuthenticationStatus()
{
SetAuthenticationStatus( m_CertStatus );
}
void CSteamNetworkingSockets::SetAuthenticationStatus( const SteamNetAuthenticationStatus_t &newStatus )
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
bool bStatusChanged = newStatus.m_eAvail != m_AuthenticationStatus.m_eAvail;
if ( !bStatusChanged && V_strcmp( m_AuthenticationStatus.m_debugMsg, newStatus.m_debugMsg ) == 0 )
return;
m_AuthenticationStatus = newStatus;
InternalGetIdentity();
if ( bStatusChanged )
{
SpewMsg( "AuthStatus (%s): %s (%s)",
SteamNetworkingIdentityRender( m_identity ).c_str(),
GetAvailabilityString( m_AuthenticationStatus.m_eAvail ), m_AuthenticationStatus.m_debugMsg );
QueueCallback( m_AuthenticationStatus, GlobalConfig::Callback_AuthStatusChanged.Get() );
}
}
#ifdef STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT
void CSteamNetworkingSockets::AsyncCertRequestFinished()
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "AsyncCertRequestFinished" );
Assert( m_msgSignedCert.has_cert() );
SetCertStatus( k_ESteamNetworkingAvailability_Current, "OK" );
m_scheduleCheckRenewCert.Cancel();
CheckAuthenticationPrerequisites( SteamNetworkingSockets_GetLocalTimestamp() );
TableScopeLock tableScopeLock( g_tables_lock );
for ( CSteamNetworkConnectionBase *pConn: g_mapConnections.IterValues() )
{
if ( pConn->m_pSteamNetworkingSocketsInterface == this )
pConn->InterfaceGotCert();
}
}
void CSteamNetworkingSockets::CertRequestFailed( ESteamNetworkingAvailability eCertAvail, ESteamNetConnectionEnd nConnectionEndReason, const char *pszMsg )
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "CertRequestFailed" );
SpewWarning( "Cert request for %s failed with reason code %d. %s\n", SteamNetworkingIdentityRender( InternalGetIdentity() ).c_str(), nConnectionEndReason, pszMsg );
m_scheduleCheckRenewCert.Schedule( SteamNetworkingSockets_GetLocalTimestamp() + k_nMillion*30 );
if ( m_msgSignedCert.has_cert() )
{
SpewMsg( "But we still have a valid cert, continuing with that one\n" );
AsyncCertRequestFinished();
return;
}
SetCertStatus( eCertAvail, "%s", pszMsg );
TableScopeLock tableScopeLock( g_tables_lock );
for ( CSteamNetworkConnectionBase *pConn: g_mapConnections.IterValues() )
{
if ( pConn->m_pSteamNetworkingSocketsInterface == this )
pConn->CertRequestFailed( nConnectionEndReason, pszMsg );
}
}
#endif
ESteamNetworkingAvailability CSteamNetworkingSockets::GetAuthenticationStatus( SteamNetAuthenticationStatus_t *pDetails )
{
SteamNetworkingGlobalLock scopeLock;
if ( pDetails )
*pDetails = m_AuthenticationStatus;
return m_AuthenticationStatus.m_eAvail;
}
HSteamListenSocket CSteamNetworkingSockets::CreateListenSocketIP( const SteamNetworkingIPAddr &localAddr, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
{
SteamNetworkingGlobalLock scopeLock( "CreateListenSocketIP" );
SteamDatagramErrMsg errMsg;
CSteamNetworkListenSocketBase *pResult = InternalCreateListenSocketIP( localAddr, nOptions, pOptions, errMsg );
if ( pResult )
return pResult->m_hListenSocketSelf;
SpewError( "Cannot create listen socket. %s", errMsg );
return k_HSteamListenSocket_Invalid;
}
CSteamNetworkListenSocketBase *CSteamNetworkingSockets::InternalCreateListenSocketIP( const SteamNetworkingIPAddr &localAddr, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg )
{
CSteamNetworkListenSocketDirectUDP *pSock = new CSteamNetworkListenSocketDirectUDP( this );
if ( !pSock )
{
V_strcpy_safe( errMsg, "new failed" );
return nullptr;
}
if ( !pSock->BInit( localAddr, nOptions, pOptions, errMsg ) )
{
pSock->Destroy();
return nullptr;
}
return pSock;
}
HSteamNetConnection CSteamNetworkingSockets::ConnectByIPAddress( const SteamNetworkingIPAddr &address, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
{
SteamNetworkingGlobalLock scopeLock( "ConnectByIPAddress" );
ConnectionScopeLock connectionLock;
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_FAKEIP
if ( address.IsFakeIP() )
{
for ( int idxOpt = 0 ; idxOpt < nOptions ; ++idxOpt )
{
if ( pOptions[idxOpt].m_eValue == k_ESteamNetworkingConfig_LocalVirtualPort )
{
SpewBug( "Cannot specify LocalVirtualPort when connecting by FakeIP" );
return k_HSteamNetConnection_Invalid;
}
}
SteamNetworkingIdentity identityRemote;
identityRemote.SetIPAddr( address );
int nRemoveVirtualPort = -1; CSteamNetworkConnectionBase *pConn = InternalConnectP2PDefaultSignaling( identityRemote, nRemoveVirtualPort, nOptions, pOptions, connectionLock );
if ( !pConn )
return k_HSteamNetConnection_Invalid;
return pConn->m_hConnectionSelf;
}
#endif
CSteamNetworkConnectionUDP *pConn = new CSteamNetworkConnectionUDP( this, connectionLock );
if ( !pConn )
return k_HSteamNetConnection_Invalid;
SteamDatagramErrMsg errMsg;
if ( !pConn->BInitConnect( address, nOptions, pOptions, errMsg ) )
{
SpewError( "Cannot create IPv4 connection. %s", errMsg );
pConn->ConnectionQueueDestroy();
return k_HSteamNetConnection_Invalid;
}
return pConn->m_hConnectionSelf;
}
EResult CSteamNetworkingSockets::AcceptConnection( HSteamNetConnection hConn )
{
SteamNetworkingGlobalLock scopeLock( "AcceptConnection" ); ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, nullptr );
if ( !pConn )
{
SpewError( "Cannot accept connection #%u; invalid connection handle", hConn );
return k_EResultInvalidParam;
}
return pConn->APIAcceptConnection();
}
bool CSteamNetworkingSockets::CloseConnection( HSteamNetConnection hConn, int nReason, const char *pszDebug, bool bEnableLinger )
{
SteamNetworkingGlobalLock scopeLock( "CloseConnection" ); ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, nullptr );
if ( !pConn )
return false;
pConn->APICloseConnection( nReason, pszDebug, bEnableLinger );
return true;
}
bool CSteamNetworkingSockets::CloseListenSocket( HSteamListenSocket hSocket )
{
SteamNetworkingGlobalLock scopeLock( "CloseListenSocket" ); CSteamNetworkListenSocketBase *pSock = GetListenSocketByHandle( hSocket );
if ( !pSock )
return false;
pSock->Destroy();
return true;
}
bool CSteamNetworkingSockets::SetConnectionUserData( HSteamNetConnection hPeer, int64 nUserData )
{
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hPeer, connectionLock, "SetConnectionUserData" );
if ( !pConn )
return false;
pConn->SetUserData( nUserData );
return true;
}
int64 CSteamNetworkingSockets::GetConnectionUserData( HSteamNetConnection hPeer )
{
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hPeer, connectionLock, "GetConnectionUserData" );
if ( !pConn )
return -1;
return pConn->GetUserData();
}
void CSteamNetworkingSockets::SetConnectionName( HSteamNetConnection hPeer, const char *pszName )
{
SteamNetworkingGlobalLock scopeLock( "SetConnectionName" ); ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hPeer, connectionLock, nullptr );
if ( !pConn )
return;
pConn->SetAppName( pszName );
}
bool CSteamNetworkingSockets::GetConnectionName( HSteamNetConnection hPeer, char *pszName, int nMaxLen )
{
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hPeer, connectionLock, "GetConnectionName" );
if ( !pConn )
return false;
V_strncpy( pszName, pConn->GetAppName(), nMaxLen );
return true;
}
EResult CSteamNetworkingSockets::ConfigureConnectionLanes( HSteamNetConnection hConn, int nNumLanes, const int *pLanePriorities, const uint16 *pLaneWeights )
{
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "ConfigureConnectionLanes" );
if ( !pConn )
return k_EResultNoConnection;
if ( !pConn->BStateIsActive() )
return k_EResultInvalidState;
return pConn->SNP_ConfigureLanes( nNumLanes, pLanePriorities, pLaneWeights );
}
EResult CSteamNetworkingSockets::SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, int nSendFlags, int64 *pOutMessageNumber )
{
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "SendMessageToConnection" );
if ( !pConn )
return k_EResultInvalidParam;
return pConn->APISendMessageToConnection( pData, cbData, nSendFlags, pOutMessageNumber );
}
void CSteamNetworkingSockets::SendMessages( int nMessages, SteamNetworkingMessage_t **pMessages, int64 *pOutMessageNumberOrResult, bool bDeleteFailedMessages )
{
struct SortMsg_t
{
HSteamNetConnection m_hConn;
int m_idx;
inline bool operator<(const SortMsg_t &x ) const
{
if ( m_hConn < x.m_hConn ) return true;
if ( m_hConn > x.m_hConn ) return false;
return m_idx < x.m_idx;
}
};
SortMsg_t *pSortMessages = (SortMsg_t *)alloca( nMessages * sizeof(SortMsg_t) );
int nSortMessages = 0;
for ( int i = 0 ; i < nMessages ; ++i )
{
CSteamNetworkingMessage *pMsg = static_cast<CSteamNetworkingMessage*>( pMessages[i] );
if ( !pMsg )
{
if ( pOutMessageNumberOrResult )
pOutMessageNumberOrResult[i] = -k_EResultInvalidParam;
continue;
}
if ( pMsg->m_conn == k_HSteamNetConnection_Invalid )
{
if ( pOutMessageNumberOrResult )
pOutMessageNumberOrResult[i] = -k_EResultInvalidParam;
if ( bDeleteFailedMessages )
pMsg->Release();
continue;
}
pSortMessages[ nSortMessages ].m_hConn = pMsg->m_conn;
pSortMessages[ nSortMessages ].m_idx = i;
++nSortMessages;
}
if ( nSortMessages < 1 )
return;
SortMsg_t *const pSortEnd = pSortMessages+nSortMessages;
std::sort( pSortMessages, pSortEnd );
SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp();
CSteamNetworkConnectionBase *pConn = nullptr;
HSteamNetConnection hConn = k_HSteamNetConnection_Invalid;
ConnectionScopeLock connectionLock;
bool bConnectionThinkImmediately = false;
bool bCurrentConnectionFailed = false;
for ( SortMsg_t *pSort = pSortMessages ; pSort < pSortEnd ; ++pSort )
{
if ( hConn != pSort->m_hConn )
{
if ( pConn )
{
if ( bConnectionThinkImmediately )
pConn->CheckConnectionStateOrScheduleWakeUp( usecNow );
connectionLock.Unlock();
bConnectionThinkImmediately = false;
}
hConn = pSort->m_hConn;
pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "SendMessages" );
bCurrentConnectionFailed = false;
}
const int idx = pSort->m_idx;
CSteamNetworkingMessage *pMsg = static_cast<CSteamNetworkingMessage*>( pMessages[idx] );
int64 result;
if ( bCurrentConnectionFailed )
{
result = 0;
}
else if ( pConn )
{
bool bThinkImmediately = false;
result = pConn->APISendMessageToConnection( pMsg, usecNow, &bThinkImmediately );
if ( bThinkImmediately )
bConnectionThinkImmediately = true;
if ( result > 0 )
{
if ( !bDeleteFailedMessages )
pMessages[idx] = nullptr;
}
}
else
{
result = -k_EResultInvalidParam;
}
if ( result <= 0 )
{
bCurrentConnectionFailed = true;
if ( bDeleteFailedMessages )
pMsg->Release();
}
if ( pOutMessageNumberOrResult )
pOutMessageNumberOrResult[idx] = result;
}
if ( bConnectionThinkImmediately )
pConn->CheckConnectionStateOrScheduleWakeUp( usecNow );
}
EResult CSteamNetworkingSockets::FlushMessagesOnConnection( HSteamNetConnection hConn )
{
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "FlushMessagesOnConnection" );
if ( !pConn )
return k_EResultInvalidParam;
return pConn->APIFlushMessageOnConnection();
}
int CSteamNetworkingSockets::ReceiveMessagesOnConnection( HSteamNetConnection hConn, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
{
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "ReceiveMessagesOnConnection" );
if ( !pConn )
return -1;
return pConn->APIReceiveMessages( ppOutMessages, nMaxMessages );
}
HSteamNetPollGroup CSteamNetworkingSockets::CreatePollGroup()
{
SteamNetworkingGlobalLock scopeLock( "CreatePollGroup" ); PollGroupScopeLock pollGroupScopeLock;
CSteamNetworkPollGroup *pPollGroup = InternalCreatePollGroup( pollGroupScopeLock );
return pPollGroup->m_hPollGroupSelf;
}
CSteamNetworkPollGroup *CSteamNetworkingSockets::InternalCreatePollGroup( PollGroupScopeLock &scopeLock )
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
TableScopeLock tableScopeLock( g_tables_lock );
CSteamNetworkPollGroup *pPollGroup = new CSteamNetworkPollGroup( this );
scopeLock.Lock( pPollGroup->m_lock );
pPollGroup->AssignHandleAndAddToGlobalTable();
return pPollGroup;
}
bool CSteamNetworkingSockets::DestroyPollGroup( HSteamNetPollGroup hPollGroup )
{
SteamNetworkingGlobalLock scopeLock( "DestroyPollGroup" ); TableScopeLock tableScopeLock( g_tables_lock ); PollGroupScopeLock pollGroupLock;
CSteamNetworkPollGroup *pPollGroup = GetPollGroupByHandle( hPollGroup, pollGroupLock, nullptr );
if ( !pPollGroup )
return false;
pollGroupLock.Abandon(); delete pPollGroup;
return true;
}
bool CSteamNetworkingSockets::SetConnectionPollGroup( HSteamNetConnection hConn, HSteamNetPollGroup hPollGroup )
{
SteamNetworkingGlobalLock scopeLock( "SetConnectionPollGroup" ); ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, nullptr );
if ( !pConn )
return false;
if ( hPollGroup == k_HSteamNetPollGroup_Invalid )
{
pConn->RemoveFromPollGroup();
return true;
}
PollGroupScopeLock pollGroupLock;
CSteamNetworkPollGroup *pPollGroup = GetPollGroupByHandle( hPollGroup, pollGroupLock, nullptr );
if ( !pPollGroup )
return false;
pConn->SetPollGroup( pPollGroup );
return true;
}
int CSteamNetworkingSockets::ReceiveMessagesOnPollGroup( HSteamNetPollGroup hPollGroup, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
{
PollGroupScopeLock pollGroupLock;
CSteamNetworkPollGroup *pPollGroup = GetPollGroupByHandle( hPollGroup, pollGroupLock, "ReceiveMessagesOnPollGroup" );
if ( !pPollGroup )
return -1;
g_lockAllRecvMessageQueues.lock();
int nMessagesReceived = pPollGroup->m_queueRecvMessages.RemoveMessages( ppOutMessages, nMaxMessages );
g_lockAllRecvMessageQueues.unlock();
return nMessagesReceived;
}
#ifdef STEAMNETWORKINGSOCKETS_STEAMCLIENT
int CSteamNetworkingSockets::ReceiveMessagesOnListenSocketLegacyPollGroup( HSteamListenSocket hSocket, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
{
SteamNetworkingGlobalLock scopeLock( "ReceiveMessagesOnListenSocket" );
CSteamNetworkListenSocketBase *pSock = GetListenSocketByHandle( hSocket );
if ( !pSock )
return -1;
if ( !pSock->m_pLegacyPollGroup )
return 0;
g_lockAllRecvMessageQueues.lock();
int nMessagesReceived = pSock->m_pLegacyPollGroup->m_queueRecvMessages.RemoveMessages( ppOutMessages, nMaxMessages );
g_lockAllRecvMessageQueues.unlock();
return nMessagesReceived;
}
#endif
bool CSteamNetworkingSockets::GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo_t *pInfo )
{
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "GetConnectionInfo" );
if ( !pConn )
return false;
if ( pInfo )
pConn->ConnectionPopulateInfo( *pInfo );
return true;
}
EResult CSteamNetworkingSockets::GetConnectionRealTimeStatus(
HSteamNetConnection hConn,
SteamNetConnectionRealTimeStatus_t *pStatus,
int nLanes, SteamNetConnectionRealTimeLaneStatus_t *pLanes
) {
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "GetConnectionRealTimeStatus" );
if ( !pConn )
return k_EResultNoConnection;
return pConn->APIGetRealTimeStatus( pStatus, nLanes, pLanes );
}
int CSteamNetworkingSockets::GetDetailedConnectionStatus( HSteamNetConnection hConn, char *pszBuf, int cbBuf )
{
SteamNetworkingDetailedConnectionStatus stats;
{
SteamNetworkingGlobalLock scopeLock( "GetDetailedConnectionStatus" ); ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, nullptr );
if ( !pConn )
return -1;
pConn->APIGetDetailedConnectionStatus( stats, SteamNetworkingSockets_GetLocalTimestamp() );
} int r = stats.Print( pszBuf, cbBuf );
if ( r > 0 )
r += 1024;
return r;
}
bool CSteamNetworkingSockets::GetListenSocketAddress( HSteamListenSocket hSocket, SteamNetworkingIPAddr *pAddress )
{
SteamNetworkingGlobalLock scopeLock( "GetListenSocketAddress" );
CSteamNetworkListenSocketBase *pSock = GetListenSocketByHandle( hSocket );
if ( !pSock )
return false;
return pSock->APIGetAddress( pAddress );
}
bool CSteamNetworkingSockets::CreateSocketPair( HSteamNetConnection *pOutConnection1, HSteamNetConnection *pOutConnection2, bool bUseNetworkLoopback, const SteamNetworkingIdentity *pPeerIdentity1, const SteamNetworkingIdentity *pPeerIdentity2 )
{
SteamNetworkingGlobalLock scopeLock( "CreateSocketPair" );
*pOutConnection1 = k_HSteamNetConnection_Invalid;
*pOutConnection2 = k_HSteamNetConnection_Invalid;
SteamNetworkingIdentity peerIdentity[ 2 ] = {};
if ( pPeerIdentity1 )
peerIdentity[0] = *pPeerIdentity1;
else
peerIdentity[0].SetLocalHost();
if ( pPeerIdentity2 )
peerIdentity[1] = *pPeerIdentity2;
else
peerIdentity[1].SetLocalHost();
if ( bUseNetworkLoopback )
{
CSteamNetworkConnectionlocalhostLoopback *pConn[2];
if ( !CSteamNetworkConnectionlocalhostLoopback::APICreateSocketPair( this, pConn, peerIdentity ) )
return false;
*pOutConnection1 = pConn[0]->m_hConnectionSelf;
*pOutConnection2 = pConn[1]->m_hConnectionSelf;
}
else
{
CSteamNetworkConnectionPipe *pConn[2];
if ( !CSteamNetworkConnectionPipe::APICreateSocketPair( this, pConn, peerIdentity ) )
return false;
*pOutConnection1 = pConn[0]->m_hConnectionSelf;
*pOutConnection2 = pConn[1]->m_hConnectionSelf;
}
return true;
}
bool CSteamNetworkingSockets::BCertHasIdentity() const
{
Assert( m_msgSignedCert.has_cert() );
Assert( m_msgCert.has_key_data() );
return m_msgCert.has_identity_string() || m_msgCert.has_legacy_identity_binary() || m_msgCert.has_legacy_steam_id();
}
bool CSteamNetworkingSockets::BMatchesIdentity( const SteamNetworkingIdentity &identity )
{
if ( identity == InternalGetIdentity() )
return true;
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_FAKEIP
if ( identity.IsFakeIP() )
{
if ( GetFakePortIndex( identity.m_ip ) >= 0 )
return true;
}
#endif
return false;
}
bool CSteamNetworkingSockets::SetCertificateAndPrivateKey( const void *pCert, int cbCert, void *pPrivateKey, int cbPrivateKey )
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "SetCertificateAndPrivateKey" );
m_msgCert.Clear();
m_msgSignedCert.Clear();
m_keyPrivateKey.Wipe();
if ( !m_keyPrivateKey.LoadFromAndWipeBuffer( pPrivateKey, cbPrivateKey ) )
{
SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Invalid private key" );
return false;
}
SteamNetworkingErrMsg parseErrMsg;
if ( !ParseCertFromPEM( pCert, cbCert, m_msgSignedCert, parseErrMsg ) )
{
SetCertStatus( k_ESteamNetworkingAvailability_Failed, parseErrMsg );
return false;
}
if (
!m_msgSignedCert.has_cert()
|| !m_msgCert.ParseFromString( m_msgSignedCert.cert() )
|| !m_msgCert.has_time_expiry()
|| !m_msgCert.has_key_data()
) {
SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Invalid cert" );
return false;
}
if ( m_msgCert.key_type() != CMsgSteamDatagramCertificate_EKeyType_ED25519 )
{
SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Invalid cert or unsupported public key type" );
return false;
}
CECSigningPublicKey pubKey;
if ( !pubKey.SetRawDataWithoutWipingInput( m_msgCert.key_data().c_str(), m_msgCert.key_data().length() ) )
{
SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Invalid public key" );
return false;
}
if ( !m_keyPrivateKey.MatchesPublicKey( pubKey ) )
{
SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Private key doesn't match public key from cert" );
return false;
}
SetCertStatus( k_ESteamNetworkingAvailability_Current, "OK" );
return true;
}
int CSteamNetworkingSockets::GetP2P_Transport_ICE_Enable( const SteamNetworkingIdentity &identityRemote, int *pOutUserFlags )
{
Assert( false );
if ( pOutUserFlags )
*pOutUserFlags = 0;
return k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Disable;
}
void CSteamNetworkingSockets::RunCallbacks()
{
std_vector<QueuedCallback> listTemp;
m_mutexPendingCallbacks.lock();
listTemp.swap( m_vecPendingCallbacks );
m_mutexPendingCallbacks.unlock();
for ( QueuedCallback &x: listTemp )
{
#define DISPATCH_CALLBACK( structType, fnType ) \
case structType::k_iCallback: \
COMPILE_TIME_ASSERT( sizeof(structType) <= sizeof(x.data) ); \
((fnType)x.fnCallback)( (structType*)x.data ); \
break; \
switch ( x.nCallback )
{
DISPATCH_CALLBACK( SteamNetConnectionStatusChangedCallback_t, FnSteamNetConnectionStatusChanged )
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR
DISPATCH_CALLBACK( SteamNetAuthenticationStatus_t, FnSteamNetAuthenticationStatusChanged )
DISPATCH_CALLBACK( SteamRelayNetworkStatus_t, FnSteamRelayNetworkStatusChanged )
#endif
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
DISPATCH_CALLBACK( SteamNetworkingMessagesSessionRequest_t, FnSteamNetworkingMessagesSessionRequest )
DISPATCH_CALLBACK( SteamNetworkingMessagesSessionFailed_t, FnSteamNetworkingMessagesSessionFailed )
#endif
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_FAKEIP
DISPATCH_CALLBACK( SteamNetworkingFakeIPResult_t, FnSteamNetworkingFakeIPResult )
#endif
default:
AssertMsg1( false, "Unknown callback type %d!", x.nCallback );
}
#undef DISPATCH_CALLBACK
}
}
void CSteamNetworkingSockets::InternalQueueCallback( int nCallback, int cbCallback, const void *pvCallback, void *fnRegisteredFunctionPtr )
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
if ( !fnRegisteredFunctionPtr )
return;
if ( cbCallback > sizeof( ((QueuedCallback*)0)->data ) )
{
AssertMsg( false, "Callback doesn't fit!" );
return;
}
m_mutexPendingCallbacks.lock();
AssertMsg( len( m_vecPendingCallbacks ) < 100, "Callbacks backing up and not being checked. Need to check them more frequently!" );
QueuedCallback &q = *push_back_get_ptr( m_vecPendingCallbacks );
q.nCallback = nCallback;
q.fnCallback = fnRegisteredFunctionPtr;
memcpy( q.data, pvCallback, cbCallback );
m_mutexPendingCallbacks.unlock();
}
#ifndef STEAMNETWORKINGSOCKETS_ENABLE_FAKEIP
bool CSteamNetworkingSockets::BeginAsyncRequestFakeIP( int nNumPorts )
{
AssertMsg( false, "FakeIP allocation requires Steam" );
return false;
}
void CSteamNetworkingSockets::GetFakeIP( int idxFirstPort, SteamNetworkingFakeIPResult_t *pInfo )
{
AssertMsg( false, "FakeIP allocation requires Steam" );
}
ISteamNetworkingFakeUDPPort *CSteamNetworkingSockets::CreateFakeUDPPort( int idxFakeServerPort )
{
AssertMsg( false, "FakeIP system requires Steam" );
return nullptr;
}
#endif
EResult CSteamNetworkingSockets::GetRemoteFakeIPForConnection( HSteamNetConnection hConn, SteamNetworkingIPAddr *pOutAddr )
{
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "GetRemoteFakeIPForConnection" );
if ( !pConn )
return k_EResultInvalidParam;
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_FAKEIP
if ( pConn->m_identityRemote.IsFakeIP() )
{
if ( pOutAddr )
*pOutAddr = pConn->m_identityRemote.m_ip;
return k_EResultOK;
}
if ( pConn->m_fakeIPRefRemote.GetInfo( nullptr, pOutAddr ) )
return k_EResultOK;
#endif
return k_EResultIPNotFound;
}
CSteamNetworkingUtils::CSteamNetworkingUtils()
{
SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "CSteamNetworkingUtils::CSteamNetworkingUtils" );
EnsureConfigValueTableInitted();
}
CSteamNetworkingUtils::~CSteamNetworkingUtils() {}
SteamNetworkingMessage_t *CSteamNetworkingUtils::AllocateMessage( int cbAllocateBuffer )
{
return CSteamNetworkingMessage::New( cbAllocateBuffer );
}
SteamNetworkingMicroseconds CSteamNetworkingUtils::GetLocalTimestamp()
{
return SteamNetworkingSockets_GetLocalTimestamp();
}
void CSteamNetworkingUtils::SetDebugOutputFunction( ESteamNetworkingSocketsDebugOutputType eDetailLevel, FSteamNetworkingSocketsDebugOutput pfnFunc )
{
SteamNetworkingSockets_SetDebugOutputFunction( eDetailLevel, pfnFunc );
}
template<typename T>
static ConfigValue<T> *GetConnectionVar( const GlobalConfigValueEntry *pEntry, ConnectionConfig *pConnectionConfig )
{
Assert( pEntry->m_eScope == k_ESteamNetworkingConfig_Connection );
intptr_t ptr = intptr_t( pConnectionConfig );
return (ConfigValue<T> *)( ptr + pEntry->m_cbOffsetOf );
}
template<typename T>
static ConfigValue<T> *EvaluateScopeConfigValue( GlobalConfigValueEntry *pEntry,
ESteamNetworkingConfigScope eScopeType,
intptr_t scopeObj,
ConnectionScopeLock &connectionLock )
{
switch ( eScopeType )
{
case k_ESteamNetworkingConfig_Global:
{
auto *pGlobalVal = static_cast< GlobalConfigValueBase<T> * >( pEntry );
return &pGlobalVal->m_value;
}
case k_ESteamNetworkingConfig_SocketsInterface:
{
CSteamNetworkingSockets *pInterface = (CSteamNetworkingSockets *)scopeObj;
if ( pEntry->m_eScope == k_ESteamNetworkingConfig_Connection )
{
return GetConnectionVar<T>( pEntry, &pInterface->m_connectionConfig );
}
break;
}
case k_ESteamNetworkingConfig_ListenSocket:
{
CSteamNetworkListenSocketBase *pSock = GetListenSocketByHandle( HSteamListenSocket( scopeObj ) );
if ( pSock )
{
if ( pEntry->m_eScope == k_ESteamNetworkingConfig_Connection )
{
return GetConnectionVar<T>( pEntry, &pSock->m_connectionConfig );
}
}
break;
}
case k_ESteamNetworkingConfig_Connection:
{
CSteamNetworkConnectionBase *pConn = GetConnectionByHandle( HSteamNetConnection( scopeObj ), connectionLock );
if ( pConn )
{
if ( pEntry->m_eScope == k_ESteamNetworkingConfig_Connection )
{
return GetConnectionVar<T>( pEntry, &pConn->m_connectionConfig );
}
}
break;
}
}
return nullptr;
}
static bool AssignConfigValueTyped( int32 *pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
{
switch ( eDataType )
{
case k_ESteamNetworkingConfig_Int32:
*pVal = *(int32*)pArg;
break;
case k_ESteamNetworkingConfig_Int64:
{
int64 arg = *(int64*)pArg;
if ( (int32)arg != arg )
return false; *pVal = (int32)arg;
break;
}
case k_ESteamNetworkingConfig_Float:
*pVal = (int32)floor( *(float*)pArg + .5f );
break;
case k_ESteamNetworkingConfig_String:
{
int x;
if ( sscanf( (const char *)pArg, "%d", &x ) != 1 )
return false;
*pVal = x;
break;
}
default:
return false;
}
return true;
}
static bool AssignConfigValueTyped( int64 *pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
{
switch ( eDataType )
{
case k_ESteamNetworkingConfig_Int32:
*pVal = *(int32*)pArg;
break;
case k_ESteamNetworkingConfig_Int64:
{
*pVal = *(int64*)pArg;
break;
}
case k_ESteamNetworkingConfig_Float:
*pVal = (int64)floor( *(float*)pArg + .5f );
break;
case k_ESteamNetworkingConfig_String:
{
long long x;
if ( sscanf( (const char *)pArg, "%lld", &x ) != 1 )
return false;
*pVal = (int64)x;
break;
}
default:
return false;
}
return true;
}
static bool AssignConfigValueTyped( float *pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
{
switch ( eDataType )
{
case k_ESteamNetworkingConfig_Int32:
*pVal = (float)( *(int32*)pArg );
break;
case k_ESteamNetworkingConfig_Int64:
{
*pVal = (float)( *(int64*)pArg );
break;
}
case k_ESteamNetworkingConfig_Float:
*pVal = *(float*)pArg;
break;
case k_ESteamNetworkingConfig_String:
{
float x;
if ( sscanf( (const char *)pArg, "%f", &x ) != 1 )
return false;
*pVal = x;
break;
}
default:
return false;
}
return true;
}
static bool AssignConfigValueTyped( std::string *pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
{
char temp[64];
switch ( eDataType )
{
case k_ESteamNetworkingConfig_Int32:
V_sprintf_safe( temp, "%d", *(int32*)pArg );
*pVal = temp;
break;
case k_ESteamNetworkingConfig_Int64:
V_sprintf_safe( temp, "%lld", (long long)*(int64*)pArg );
*pVal = temp;
break;
case k_ESteamNetworkingConfig_Float:
V_sprintf_safe( temp, "%g", *(float*)pArg );
*pVal = temp;
break;
case k_ESteamNetworkingConfig_String:
*pVal = (const char *)pArg;
break;
default:
return false;
}
return true;
}
static bool AssignConfigValueTyped( void **pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
{
switch ( eDataType )
{
case k_ESteamNetworkingConfig_Ptr:
*pVal = *(void **)pArg;
break;
default:
return false;
}
return true;
}
template<typename T>
bool SetConfigValueTyped(
GlobalConfigValueEntry *pEntry,
ESteamNetworkingConfigScope eScopeType,
intptr_t scopeObj,
ESteamNetworkingConfigDataType eDataType,
const void *pArg
) {
ConnectionScopeLock connectionLock;
ConfigValue<T> *pVal = EvaluateScopeConfigValue<T>( pEntry, eScopeType, scopeObj, connectionLock );
if ( !pVal )
return false;
if ( pVal->IsLocked() )
return false;
if ( pArg == nullptr )
{
if ( eScopeType == k_ESteamNetworkingConfig_Global )
{
auto *pGlobal = (typename GlobalConfigValueBase<T>::Value *)( pVal );
Assert( pGlobal->m_pInherit == nullptr );
Assert( pGlobal->IsSet() );
pGlobal->m_data = pGlobal->m_defaultValue;
}
else if ( eScopeType == k_ESteamNetworkingConfig_Connection && pEntry->m_eValue == k_ESteamNetworkingConfig_ConnectionUserData )
{
SpewError( "Cannot clear connection user data\n" );
return false;
}
else
{
Assert( pVal->m_pInherit );
pVal->m_eState = ConfigValueBase::kENotSet;
}
return true;
}
if ( !AssignConfigValueTyped( &pVal->m_data, eDataType, pArg ) )
return false;
pVal->m_eState = ConfigValueBase::kESet;
pEntry->Clamp<T>( pVal->m_data );
return true;
}
template<typename T>
ESteamNetworkingGetConfigValueResult ReturnConfigValueTyped( const T &data, void *pData, size_t *cbData )
{
ESteamNetworkingGetConfigValueResult eResult;
if ( !pData || *cbData < sizeof(T) )
{
eResult = k_ESteamNetworkingGetConfigValue_BufferTooSmall;
}
else
{
*(T*)pData = data;
eResult = k_ESteamNetworkingGetConfigValue_OK;
}
*cbData = sizeof(T);
return eResult;
}
template<>
ESteamNetworkingGetConfigValueResult ReturnConfigValueTyped<std::string>( const std::string &data, void *pData, size_t *cbData )
{
size_t l = data.length() + 1;
ESteamNetworkingGetConfigValueResult eResult;
if ( !pData || *cbData < l )
{
eResult = k_ESteamNetworkingGetConfigValue_BufferTooSmall;
}
else
{
memcpy( pData, data.c_str(), l );
eResult = k_ESteamNetworkingGetConfigValue_OK;
}
*cbData = l;
return eResult;
}
template<typename T>
ESteamNetworkingGetConfigValueResult GetConfigValueTyped(
GlobalConfigValueEntry *pEntry,
ESteamNetworkingConfigScope eScopeType,
intptr_t scopeObj,
void *pResult, size_t *cbResult
) {
ConnectionScopeLock connectionLock;
ConfigValue<T> *pVal = EvaluateScopeConfigValue<T>( pEntry, eScopeType, scopeObj, connectionLock );
if ( !pVal )
{
*cbResult = 0;
return k_ESteamNetworkingGetConfigValue_BadScopeObj;
}
bool bValWasSet = pVal->IsSet();
while ( !pVal->IsSet() )
{
Assert( pVal->m_pInherit );
pVal = static_cast<ConfigValue<T> *>( pVal->m_pInherit );
}
ESteamNetworkingGetConfigValueResult eResult = ReturnConfigValueTyped( pVal->m_data, pResult, cbResult );
if ( eResult == k_ESteamNetworkingGetConfigValue_OK && !bValWasSet )
eResult = k_ESteamNetworkingGetConfigValue_OKInherited;
return eResult;
}
bool CSteamNetworkingUtils::SetConfigValue( ESteamNetworkingConfigValue eValue,
ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj,
ESteamNetworkingConfigDataType eDataType, const void *pValue )
{
switch ( eValue )
{
case k_ESteamNetworkingConfig_MTU_DataSize:
SpewWarning( "MTU_DataSize is readonly" );
return false;
case k_ESteamNetworkingConfig_ConnectionUserData:
{
if ( eScopeType != k_ESteamNetworkingConfig_Connection )
break;
int64 newData;
if ( !AssignConfigValueTyped( &newData, eDataType, pValue ) )
return false;
ConnectionScopeLock connectionLock;
CSteamNetworkConnectionBase *pConn = GetConnectionByHandle( HSteamNetConnection( scopeObj ), connectionLock );
if ( !pConn )
return false;
pConn->SetUserData( newData );
return true;
}
}
GlobalConfigValueEntry *pEntry = FindConfigValueEntry( eValue );
if ( pEntry == nullptr )
return false;
SteamNetworkingGlobalLock scopeLock( "SetConfigValue" );
switch ( pEntry->m_eDataType )
{
case k_ESteamNetworkingConfig_Int32: return SetConfigValueTyped<int32>( pEntry, eScopeType, scopeObj, eDataType, pValue );
case k_ESteamNetworkingConfig_Int64: return SetConfigValueTyped<int64>( pEntry, eScopeType, scopeObj, eDataType, pValue );
case k_ESteamNetworkingConfig_Float: return SetConfigValueTyped<float>( pEntry, eScopeType, scopeObj, eDataType, pValue );
case k_ESteamNetworkingConfig_String: return SetConfigValueTyped<std::string>( pEntry, eScopeType, scopeObj, eDataType, pValue );
case k_ESteamNetworkingConfig_Ptr: return SetConfigValueTyped<void *>( pEntry, eScopeType, scopeObj, eDataType, pValue );
}
Assert( false );
return false;
}
ESteamNetworkingGetConfigValueResult CSteamNetworkingUtils::GetConfigValue(
ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType,
intptr_t scopeObj, ESteamNetworkingConfigDataType *pOutDataType,
void *pResult, size_t *cbResult )
{
SteamNetworkingGlobalLock scopeLock( "GetConfigValue" );
if ( eValue == k_ESteamNetworkingConfig_MTU_DataSize )
{
int32 MTU_packetsize;
size_t cbMTU_packetsize = sizeof(MTU_packetsize);
ESteamNetworkingGetConfigValueResult rFetch = GetConfigValueTyped<int32>( &GlobalConfig::MTU_PacketSize, eScopeType, scopeObj, &MTU_packetsize, &cbMTU_packetsize );
if ( rFetch < 0 )
return rFetch;
int32 MTU_DataSize = std::max( 0, MTU_packetsize - k_cbSteamNetworkingSocketsNoFragmentHeaderReserve );
ESteamNetworkingGetConfigValueResult rStore = ReturnConfigValueTyped<int32>( MTU_DataSize, pResult, cbResult );
if ( rStore != k_ESteamNetworkingGetConfigValue_OK )
return rStore;
return rFetch;
}
GlobalConfigValueEntry *pEntry = FindConfigValueEntry( eValue );
if ( pEntry == nullptr )
return k_ESteamNetworkingGetConfigValue_BadValue;
if ( pOutDataType )
*pOutDataType = pEntry->m_eDataType;
switch ( pEntry->m_eDataType )
{
case k_ESteamNetworkingConfig_Int32: return GetConfigValueTyped<int32>( pEntry, eScopeType, scopeObj, pResult, cbResult );
case k_ESteamNetworkingConfig_Int64: return GetConfigValueTyped<int64>( pEntry, eScopeType, scopeObj, pResult, cbResult );
case k_ESteamNetworkingConfig_Float: return GetConfigValueTyped<float>( pEntry, eScopeType, scopeObj, pResult, cbResult );
case k_ESteamNetworkingConfig_String: return GetConfigValueTyped<std::string>( pEntry, eScopeType, scopeObj, pResult, cbResult );
case k_ESteamNetworkingConfig_Ptr: return GetConfigValueTyped<void *>( pEntry, eScopeType, scopeObj, pResult, cbResult );
}
Assert( false ); return k_ESteamNetworkingGetConfigValue_BadValue;
}
static bool BEnumerateConfigValue( const GlobalConfigValueEntry *pVal, bool bEnumerateDevVars )
{
if ( pVal->m_eDataType == k_ESteamNetworkingConfig_Ptr )
return false;
switch ( pVal->m_eValue )
{
case k_ESteamNetworkingConfig_SymmetricConnect:
case k_ESteamNetworkingConfig_LocalVirtualPort:
case k_ESteamNetworkingConfig_ConnectionUserData:
return false;
case k_ESteamNetworkingConfig_IP_AllowWithoutAuth:
case k_ESteamNetworkingConfig_Unencrypted:
case k_ESteamNetworkingConfig_SDRClient_FakeClusterPing:
return bEnumerateDevVars;
}
return true;
}
const char *CSteamNetworkingUtils::GetConfigValueInfo( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigDataType *pOutDataType, ESteamNetworkingConfigScope *pOutScope )
{
const GlobalConfigValueEntry *pVal = FindConfigValueEntry( eValue );
if ( pVal == nullptr )
return nullptr;
if ( pOutDataType )
*pOutDataType = pVal->m_eDataType;
if ( pOutScope )
*pOutScope = pVal->m_eScope;
return pVal->m_pszName;
}
ESteamNetworkingConfigValue CSteamNetworkingUtils::IterateGenericEditableConfigValues( ESteamNetworkingConfigValue eCurrent, bool bEnumerateDevVars )
{
const GlobalConfigValueEntry *p;
if ( eCurrent == k_ESteamNetworkingConfig_Invalid )
{
p = s_vecConfigValueTable[0];
}
else
{
p = FindConfigValueEntry( eCurrent );
if ( p )
p = p->m_pNextEntry;
}
while ( p )
{
if ( BEnumerateConfigValue( p, bEnumerateDevVars ) )
return p->m_eValue;
p = p->m_pNextEntry;
}
return k_ESteamNetworkingConfig_Invalid;
}
void CSteamNetworkingUtils::SteamNetworkingIPAddr_ToString( const SteamNetworkingIPAddr &addr, char *buf, size_t cbBuf, bool bWithPort )
{
::SteamNetworkingIPAddr_ToString( &addr, buf, cbBuf, bWithPort );
}
bool CSteamNetworkingUtils::SteamNetworkingIPAddr_ParseString( SteamNetworkingIPAddr *pAddr, const char *pszStr )
{
return ::SteamNetworkingIPAddr_ParseString( pAddr, pszStr );
}
void CSteamNetworkingUtils::SteamNetworkingIdentity_ToString( const SteamNetworkingIdentity &identity, char *buf, size_t cbBuf )
{
return ::SteamNetworkingIdentity_ToString( &identity, buf, cbBuf );
}
bool CSteamNetworkingUtils::SteamNetworkingIdentity_ParseString( SteamNetworkingIdentity *pIdentity, const char *pszStr )
{
return ::SteamNetworkingIdentity_ParseString( pIdentity, sizeof(SteamNetworkingIdentity), pszStr );
}
ESteamNetworkingFakeIPType CSteamNetworkingUtils::GetIPv4FakeIPType( uint32 nIPv4 )
{
return SteamNetworkingSocketsLib::GetIPv4FakeIPType( nIPv4 );
}
EResult CSteamNetworkingUtils::GetRealIdentityForFakeIP( const SteamNetworkingIPAddr &fakeIP, SteamNetworkingIdentity *pOutRealIdentity )
{
return k_EResultDisabled;
}
ESteamNetworkingFakeIPType CSteamNetworkingUtils::SteamNetworkingIPAddr_GetFakeIPType( const SteamNetworkingIPAddr &addr )
{
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_FAKEIP
return ::SteamNetworkingIPAddr_GetFakeIPType( &addr );
#else
return k_ESteamNetworkingFakeIPType_NotFake;
#endif
}
AppId_t CSteamNetworkingUtils::GetAppID()
{
return m_nAppID;
}
void CSteamNetworkingUtils::TEST_ResetSelf()
{
m_nAppID = 0;
}
time_t CSteamNetworkingUtils::GetTimeSecure()
{
return time(nullptr);
}
const char *CSteamNetworkingUtils::GetBuildString()
{
#if defined( STEAMNETWORKINGSOCKETS_OPENSOURCE )
return "opensource " __DATE__ " " __TIME__;
#elif defined( STEAMNETWORKINGSOCKETS_STANDALONELIB )
return "lib " __DATE__ " " __TIME__;
#elif defined( STEAMNETWORKINGSOCKETS_STEAMCLIENT )
return "steam "
#ifdef BRANCH_MAIN
"(main) "
#elif !defined( BRANCH_REL_CLIENT )
"(branch?) "
#endif
__DATE__ " " __TIME__;
#elif defined( STEAMNETWORKINGSOCKETS_STREAMINGCLIENT )
return "stream "
#ifdef BRANCH_MAIN
"(main) "
#elif !defined( BRANCH_REL_CLIENT )
"(branch?) "
#endif
__DATE__ " " __TIME__;
#else
#error "Huh?"
#endif
}
const char *CSteamNetworkingUtils::GetPlatformString()
{
#if IsNintendoSwitch()
return "nswitch";
#elif IsXboxScarlett()
return "scarlett";
#elif defined( _STADIA )
return "stadia";
#elif IsXboxOne()
return "xbone";
#elif IsPS4()
return "ps4";
#elif IsPS5()
return "ps5";
#elif IsTVOS()
return "tvos";
#elif IsIOS()
return "ios";
#elif IsOSX()
return "osx";
#elif IsAndroid()
return "android";
#elif defined( _WINDOWS )
return "windows";
#elif IsLinux()
return "linux";
#elif IsFreeBSD()
return "freebsd";
#elif IsOpenBSD()
return "openbsd";
#else
#error "Unknown platform"
#endif
}
} using namespace SteamNetworkingSocketsLib;
#ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
static CSteamNetworkingSockets *s_pSteamNetworkingSockets = nullptr;
STEAMNETWORKINGSOCKETS_INTERFACE bool GameNetworkingSockets_Init( const SteamNetworkingIdentity *pIdentity, SteamNetworkingErrMsg &errMsg )
{
SteamNetworkingGlobalLock lock( "GameNetworkingSockets_Init" );
if ( s_pSteamNetworkingSockets )
{
AssertMsg( false, "GameNetworkingSockets_init called multiple times?" );
return true;
}
CSteamNetworkingSockets *pSteamNetworkingSockets = new CSteamNetworkingSockets( ( CSteamNetworkingUtils *)SteamNetworkingUtils() );
if ( !pSteamNetworkingSockets->BInitGameNetworkingSockets( pIdentity, errMsg ) )
{
pSteamNetworkingSockets->Destroy();
return false;
}
s_pSteamNetworkingSockets = pSteamNetworkingSockets;
return true;
}
STEAMNETWORKINGSOCKETS_INTERFACE void GameNetworkingSockets_Kill()
{
SteamNetworkingGlobalLock lock( "GameNetworkingSockets_Kill" );
if ( s_pSteamNetworkingSockets )
{
s_pSteamNetworkingSockets->Destroy();
s_pSteamNetworkingSockets = nullptr;
}
}
STEAMNETWORKINGSOCKETS_INTERFACE ISteamNetworkingSockets *SteamNetworkingSockets_LibV12()
{
return s_pSteamNetworkingSockets;
}
STEAMNETWORKINGSOCKETS_INTERFACE ISteamNetworkingUtils *SteamNetworkingUtils_LibV4()
{
static CSteamNetworkingUtils s_utils;
return &s_utils;
}
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
STEAMNETWORKINGSOCKETS_INTERFACE ISteamNetworkingMessages* SteamNetworkingMessages_LibV2()
{
if ( !s_pSteamNetworkingSockets )
return nullptr;
return s_pSteamNetworkingSockets->GetSteamNetworkingMessages();
}
#endif
#endif