diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /public/steamnetworkingsockets | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'public/steamnetworkingsockets')
| -rw-r--r-- | public/steamnetworkingsockets/isteamnetworkingsockets.h | 330 | ||||
| -rw-r--r-- | public/steamnetworkingsockets/isteamnetworkingutils.h | 125 | ||||
| -rw-r--r-- | public/steamnetworkingsockets/steamdatagram_stats.h | 298 | ||||
| -rw-r--r-- | public/steamnetworkingsockets/steamdatagram_ticketgen.h | 60 | ||||
| -rw-r--r-- | public/steamnetworkingsockets/steamdatagram_tickets.h | 151 | ||||
| -rw-r--r-- | public/steamnetworkingsockets/steamnetworkingtypes.h | 641 |
6 files changed, 1605 insertions, 0 deletions
diff --git a/public/steamnetworkingsockets/isteamnetworkingsockets.h b/public/steamnetworkingsockets/isteamnetworkingsockets.h new file mode 100644 index 0000000..b2d83ab --- /dev/null +++ b/public/steamnetworkingsockets/isteamnetworkingsockets.h @@ -0,0 +1,330 @@ +//====== Copyright Valve Corporation, All rights reserved. ==================== +// +// Purpose: A low level API similar to Berkeley socket, to send messages +// between hosts over the Steam network and addressed using Steam IDs. +// +//============================================================================= + +#ifndef ISTEAMNETWORKINGSOCKETS +#define ISTEAMNETWORKINGSOCKETS +#ifdef _WIN32 +#pragma once +#endif + +#include "steamnetworkingtypes.h" + +// #KLUDGE! This is so we don't have to link with steam_api.lib +#include <steam/steam_api.h> +#include <steam/steam_gameserver.h> + +//----------------------------------------------------------------------------- +/// Lower level networking interface that more closely mirrors the standard +/// Berkeley sockets model. Sockets are hard! You should probably only use +/// this interface under the existing circumstances: +/// +/// - You have an existing socket-based codebase you want to port, or coexist with. +/// - You want to be able to connect based on IP address, rather than (just) Steam ID. +/// - You need low-level control of bandwidth utilization, when to drop packets, etc. +/// +/// Note that neither of the terms "connection" and "socket" will correspond +/// one-to-one with an underlying UDP socket. An attempt has been made to +/// keep the semantics as similar to the standard socket model when appropriate, +/// but some deviations do exist. +class ISteamSocketNetworking +{ +public: + + /// Creates a "server" socket that listens for clients to connect to, either by calling + /// ConnectSocketBySteamID or ConnectSocketByIPv4Address. + /// + /// nSteamConnectVirtualPort specifies how clients can connect to this socket using + /// ConnectBySteamID. A negative value indicates that this functionality is + /// disabled and clients must connect by IP address. It's very common for applications + /// to only have one listening socket; in that case, use zero. If you need to open + /// multiple listen sockets and have clients be able to connect to one or the other, then + /// nSteamConnectVirtualPort should be a small integer constant unique to each listen socket + /// you create. + /// + /// If you want clients to connect to you by your IPv4 addresses using + /// ConnectByIPv4Address, then you must set nPort to be nonzero. Steam will + /// bind a UDP socket to the specified local port, and clients will send packets using + /// ordinary IP routing. It's up to you to take care of NAT, protecting your server + /// from DoS, etc. If you don't need clients to connect to you by IP, then set nPort=0. + /// Use nIP if you wish to bind to a particular local interface. Typically you will use 0, + /// which means to listen on all interfaces, and accept the default outbound IP address. + /// If nPort is zero, then nIP must also be zero. + /// + /// A SocketStatusCallback_t callback when another client attempts a connection. + virtual HSteamListenSocket CreateListenSocket( int nSteamConnectVirtualPort, uint32 nIP, uint16 nPort ) = 0; + + /// Creates a connection and begins talking to a remote destination. The remote host + /// must be listening with the appropriate call to CreateListenSocket. + /// + /// Use ConnectBySteamID to connect using the SteamID (client or game server) as the network address. + /// Use ConnectByIPv4Address to connect by IP address. + /// + /// On success, a SocketStatusCallback_t callback is triggered. + /// On failure or timeout, a SocketStatusCallback_t callback with a failure code in m_eSNetSocketState + virtual HSteamNetConnection ConnectBySteamID( CSteamID steamIDTarget, int nVirtualPort, int nTimeoutSec ) = 0; + virtual HSteamNetConnection ConnectByIPv4Address( uint32 nIP, uint16 nPort, int nTimeoutSec ) = 0; + + /// Accept an incoming connection that has been received on a listen socket. + /// + /// When a connection attempt is received (perhaps after a few basic handshake + /// packets have been exchanged to prevent trivial spoofing), a connection interface + /// object is created in the k_ESteamNetworkingConnectionState_Connecting state + /// and a SteamNetConnectionStatusChangedCallback_t is posted. At this point, your + /// application MUST either accept or close the connection. (It may not ignore it.) + /// Accepting the connection will transition it into the connected state. + /// + /// You should take action within a few seconds, because accepting the connection is + /// what actually sends the reply notifying the client that they are connected. If you + /// delay taking action, from the client's perspective it is the same as the network + /// being unresponsive, and the client may timeout the connection attempt. In other + /// words, the client cannot distinguish between a delay caused by network problems + /// and a delay caused by the application. + /// + /// This means that if your application goes for more than a few seconds without + /// processing callbacks, then there is a chance that a client may attempt to connect + /// in that interval, and timeout. + /// + /// If the application does not respond to the connection attempt in a timely manner, + /// and we stop receiving communication from the client, the connection attempt will + /// be timed out locally, transitioning the connection to the + /// k_ESteamNetworkingConnectionState_ProblemDetectedLocally state. The client may also + /// close the connection before it is accepted and a transition to the + /// k_ESteamNetworkingConnectionState_ClosedByPeer is also possible. + /// + /// Returns k_EResultInvalidParam if the handle is invalid. + /// 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.) + virtual EResult AcceptConnection( HSteamNetConnection hConn ) = 0; + + /// Disconnects from the remote host and invalidates the connection handle. + /// Any unread data on the connection is discarded. + /// + /// nReason is an application defined code that will be received on the other + /// end and recorded (when possible) in backend analytics. The value should + /// come from a restricted range. (See ESteamNetConnectionEnd.) If you don't need + /// to communicate any information to the remote host, and do not want analytics to + /// be able to distinguish "normal" connection terminations from "exceptional" ones, + /// You may pass zero, in which case the generic value of + /// k_ESteamNetConnectionEnd_App_Generic will be used. + /// + /// pszDebug is an optional human-readable diagnostic string that will be received + /// by the remote host and recorded (when possible) in backend analytics. + /// + /// If you wish to put the socket into a "linger" state, where an attempt is made to + /// flush any remaining sent data, use bEnableLinger=true. Otherwise reliable data + /// is not flushed. + /// + /// If the connection has already ended and you are just freeing up the + /// connection interface, the reason code, debug string, and linger flag are + /// ignored. + virtual bool CloseConnection( HSteamNetConnection hPeer, int nReason, const char *pszDebug, bool bEnableLinger ) = 0; + + /// Destroy a listen socket, and all the client sockets generated by accepting connections + /// on the listen socket. + /// + /// pszNotifyRemoteReason determines what cleanup actions are performed on the client + /// sockets being destroyed. (See DestroySocket for more details.) + /// + /// Note that if cleanup is requested and you have requested the listen socket bound to a + /// particular local port to facilitate direct UDP/IPv4 connections, then the underlying UDP + /// socket must remain open until all clients have been cleaned up. + virtual bool CloseListenSocket( HSteamListenSocket hSocket, const char *pszNotifyRemoteReason ) = 0; + + /// Set connection user data. Returns false if the handle is invalid. + virtual bool SetConnectionUserData( HSteamNetConnection hPeer, int64 nUserData ) = 0; + + /// Fetch connection user data. Returns -1 if handle is invalid + /// or if you haven't set any userdata on the connection. + virtual int64 GetConnectionUserData( HSteamNetConnection hPeer ) = 0; + + /// Set a name for the connection, used mostly for debugging + virtual void SetConnectionName( HSteamNetConnection hPeer, const char *pszName ) = 0; + + /// Fetch connection user data. Returns -1 if handle is invalid + /// or if you haven't set any userdata on the connection. + virtual void GetConnectionName( HSteamNetConnection hPeer, char *ppszName, int nMaxLen ) = 0; + + /// Send a message to the remote host on the connected socket. + /// + /// eSendType determines the delivery guarantees that will be provided, + /// when data should be buffered, etc. + /// + /// Note that the semantics we use for messages are not precisely + /// the same as the semantics of a standard "stream" socket. + /// (SOCK_STREAM) For an ordinary stream socket, the boundaries + /// between chunks are not considered relevant, and the sizes of + /// the chunks of data written will not necessarily match up to + /// the sizes of the chunks that are returned by the reads on + /// the other end. The remote host might read a partial chunk, + /// or chunks might be coalesced. For the message semantics + /// used here, however, the sizes WILL match. Each send call + /// will match a successful read call on the remote host + /// one-for-one. If you are porting existing stream-oriented + /// code to the semantics of reliable messages, your code should + /// work the same, since reliable message semantics are more + /// strict than stream semantics. The only caveat is related to + /// performance: there is per-message overhead to retain the + /// messages sizes, and so if your code sends many small chunks + /// of data, performance will suffer. Any code based on stream + /// sockets that does not write excessively small chunks will + /// work without any changes. + virtual EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, ESteamNetworkingSendType eSendType ) = 0; + + /// Fetch the next available message(s) from the socket, if any. + /// Returns the number of messages returned into your array, up to nMaxMessages. + /// If the connection handle is invalid, -1 is returned. + /// + /// The order of the messages returned in the array is relevant. + /// Reliable messages will be received in the order they were sent (and with the + /// same sizes --- see SendMessageToConnection for on this subtle difference from a stream socket). + /// + /// FIXME - We're still debating the exact set of guarantees for unreliable, so this might change. + /// Unreliable messages may not be received. The order of delivery of unreliable messages + /// is NOT specified. They may be received out of order with respect to each other or + /// reliable messages. They may be received multiple times! + /// + /// If any messages are returned, you MUST call Release() to each of them free up resources + /// after you are done. It is safe to keep the object alive for a little while (put it + /// into some queue, etc), and you may call Release() from any thread. + virtual int ReceiveMessagesOnConnection( HSteamNetConnection hConn, ISteamNetworkingMessage **ppOutMessages, int nMaxMessages ) = 0; + + /// Same as ReceiveMessagesOnConnection, but will return the next message available + /// on any client socket that was accepted through the specified listen socket. Use + /// ISteamNetworkingMessage::GetConnection to know which client connection. + /// + /// Delivery order of messages among different clients is not defined. They may + /// be returned in an order different from what they were actually received. (Delivery + /// order of messages from the same client is well defined, and thus the order of the + /// messages is relevant!) + virtual int ReceiveMessagesOnListenSocket( HSteamListenSocket hSocket, ISteamNetworkingMessage **ppOutMessages, int nMaxMessages ) = 0; + + /// Returns information about the specified connection. + virtual bool GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo_t *pInfo ) = 0; + + /// Returns brief set of connection status that you might want to display + /// to the user in game. + virtual bool GetQuickConnectionStatus( HSteamNetConnection hConn, SteamNetworkingQuickConnectionStatus *pStats ) = 0; + + /// Returns detailed connection stats in text format. Useful + /// for dumping to a log, etc. + /// + /// Returns: + /// -1 failure (bad connection handle) + /// 0 OK, your buffer was filled in and '\0'-terminated + /// >0 Your buffer was either nullptr, or it was too small and the text got truncated. Try again with a buffer of at least N bytes. + virtual int GetDetailedConnectionStatus( HSteamNetConnection hConn, char *pszBuf, int cbBuf ) = 0; + + /// Returns information about the listen socket. + /// + /// *pnIP and *pnPort will be 0 if the socket is set to listen for connections based + /// on SteamID only. If your listen socket accepts connections on IPv4, then both + /// fields will return nonzero, even if you originally passed a zero IP. However, + /// note that the address returned may be a private address (e.g. 10.0.0.x or 192.168.x.x), + /// and may not be reachable by a general host on the Internet. + virtual bool GetListenSocketInfo( HSteamListenSocket hSocket, uint32 *pnIP, uint16 *pnPort ) = 0; + + // + // Special SDR connections involved with servers hosted in Valve data centers + // + virtual bool SetHostedDedicatedServerCertificate( const void *pCert, int cbCert, void *pPrivateKey, int cbPrivateKey ) = 0; + virtual HSteamListenSocket CreateHostedDedicatedServerListenSocket( uint16 nPort ) = 0; + virtual HSteamNetConnection ConnectToHostedDedicatedServer( CSteamID steamIDTarget ) = 0; + + // + // Gets some debug text from the connection + // + virtual bool GetConnectionDebugText( HSteamNetConnection hConn, char *pOut, int nOutCCH ) = 0; + + // + // Set and get configuration values, see ESteamNetworkingConfigurationValue for individual descriptions. + // + // Returns the value or -1 is eConfigValue is invalid + virtual int32 GetConfigurationValue( ESteamNetworkingConfigurationValue eConfigValue ) = 0; + // Returns true if successfully set + virtual bool SetConfigurationValue( ESteamNetworkingConfigurationValue eConfigValue, int32 nValue ) = 0; + + // Return the name of an int configuration value, or NULL if config value isn't known + virtual const char *GetConfigurationValueName( ESteamNetworkingConfigurationValue eConfigValue ) = 0; + + // + // Set and get configuration strings, see ESteamNetworkingConfigurationString for individual descriptions. + // + // Get the configuration string, returns length of string needed if pDest is nullpr or destSize is 0 + // returns -1 if the eConfigValue is invalid + virtual int32 GetConfigurationString( ESteamNetworkingConfigurationString eConfigString, char *pDest, int32 destSize ) = 0; + virtual bool SetConfigurationString( ESteamNetworkingConfigurationString eConfigString, const char *pString ) = 0; + + // Return the name of a string configuration value, or NULL if config value isn't known + virtual const char *GetConfigurationStringName( ESteamNetworkingConfigurationString eConfigString ) = 0; + +}; +#define STEAMSOCKETNETWORKING_VERSION "SteamSocketNetworking001" + +// Notification struct used to notify when a connection has changed state +struct SteamNetConnectionStatusChangedCallback_t +{ + HSteamNetConnection m_hConn; //< Connection handle + SteamNetConnectionInfo_t m_info; //< Full connection info + int m_eOldState; //< ESNetSocketState. (Current stats is in m_info) +}; + +// Temporary global accessor. This will be moved to steam_api.h +STEAMDATAGRAMLIB_INTERFACE ISteamSocketNetworking *SteamSocketNetworking(); + +typedef void * ( S_CALLTYPE *FSteamInternal_CreateInterface )( const char *); +typedef void ( S_CALLTYPE *FSteamAPI_RegisterCallResult)( class CCallbackBase *pCallback, SteamAPICall_t hAPICall ); +typedef void ( S_CALLTYPE *FSteamAPI_UnregisterCallResult)( class CCallbackBase *pCallback, SteamAPICall_t hAPICall ); + +/// !KLUDGE! Glue code that will go away when we move everything into +/// the ISteamNetwork interfaces +STEAMDATAGRAMLIB_INTERFACE void SteamDatagramClient_Internal_SteamAPIKludge( FSteamAPI_RegisterCallResult fnRegisterCallResult, FSteamAPI_UnregisterCallResult fnUnregisterCallResult ); +STEAMDATAGRAMLIB_INTERFACE bool SteamDatagramClient_Init_Internal( const char *pszCacheDirectory, /* ESteamDatagramPartner */ int ePartner, int iPartnerMask, SteamDatagramErrMsg &errMsg, ISteamClient *pClient, HSteamUser hSteamUser, HSteamPipe hSteamPipe ); +inline bool SteamDatagramClient_Init( const char *pszCacheDirectory, /* ESteamDatagramPartner */ int ePartner, int iPartnerMask, SteamDatagramErrMsg &errMsg ) +{ + SteamDatagramClient_Internal_SteamAPIKludge( &::SteamAPI_RegisterCallResult, &::SteamAPI_UnregisterCallResult ); + return SteamDatagramClient_Init_Internal( pszCacheDirectory, ePartner, iPartnerMask, errMsg, ::SteamClient(), ::SteamAPI_GetHSteamUser(), ::SteamAPI_GetHSteamPipe() ); +} + + +/// Shutdown all clients and close all sockets +STEAMDATAGRAMLIB_INTERFACE void SteamDatagramClient_Kill(); + +/// Initialize the game server interface +STEAMDATAGRAMLIB_INTERFACE bool SteamDatagramServer_Init_Internal( SteamDatagramErrMsg &errMsg, ISteamClient *pClient, HSteamUser hSteamUser, HSteamPipe hSteamPipe ); +// KLUDGE TF is using an old version of the SDK, which doesn't have this. TF doesn't need this, so just comment it out. +// We'll need to upgrade the Steamworks SDK if we want to actually use SDR +//inline bool SteamDatagramServer_Init( SteamDatagramErrMsg &errMsg ) +//{ +// SteamDatagramClient_Internal_SteamAPIKludge( &::SteamAPI_RegisterCallResult, &::SteamAPI_UnregisterCallResult ); +// return SteamDatagramServer_Init_Internal( errMsg, ::SteamGameServerClient(), ::SteamGameServer_GetHSteamUser(), ::SteamGameServer_GetHSteamPipe() ); +//} + +/// Shutdown the game server interface +STEAMDATAGRAMLIB_INTERFACE void SteamDatagramServer_Kill( ); + +// !KLUDGE! Check for connections that have changed status, and post callbacks. +// This is temporary we can hook this up using the ordinary steam CCallback mechanism +typedef void (*FSteamNetConnectionStatusChangedCallback)( SteamNetConnectionStatusChangedCallback_t *pInfo ); +STEAMDATAGRAMLIB_INTERFACE void Temp_DispatchsSteamNetConnectionStatusChangedCallbacks( FSteamNetConnectionStatusChangedCallback fnCallback ); + +enum ESteamDatagramDebugOutputType +{ + k_ESteamDatagramDebugOutputType_None, + k_ESteamDatagramDebugOutputType_Error, + k_ESteamDatagramDebugOutputType_Important, // Nothing is wrong, but this is an important notification + k_ESteamDatagramDebugOutputType_Warning, + k_ESteamDatagramDebugOutputType_Msg, // Recommended amount + k_ESteamDatagramDebugOutputType_Verbose, // Quite a bit + k_ESteamDatagramDebugOutputType_Debug, // Practically everything +}; + +/// Setup callback for debug output, and the desired verbosity you want. +typedef void (*FSteamDatagramDebugOutput)( /* ESteamDatagramDebugOutputType */ int nType, const char *pszMsg ); +STEAMDATAGRAMLIB_INTERFACE void SteamDatagram_SetDebugOutputFunction( /* ESteamDatagramDebugOutputType */ int eDetailLevel, FSteamDatagramDebugOutput pfnFunc ); + +#endif // ISTEAMNETWORKINGSOCKETS diff --git a/public/steamnetworkingsockets/isteamnetworkingutils.h b/public/steamnetworkingsockets/isteamnetworkingutils.h new file mode 100644 index 0000000..80190e9 --- /dev/null +++ b/public/steamnetworkingsockets/isteamnetworkingutils.h @@ -0,0 +1,125 @@ +//====== Copyright Valve Corporation, All rights reserved. ==================== +// +// Purpose: misc networking utilities +// +//============================================================================= + +#ifndef ISTEAMNETWORKINGUTILS +#define ISTEAMNETWORKINGUTILS +#ifdef _WIN32 +#pragma once +#endif + +#include "steamnetworkingtypes.h" +struct SteamDatagramRelayAuthTicket; + +//----------------------------------------------------------------------------- +/// Misc networking utilities for checking the local networking environment +/// and estimating pings. +class ISteamNetworkingUtils +{ +public: + + /// Fetch current timestamp. These values never go backwards, and + /// the initial value is low enough that practically speaking it's + /// not necessary to worry about the value wrapping around. + virtual SteamNetworkingMicroseconds GetLocalTimestamp() = 0; + + /// Check if the ping data of sufficient recency is available, and if + /// it's too old, start refreshing it. + /// + /// Games that use the ping location information will typically + /// want to call this at boot time, to make sure all prerequisites + /// are ready. Especially since the first measurement might take + /// slightly longer than subsequent measurements. + /// + /// Returns true if sufficiently recent data is already available. + /// + /// Returns false if sufficiently recent data is not available. In this + /// case, ping measurement is initiated, if it is not already active. + /// (You cannot restart a measurement already in progress.) + /// + /// A FIXME event will be posted when measurement is completed. + virtual bool CheckPingDataUpToDate( float flMaxAgeSeconds ) = 0; + + /// Return location info for the current host. Returns the approximate + /// age of the data, in seconds, or -1 if no data is available. + /// Note that this might return an age older than the age of your game's + /// process, if the data was obtained before you game started. + /// + /// This always return the most up-to-date information we have available + /// right now, even if we are in the middle of re-calculating ping times. + virtual float GetLocalPingLocation( SteamNetworkPingLocation_t &result ) = 0; + + /// Return true if we are taking ping measurements to update our ping + /// location or select optimal routing. Ping measurement typically takes + /// a few seconds, perhaps up to 10 seconds. + virtual bool IsPingMeasurementInProgress() = 0; + + /// Estimate the round-trip latency between two arbitrary locations, in + /// milliseconds. This is a conservative estimate, based on routing through + /// the relay network. For most basic connections based on SteamID, + /// this ping time will be pretty accurate, since it will be based on the + /// route likely to be actually used. + /// + /// If a direct IP route is used (perhaps via NAT traversal), then the route + /// will be different, and the ping time might be better. Or it might actually + /// be a bit worse! Standard IP routing is frequently suboptimal! + /// + /// but even in this case, the estimate obtained using this method is a + /// reasonable upper bound on the ping time. (Also it has the advantage + /// of returning immediately and not sending any packets.) + /// + /// In a few cases we might not able to estimate the route. In this case + /// a negative value is returned. k_nSteamNetworkingPing_Failed means + /// the reason was because of some networking difficulty. (Failure to + /// ping, etc) k_nSteamNetworkingPing_Unknown is returned if we cannot + /// currently answer the question for some other reason. + virtual int EstimatePingTimeBetweenTwoLocations( const SteamNetworkPingLocation_t &location1, const SteamNetworkPingLocation_t &location2 ) = 0; + + /// Same as EstimatePingTime, but assumes that one location is the local host. + /// This is a bit faster, especially if you need to calculate a bunch of + /// these in a loop to find the fastest one. + /// + /// In rare cases this might return a slightly different estimate than combining + /// GetLocalPingLocation with EstimatePingTimeBetweenTwoLocations. That's because + /// this function uses a slightly more complete description + virtual int EstimatePingTimeFromLocalHost( const SteamNetworkPingLocation_t &remoteLocation ) = 0; + + // FIXME: + // + // Check current internet connection status + + // + // Low level ticket stuff. I need to get some advice and talk through how this should work + // or how best to tuck it away and make it transparent. + // + + virtual bool ReceivedTicket( const void *pvTicket, int cbTicket, SteamDatagramRelayAuthTicket *pOutParsedTicket ) = 0; + virtual bool HasTicketForServer( CSteamID steamID ) = 0; + virtual uint32 GetIPForServerSteamIDFromTicket( CSteamID steamID ) = 0; + + // + // Low level network config stuff I haven't figure out how best to tuck away. + // Dota and CSGO use it because we have gameservers in the datacenter, and + // we need this information to do region selection. But most games won't + // need it. + // + + /// Fetch directly measured ping time from local host to a particular network PoP. + /// Most games will not need to call this. + virtual int GetPingToDataCenter( SteamNetworkingPOPID popID, SteamNetworkingPOPID *pViaRelayPoP ) = 0; + virtual int GetDirectPingToPOP( SteamNetworkingPOPID popID ) = 0; + + /// Get number of network PoPs in the config + virtual int GetPOPCount() = 0; + + /// Get list of all POP IDs + virtual int GetPOPList( SteamNetworkingPOPID *list, int nListSz ) = 0; +}; +#define STEAMNETWORKINGUTILS_VERSION "SteamNetworkingUtils001" + +/// Get ISteamNetworkingUtils object. This will eventually go in Steam_api.h with all the rest of its kin +STEAMDATAGRAMLIB_INTERFACE ISteamNetworkingUtils *SteamNetworkingUtils(); + +#endif // ISTEAMNETWORKINGUTILS diff --git a/public/steamnetworkingsockets/steamdatagram_stats.h b/public/steamnetworkingsockets/steamdatagram_stats.h new file mode 100644 index 0000000..8f6cb53 --- /dev/null +++ b/public/steamnetworkingsockets/steamdatagram_stats.h @@ -0,0 +1,298 @@ +//====== Copyright Valve Corporation, All rights reserved. ==================== +// +// Some public types for communicating detailed connection stats +// +//============================================================================= + +#ifndef STEAMDATAGRAM_STATS_H +#define STEAMDATAGRAM_STATS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "steamnetworkingtypes.h" + +#pragma pack(push) +#pragma pack(8) + +STEAMDATAGRAMLIB_INTERFACE void SteamDatagramLinkInstantaneousStats_Clear( SteamDatagramLinkInstantaneousStats *pThis ); +STEAMDATAGRAMLIB_INTERFACE void SteamDatagramLinkLifetimeStats_Clear( SteamDatagramLinkLifetimeStats *pThis ); +STEAMDATAGRAMLIB_INTERFACE int SteamDatagramLinkLifetimeStats_PingHistogramTotalCount( const SteamDatagramLinkLifetimeStats *pThis ); +STEAMDATAGRAMLIB_INTERFACE int SteamDatagramLinkLifetimeStats_QualityHistogramTotalCount( const SteamDatagramLinkLifetimeStats *pThis ); +STEAMDATAGRAMLIB_INTERFACE int SteamDatagramLinkLifetimeStats_JitterHistogramTotalCount( const SteamDatagramLinkLifetimeStats *pThis ); +STEAMDATAGRAMLIB_INTERFACE int SteamDatagramLinkLifetimeStats_TXSpeedHistogramTotalCount( const SteamDatagramLinkLifetimeStats *pThis ); +STEAMDATAGRAMLIB_INTERFACE int SteamDatagramLinkLifetimeStats_RXSpeedHistogramTotalCount( const SteamDatagramLinkLifetimeStats *pThis ); +STEAMDATAGRAMLIB_INTERFACE void SteamDatagramLinkStats_Clear( SteamDatagramLinkStats *pThis ); +STEAMDATAGRAMLIB_INTERFACE void SteamNetworkingDetailedConnectionStatus_Clear( SteamNetworkingDetailedConnectionStatus *pThis ); +STEAMDATAGRAMLIB_INTERFACE int SteamNetworkingDetailedConnectionStatus_Print( const SteamNetworkingDetailedConnectionStatus *pThis, char *pszBuf, int cbBuf ); + +/// Instantaneous statistics for a link between two hosts. +struct SteamDatagramLinkInstantaneousStats +{ + + /// Data rates + float m_flOutPacketsPerSec; + float m_flOutBytesPerSec; + float m_flInPacketsPerSec; + float m_flInBytesPerSec; + + /// Smoothed ping. This will be -1 if we don't have any idea! + int m_nPingMS; + + /// 0...1, estimated number of packets that were sent to us, but we failed to receive. + /// <0 if we haven't received any sequenced packets and so we don't have any way to estimate this. + float m_flPacketsDroppedPct; + + /// Packets received with a sequence number abnormality, other than basic packet loss. (Duplicated, out of order, lurch.) + /// <0 if we haven't received any sequenced packets and so we don't have any way to estimate this. + float m_flPacketsWeirdSequenceNumberPct; + + /// Peak jitter + int m_usecMaxJitter; + + /// Current sending rate, this can be low at connection start until the slow start + /// ramps it up. It's adjusted as packets are lost and congestion is encountered during + /// the connection + int m_nSendRate; + + /// How many pending bytes are waiting to be sent. This is data that is currently waiting + /// to be sent and in outgoing buffers. If this is zero, then the connection is idle + /// and all pending data has been sent. Note that in case of packet loss any pending + /// reliable data might be re-sent. This does not include data that has been sent and is + /// waiting for acknowledgment. + int m_nPendingBytes; + + inline void Clear() { SteamDatagramLinkInstantaneousStats_Clear( this ); } +}; + +/// Stats for the lifetime of a connection. +/// Should match CMsgSteamDatagramLinkLifetimeStats +struct SteamDatagramLinkLifetimeStats +{ + /// Reset all values to zero / unknown status + inline void Clear() { return SteamDatagramLinkLifetimeStats_Clear( this ); } + + // + // Lifetime counters. + // NOTE: Average packet loss, etc can be deduced from this. + // + int64 m_nPacketsSent; + int64 m_nBytesSent; + int64 m_nPacketsRecv; // total number of packets received, some of which might not have had a sequence number. Don't use this number to try to estimate lifetime packet loss, use m_nPacketsRecvSequenced + int64 m_nBytesRecv; + int64 m_nPktsRecvSequenced; // packets that we received that had a sequence number. + int64 m_nPktsRecvDropped; + int64 m_nPktsRecvOutOfOrder; + int64 m_nPktsRecvDuplicate; + int64 m_nPktsRecvSequenceNumberLurch; + + // SNP message counters + int64 m_nMessagesSentReliable; + int64 m_nMessagesSentUnreliable; + int64 m_nMessagesRecvReliable; + int64 m_nMessagesRecvUnreliable; + + // + // Ping distribution + // + int m_nPingHistogram25; // 0..25 + int m_nPingHistogram50; // 26..50 + int m_nPingHistogram75; // 51..75 + int m_nPingHistogram100; // etc + int m_nPingHistogram125; + int m_nPingHistogram150; + int m_nPingHistogram200; + int m_nPingHistogram300; + int m_nPingHistogramMax; // >300 + inline int PingHistogramTotalCount() const { return SteamDatagramLinkLifetimeStats_PingHistogramTotalCount( this ); } + + // Distribution. + // NOTE: Some of these might be -1 if we didn't have enough data to make a meaningful estimate! + // It takes fewer samples to make an estimate of the median than the 98th percentile! + short m_nPingNtile5th; // 5% of ping samples were <= Nms + short m_nPingNtile50th; // 50% of ping samples were <= Nms + short m_nPingNtile75th; // 70% of ping samples were <= Nms + short m_nPingNtile95th; // 95% of ping samples were <= Nms + short m_nPingNtile98th; // 98% of ping samples were <= Nms + short m__pad1; + + + // + // Connection quality distribution + // + int m_nQualityHistogram100; // This means everything was perfect. If we delivered over 100 packets in the interval and were less than perfect, but greater than 99.5%, we will use 99% instead. + int m_nQualityHistogram99; // 99%+ + int m_nQualityHistogram97; + int m_nQualityHistogram95; + int m_nQualityHistogram90; + int m_nQualityHistogram75; + int m_nQualityHistogram50; + int m_nQualityHistogram1; + int m_nQualityHistogramDead; // we received nothing during the interval; it looks like the connection dropped + inline int QualityHistogramTotalCount() const { return SteamDatagramLinkLifetimeStats_QualityHistogramTotalCount( this ); } + + // Distribution. Some might be -1, see above for why. + short m_nQualityNtile2nd; // 2% of measurement intervals had quality <= N% + short m_nQualityNtile5th; // 5% of measurement intervals had quality <= N% + short m_nQualityNtile25th; // 25% of measurement intervals had quality <= N% + short m_nQualityNtile50th; // 50% of measurement intervals had quality <= N% + + // Jitter histogram + int m_nJitterHistogramNegligible; + int m_nJitterHistogram1; + int m_nJitterHistogram2; + int m_nJitterHistogram5; + int m_nJitterHistogram10; + int m_nJitterHistogram20; + inline int JitterHistogramTotalCount() const { return SteamDatagramLinkLifetimeStats_JitterHistogramTotalCount( this ); } + + // + // Connection transmit speed histogram + // + int m_nTXSpeedMax; // Max speed we hit + + int m_nTXSpeedHistogram16; // Speed at kb/s + int m_nTXSpeedHistogram32; + int m_nTXSpeedHistogram64; + int m_nTXSpeedHistogram128; + int m_nTXSpeedHistogram256; + int m_nTXSpeedHistogram512; + int m_nTXSpeedHistogram1024; + int m_nTXSpeedHistogramMax; + inline int TXSpeedHistogramTotalCount() const { return SteamDatagramLinkLifetimeStats_TXSpeedHistogramTotalCount( this ); } + + // Distribution. Some might be -1, see above for why. + int m_nTXSpeedNtile5th; // 5% of transmit samples were <= N kb/s + int m_nTXSpeedNtile50th; // 50% of transmit samples were <= N kb/s + int m_nTXSpeedNtile75th; // 75% of transmit samples were <= N kb/s + int m_nTXSpeedNtile95th; // 95% of transmit samples were <= N kb/s + int m_nTXSpeedNtile98th; // 98% of transmit samples were <= N kb/s + + // + // Connection receive speed histogram + // + int m_nRXSpeedMax; // Max speed we hit that formed the histogram + + int m_nRXSpeedHistogram16; // Speed at kb/s + int m_nRXSpeedHistogram32; + int m_nRXSpeedHistogram64; + int m_nRXSpeedHistogram128; + int m_nRXSpeedHistogram256; + int m_nRXSpeedHistogram512; + int m_nRXSpeedHistogram1024; + int m_nRXSpeedHistogramMax; + inline int RXSpeedHistogramTotalCount() const { return SteamDatagramLinkLifetimeStats_RXSpeedHistogramTotalCount( this ); } + + // Distribution. Some might be -1, see above for why. + int m_nRXSpeedNtile5th; // 5% of transmit samples were <= N kb/s + int m_nRXSpeedNtile50th; // 50% of transmit samples were <= N kb/s + int m_nRXSpeedNtile75th; // 75% of transmit samples were <= N kb/s + int m_nRXSpeedNtile95th; // 95% of transmit samples were <= N kb/s + int m_nRXSpeedNtile98th; // 98% of transmit samples were <= N kb/s + +}; + +/// Link stats. Pretty much everything you might possibly want to know about the connection +struct SteamDatagramLinkStats +{ + + /// Latest instantaneous stats, calculated locally + SteamDatagramLinkInstantaneousStats m_latest; + + /// Peak values for each instantaneous stat + //SteamDatagramLinkInstantaneousStats m_peak; + + /// Lifetime stats, calculated locally + SteamDatagramLinkLifetimeStats m_lifetime; + + /// Latest instantaneous stats received from remote host. + /// (E.g. "sent" means they are reporting what they sent.) + SteamDatagramLinkInstantaneousStats m_latestRemote; + + /// How many seconds ago did we receive m_latestRemote? + /// This will be <0 if the data is not valid! + float m_flAgeLatestRemote; + + /// Latest lifetime stats received from remote host. + SteamDatagramLinkLifetimeStats m_lifetimeRemote; + + /// How many seconds ago did we receive the lifetime stats? + /// This will be <0 if the data is not valid! + float m_flAgeLifetimeRemote; + + /// Reset everything to unknown/initial state. + inline void Clear() { SteamDatagramLinkStats_Clear( this ); } +}; + +/// Status of a particular network resource +enum ESteamDatagramAvailability +{ + k_ESteamDatagramAvailability_CannotTry = -3, // A dependent resource is missing, so this service is unavailable. (E.g. we cannot talk to routers because Internet is down or we don't have the network config.) + k_ESteamDatagramAvailability_Failed = -2, // We have tried for enough time that we would expect to have been successful by now. We have never been successful + k_ESteamDatagramAvailability_Previously = -1, // We tried and were successful at one time, but now it looks like we have a problem + k_ESteamDatagramAvailability_Unknown = 0, // Unknown, or not applicable in this context + k_ESteamDatagramAvailability_NeverTried = 1, // We don't know because we haven't ever checked + k_ESteamDatagramAvailability_Attempting = 2, // We're trying now, but are not yet successful. This is not an error, but it's not success, either. + k_ESteamDatagramAvailability_Current = 3, // Resource is online. +}; + +/// Describe detailed state of current connection +struct SteamNetworkingDetailedConnectionStatus +{ + /// Basic connection info + SteamNetConnectionInfo_t m_info; + + /// Do we have a valid network configuration? We cannot do anything without this. + ESteamDatagramAvailability m_eAvailNetworkConfig; + +// /// Does it look like we have a connection to the Internet at all? +// EAvailability m_eAvailInternet; + + /// Successful communication with a box on the routing network. + /// This will be marked as failed if there is a general internet + /// connection. + ESteamDatagramAvailability m_eAvailAnyRouterCommunication; + + /// End-to-end communication with the remote host. + //ESteamDatagramAvailability m_eAvailEndToEnd; + + /// Stats for end-to-end link to the gameserver + SteamDatagramLinkStats m_statsEndToEnd; + + /// Currently selected front router, if any. + /// Note that PoP ID can be found in the SteamNetConnectionInfo_t + char m_szPrimaryRouterName[64]; + uint32 m_unPrimaryRouterIP; + uint16 m_unPrimaryRouterPort; + + /// Stats for "front" link to current router + SteamDatagramLinkStats m_statsPrimaryRouter; + + /// Back ping time as reported by primary. + /// (The front ping is in m_statsPrimaryRouter, + /// and usually the front ping plus the back ping should + /// approximately equal the end-to-end ping) + int m_nPrimaryRouterBackPing; + + /// Currently selected back router, if any + SteamNetworkingPOPID m_idBackupRouterCluster; + char m_szBackupRouterName[64]; + uint32 m_unBackupRouterIP; + uint16 m_unBackupRouterPort; + + /// Ping times to backup router, if any + int m_nBackupRouterFrontPing, m_nBackupRouterBackPing; + + /// Clear everything to an unknown state + inline void Clear() { SteamNetworkingDetailedConnectionStatus_Clear( this ); } + + /// Print into a buffer. + /// 0 = OK + /// >1 = buffer was null or too small (in which case truncation happened). + /// Pass a buffer of at least N bytes. + inline int Print( char *pszBuf, int cbBuf ) const { return SteamNetworkingDetailedConnectionStatus_Print( this, pszBuf, cbBuf ); } +}; + +#pragma pack(pop) + +#endif // STEAMDATAGRAM_STATS_H diff --git a/public/steamnetworkingsockets/steamdatagram_ticketgen.h b/public/steamnetworkingsockets/steamdatagram_ticketgen.h new file mode 100644 index 0000000..32e080a --- /dev/null +++ b/public/steamnetworkingsockets/steamdatagram_ticketgen.h @@ -0,0 +1,60 @@ +//====== Copyright Valve Corporation, All rights reserved. ==================== +// +// Backend functions to generate authorization tickets for steam datagram +// +//============================================================================= + +#ifndef STEAMDATAGRAM_TICKETGEN_H +#define STEAMDATAGRAM_TICKETGEN_H +#ifdef _WIN32 +#pragma once +#endif + +// Import some common stuff that is useful by both the client +// and the backend ticket-generating authority. +#include "steamdatagram_tickets.h" + +struct SteamDatagramSignedTicketBlob +{ + int m_sz; + uint8 m_blob[ k_cbSteamDatagramMaxSerializedTicket ]; +}; + +/// Initialize ticket generation with an Ed25519 private key. +/// See: https://ed25519.cr.yp.to/ +/// +/// Input buffer will be securely wiped. +/// +/// You can generate an Ed25519 key using OpenSSH: +/// +/// ssh-keygen -t ed25519 +/// +/// The private key should be a PEM-like block of text +/// ("-----BEGIN OPENSSH PRIVATE KEY-----"). +/// Private keys encrypted with a password are not supported. +/// +/// In order for signatures using this key to be accepted by the relay network, +/// you need to send your public key to Valve. This key should be on a single line +/// of text that begins with "ssh-ed25519". (The format used in the .ssh/authorized_keys +/// file.) +STEAMDATAGRAM_TICKET_INTERFACE bool SteamDatagram_InitTicketGenerator_Ed25519( void *pvPrivateKey, size_t cbPrivateKey ); + +/// Serialize the specified auth ticket and attach a signature. +/// Returns false if you did something stupid like forgot to load a key. +/// Will also fail if your ticket is too big. (Probably because you +/// added too many extra fields.) +STEAMDATAGRAM_TICKET_INTERFACE bool SteamDatagram_SerializeAndSignTicket( const SteamDatagramRelayAuthTicket &ticket, SteamDatagramSignedTicketBlob &outBlob ); + +// +// Legacy / deprecated +// + +/// Initialize ticket generation with an RSA private key. You can either +/// pass a PEM block ("-----BEGIN PRIVATE KEY-----"), or binary PKCS#8 DER. +/// Input buffer will be securely wiped. +STEAMDATAGRAM_TICKET_INTERFACE bool SteamDatagram_InitTicketGenerator_RSA_deprecated( void *pvPrivateKey, size_t cbPrivateKey ); + +/// Generate a signature for legacy support +STEAMDATAGRAM_TICKET_INTERFACE bool SteamDatagram_SerializeAndSignTicket_deprecated( const SteamDatagramRelayAuthTicket &ticket, SteamDatagramSignedTicketBlob &outBlob ); + +#endif // STEAMDATAGRAM_TICKETGEN_H diff --git a/public/steamnetworkingsockets/steamdatagram_tickets.h b/public/steamnetworkingsockets/steamdatagram_tickets.h new file mode 100644 index 0000000..22191ad --- /dev/null +++ b/public/steamnetworkingsockets/steamdatagram_tickets.h @@ -0,0 +1,151 @@ +//====== Copyright Valve Corporation, All rights reserved. ==================== +// +// Types and utilities for handling steam datagram tickets. These are +// useful for both the client and the backend ticket generating authority. +// +//============================================================================= + +#ifndef STEAMDATAGRAM_TICKETS_H +#define STEAMDATAGRAM_TICKETS_H +#ifdef _WIN32 +#pragma once +#endif + +#ifndef assert + #include <assert.h> +#endif + +#include "steamnetworkingtypes.h" + +#pragma pack(push) +#pragma pack(8) + +#if defined( STEAMDATAGRAM_TICKETGEN_FOREXPORT ) || defined( STEAMDATAGRAMLIB_FOREXPORT ) + #define STEAMDATAGRAM_TICKET_INTERFACE DLL_EXPORT +#elif defined( STEAMDATAGRAMLIB_STATIC_LINK ) + #define STEAMDATAGRAM_TICKET_INTERFACE extern "C" +#else + #define STEAMDATAGRAM_TICKET_INTERFACE DLL_IMPORT +#endif + +/// Max length of serialized auth ticket. This is important so that we +/// can ensure that we always fit into a single UDP datagram (along with +/// other certs and signatures) and kep the implementation simple. +const size_t k_cbSteamDatagramMaxSerializedTicket = 512; + +/// Network-routable identifier for a service. In general, clients should +/// treat this as an opaque structure. The only thing that is important +/// is that this contains everything the system needs to route packets to a +/// service. +struct SteamDatagramServiceNetID +{ + // Just use the private LAN address to identify the service + uint32 m_unIP; + uint16 m_unPort; + uint16 m__nPad1; + + void Clear() { *(uint64 *)this = 0; } + + uint64 ConvertToUint64() const { return ( uint64(m_unIP) << 16U ) | uint64(m_unPort); } + void SetFromUint64( uint64 x ) + { + m_unIP = uint32(x >> 16U); + m_unPort = uint16(x); + m__nPad1 = 0; + } + + inline bool operator==( const SteamDatagramServiceNetID &x ) const { return m_unIP == x.m_unIP && m_unPort == x.m_unPort; } +}; + +/// Ticket used to gain access to the relay network. +struct SteamDatagramRelayAuthTicket +{ + SteamDatagramRelayAuthTicket() { Clear(); } + + /// Reset all fields + void Clear() { memset( this, 0, sizeof(*this) ); } + + /// Steam ID of the gameserver we want to talk to + CSteamID m_steamIDGameserver; + + /// Steam ID of the person who was authorized. + CSteamID m_steamIDAuthorizedSender; + + /// SteamID is authorized to send from a particular public IP. If this + /// is 0, then the sender is not restricted to a particular IP. + uint32 m_unPublicIP; + + /// Time when the ticket expires. + RTime32 m_rtimeTicketExpiry; + + /// Routing information + SteamDatagramServiceNetID m_routing; + + /// App ID this is for + uint32 m_nAppID; + + struct ExtraField + { + enum EType + { + k_EType_String, + k_EType_Int, // For most small integral values. Uses google protobuf sint64, so it's small on the wire. WARNING: In some places this value may be transmitted in JSON, in which case precision may be lost in backend analytics. Don't use this afor an "identifier", use it for a scalar quantity. + k_EType_Fixed64, // 64 arbitrary bits. This value is treated as an "identifier". In places where JSON format is used, it will be serialized as a string. No aggregation / analytics can be performed on this value. + }; + int /* EType */ m_eType; + char m_szName[28]; + + union { + char m_szStringValue[128]; + int64 m_nIntValue; + uint64 m_nFixed64Value; + }; + }; + enum { k_nMaxExtraFields = 16 }; + int m_nExtraFields; + ExtraField m_vecExtraFields[ k_nMaxExtraFields ]; + + /// Helper to add an extra field in a single call + void AddExtraField_Int( const char *pszName, int64 val ) + { + ExtraField *p = AddExtraField( pszName, ExtraField::k_EType_Int ); + if ( p ) + p->m_nIntValue = val; + } + void AddExtraField_Fixed64( const char *pszName, uint64 val ) + { + ExtraField *p = AddExtraField( pszName, ExtraField::k_EType_Fixed64 ); + if ( p ) + p->m_nFixed64Value = val; + } + void AddExtraField_String( const char *pszName, const char *val ) + { + ExtraField *p = AddExtraField( pszName, ExtraField::k_EType_Fixed64 ); + if ( p ) + V_strcpy_safe( p->m_szStringValue, val ); + } + +private: + ExtraField *AddExtraField( const char *pszName, ExtraField::EType eType ) + { + if ( m_nExtraFields >= k_nMaxExtraFields ) + { + assert( false ); + return nullptr; + } + ExtraField *p = &m_vecExtraFields[ m_nExtraFields++ ]; + p->m_eType = eType; + V_strcpy_safe( p->m_szName, pszName ); + return p; + } +}; + +/// Unpack signed ticket. Does not check the signature! +/// Note that it's assumed that you won't need to link with both the client +/// lib and the ticket generating lib in any one project, since this symbol +/// is exposed in both. +STEAMDATAGRAM_TICKET_INTERFACE bool SteamDatagramRelayAuthTicket_Parse( const void *pvTicket, int cbTicket, SteamDatagramRelayAuthTicket *pOutTicket, SteamDatagramErrMsg &errMsg ); + +#pragma pack(pop) + +#endif // STEAMDATAGRAM_TICKETS_H diff --git a/public/steamnetworkingsockets/steamnetworkingtypes.h b/public/steamnetworkingsockets/steamnetworkingtypes.h new file mode 100644 index 0000000..2fd1baa --- /dev/null +++ b/public/steamnetworkingsockets/steamnetworkingtypes.h @@ -0,0 +1,641 @@ +//====== Copyright Valve Corporation, All rights reserved. ==================== +// +// Purpose: misc networking utilities +// +//============================================================================= + +#ifndef STEAMNETWORKINGTYPES +#define STEAMNETWORKINGTYPES +#ifdef _WIN32 +#pragma once +#endif + +#include "steam/steamtypes.h" +#include "steam/steamclientpublic.h" +#include "tier0/platform.h" + +#if defined( STEAMDATAGRAMLIB_STATIC_LINK ) + #define STEAMDATAGRAMLIB_INTERFACE extern +#elif defined( STEAMDATAGRAMLIB_FOREXPORT ) + #define STEAMDATAGRAMLIB_INTERFACE DLL_EXPORT +#else + #define STEAMDATAGRAMLIB_INTERFACE DLL_IMPORT +#endif + +#pragma pack( push, 8 ) + +struct SteamNetworkPingLocation_t; +class ISteamNetworkingMessage; +struct SteamDatagramLinkStats; +struct SteamDatagramLinkLifetimeStats; +struct SteamDatagramLinkInstantaneousStats; +struct SteamNetworkingDetailedConnectionStatus; + +/// Handle used to identify a connection to a remote host. +typedef uint32 HSteamNetConnection; +const HSteamNetConnection k_HSteamNetConnection_Invalid = 0; + +/// Handle used to identify a "listen socket". +typedef uint32 HSteamListenSocket; +const HSteamListenSocket k_HSteamListenSocket_Invalid = 0; + +/// Configuration values for Steam networking. +/// +/// Most of these are for controlling extend logging or features +/// of various subsystems +enum ESteamNetworkingConfigurationValue +{ + // Set to true (non-zero) to use Steam Network Protocol for message + // delivery. + // SNP is a protocol for sending a mix of reliable and unreliable message + // payloads and handles retransmission of reliable messages and also + // manages transfer rate congestion control. Defaults to 1 (on). + k_ESteamNetworkingConfigurationValue_SNP = 0, + + // 0-100 Randomly discard N pct of unreliable messages instead of sending + // Defaults to 0 (no loss). + k_ESteamNetworkingConfigurationValue_FakeMessageLoss_Send = 1, + + // 0-100 Randomly discard N pct of unreliable messages upon receive + // Defaults to 0 (no loss). + k_ESteamNetworkingConfigurationValue_FakeMessageLoss_Recv = 2, + + // 0-100 Randomly discard N pct of packets instead of sending + k_ESteamNetworkingConfigurationValue_FakePacketLoss_Send = 3, + + // 0-100 Randomly discard N pct of packets received + k_ESteamNetworkingConfigurationValue_FakePacketLoss_Recv = 4, + + // Set to true (non-zero) to open up a seperate debug window showing SNP + // state for all current connections. Only works on Windows. + // Defaults to 0 (off) + k_ESteamNetworkingConfigurationValue_SNP_DebugWindow = 5, + + // Upper limit of buffered pending bytes to be sent, if this is reached + // SendMessage will return k_EResultPending + // Default is 512k (524288 bytes) + k_ESteamNetworkingConfigurationValue_SNP_SendBufferSize = 6, + + // Maximum send rate clamp, 0 is no limit + // This value will control the maximum allowed sending rate that congestion + // is allowed to reach. Default is 0 (no-limit) + k_ESteamNetworkingConfigurationValue_SNP_MaxRate = 7, + + // Minimum send rate clamp, 0 is no limit + // This value will control the minimum allowed sending rate that congestion + // is allowed to reach. Default is 0 (no-limit) + k_ESteamNetworkingConfigurationValue_SNP_MinRate = 8, + + // Set to true (non-zero) to enable logging of SNP RTT + // Default is 0 (off) + k_ESteamNetworkingConfigurationValue_SNP_Log_RTT = 9, + + // Set to true (non-zero) to enable logging of SNP Packet + // Default is 0 (off) + k_ESteamNetworkingConfigurationValue_SNP_Log_Packet = 10, + + // Set to true (non-zero) to enable logging of SNP Segments + // Default is 0 (off) + k_ESteamNetworkingConfigurationValue_SNP_Log_Segments = 11, + + // Set to true (non-zero) to enable logging of SNP Feedback + // Default is 0 (off) + k_ESteamNetworkingConfigurationValue_SNP_Log_Feedback = 12, + + // Set to true (non-zero) to enable logging of SNP Relible + // Default is 0 (off) + k_ESteamNetworkingConfigurationValue_SNP_Log_Reliable = 13, + + // Set to true (non-zero) to enable logging of SNP Messages + // Default is 0 (off) + k_ESteamNetworkingConfigurationValue_SNP_Log_Message = 14, + + // Set to true (non-zero) to enable logging of SNP Loss + // Default is 0 (off) + k_ESteamNetworkingConfigurationValue_SNP_Log_Loss = 15, + + // Set to true (non-zero) to enable logging of SNP Throughput + // Default is 0 (off) + k_ESteamNetworkingConfigurationValue_SNP_Log_X = 16, + + // If the first N pings to a port all fail, mark that port as unavailable for + // a while, and try a different one. Some ISPs and routers may drop the first + // packet, so setting this to 1 may greatly disrupt communications. + k_ESteamNetworkingConfigurationValue_ClientConsecutitivePingTimeoutsFailInitial = 17, + + // If N consecutive pings to a port fail, after having received successful + // communication, mark that port as unavailable for a while, and try a + // different one. + k_ESteamNetworkingConfigurationValue_ClientConsecutitivePingTimeoutsFail = 18, + + /// Minimum number of lifetime pings we need to send, before we think our estimate + /// is solid. The first ping to each cluster is very often delayed because of NAT, + /// routers not having the best route, etc. Until we've sent a sufficient number + /// of pings, our estimate is often inaccurate. Keep pinging until we get this + /// many pings. + k_ESteamNetworkingConfigurationValue_ClientMinPingsBeforePingAccurate = 19, + + // Set all steam datagram traffic to originate from the same local port. + // By default, we open up a new UDP socket (on a different local port) + // for each relay. This is not optimal, but it works around some + // routers that don't implement NAT properly. If you have intermittent + // problems talking to relays that might be NAT related, try toggling + // this flag + k_ESteamNetworkingConfigurationValue_ClientSingleSocket = 20, + + // Globally delay all outbound packets by N ms before sending + k_ESteamNetworkingConfigurationValue_FakePacketLag_Send = 21, + + // Globally delay all received packets by N ms before processing + k_ESteamNetworkingConfigurationValue_FakePacketLag_Recv = 22, + + // Don't automatically fail IP connections that don't have strong auth. + // On clients, this means we will attempt the connection even if we don't + // know our SteamID or can't get a cert. On the server, it means that we won't + // automatically reject a connection due to a failure to authenticate. + // (You can examine the incoming connection and decide whether to accept it.) + k_ESteamNetworkingConfigurationValue_IP_Allow_Without_Auth = 23, + + // Number of k_ESteamNetworkingConfigurationValue defines + k_ESteamNetworkingConfigurationValue_Count, +}; + +/// Configuration strings for Steam networking. +/// +/// Most of these are for controlling extend logging or features +/// of various subsystems +enum ESteamNetworkingConfigurationString +{ + // Code of relay cluster to use. If not empty, we will only use relays in that cluster. E.g. 'iad' + k_ESteamNetworkingConfigurationString_ClientForceRelayCluster = 0, + + // For debugging, generate our own (unsigned) ticket, using the specified + // gameserver address. Router must be configured to accept unsigned tickets. + k_ESteamNetworkingConfigurationString_ClientDebugTicketAddress = 1, + + // For debugging. Override list of relays from the config with this set + // (maybe just one). Comma-separated list. + k_ESteamNetworkingConfigurationString_ClientForceProxyAddr = 2, + + // Number of k_ESteamNetworkingConfigurationString defines + k_ESteamNetworkingConfigurationString_Count = k_ESteamNetworkingConfigurationString_ClientForceProxyAddr + 1, +}; + +/// Basically a duplicate of EP2PSend. +enum ESteamNetworkingSendType +{ + // Send an unreliable message. Can be lost. + // The sending API does have some knowledge of the underlying connection, so if there is no NAT-traversal accomplished or + // there is a recognized adjustment happening on the connection, the packet will be batched until the connection is open again. + k_ESteamNetworkingSendType_Unreliable = 0, + + // Reliable message send. Can send up to 512kb of data in a single message. + // Does fragmentation/re-assembly of messages under the hood, as well as a sliding window for efficient sends of large chunks of data. + k_ESteamNetworkingSendType_Reliable = 1, +}; + +/// High level connection status +enum ESteamNetworkingConnectionState +{ + + /// Dummy value used to indicate an error condition in the API. + /// Specified connection doesn't exist or has already been closed. + k_ESteamNetworkingConnectionState_None = 0, + + /// - For connections on the "client" side (initiated locally): + /// We're in the process of trying to establish a connection. + /// Depending on the socket type, we might not even know who they are. + /// Note that it is not possible to tell if we are waiting on the + /// network to complete handshake packets, or for the application layer + /// to accept the connection. + /// + /// - For connections on the "server" side (accepted through listen socket): + /// We have completed a series of basic handshake packets, and the connection + /// is ready to be accepted using AcceptConnection. + /// + /// In either case, any unreliable packets sent now are almost certain + /// to be dropped. Attempts to receive packets are guaranteed to fail. + /// You may send messages if the send mode allows for them to be queued. + /// but if you close the connection before the connection is actually + /// established, any queued messages will be discarded immediately. + /// (We will not attempt to flush the queue and confirm delivery to the + /// remote host, which ordinarily happens when a connection is closed.) + k_ESteamNetworkingConnectionState_Connecting = 1, + + /// We've received communications from our peer (and we know + /// who they are) and are all good. If you close the connection now, + /// we will make our best effort to flush out any reliable sent data that + /// has not been acknowledged by the peer. (But note that this happens + /// from within the application process, so unlike a TCP connection, you are + /// not totally handing it off to the operating system to deal with it.) + k_ESteamNetworkingConnectionState_Connected = 2, + + /// Connection has been closed by our peer, but not closed locally. + /// The connection still exists from an API perspective. You must close the + /// handle to free up resources. If there are any messages in the inbound queue, + /// you may retrieve them. Otherwise, nothing may be done with the connection + /// except to close it. + /// + /// This stats is similar to CLOSE_WAIT in the TCP state machine. + k_ESteamNetworkingConnectionState_ClosedByPeer = 3, + + /// A disruption in the connection has been detected locally. (E.g. timeout, + /// local internet connection disrupted, etc.) + /// + /// The connection still exists from an API perspective. You must close the + /// handle to free up resources. + /// + /// Attempts to send further messages will fail. Any remaining received messages + /// in the queue are available. + k_ESteamNetworkingConnectionState_ProblemDetectedLocally = 4, + +// +// The following values are used internally and will not be returned by any API. +// We document them here to provide a little insight into the state machine that is used +// under the hood. +// + + /// We've disconnected on our side, and from an API perspective the connection is closed. + /// No more data may be sent or received. All reliable data has been flushed, or else + /// we've given up and discarded it. We do not yet know for sure that the client knows + /// the connection has been closed, however, so we're just hanging around so that if we do + /// get a packet from them, we can send them the appropriate packets so that they can + /// know why the connection was closed (and not have to rely on a timeout, which makes + /// it appear as if something is wrong). + /// + /// Note that, unlike TCP connections, Steam networking sessions have additional + /// session identifiers that make the TIME_WAIT state unnecessary. (If another connection + /// is established on the same port, and we get packets from an old session, we are able to + /// identify this and either ignore those packets, or inform the remote host that the old + /// session has been closed, if appropriate). + k_ESteamNetworkingConnectionState_FinWait = -1, + + /// We've disconnected on our side, and from an API perspective the connection is closed. + /// No more data may be sent or received. From a network perspective, however, on the wire, + /// we have not yet given any indication to the peer that the connection is closed. + /// We are in the process of flushing out the last bit of reliable data. Once that is done, + /// we will inform the peer that the connection has been closed, and transition to the + /// FinWait state. + /// + /// Note that no indication is given to the remote host that we have closed the connection, + /// until the data has been flushed. If the remote host attempts to send us data, we will + /// do whatever is necessary to keep the connection alive until it can be closed properly. + /// But in fact the data will be discarded, since there is no way for the application to + /// read it back. Typically this is not a problem, as application protocols that utilize + /// the lingering functionality are designed for the remote host to wait for the response + /// before sending any more data. + k_ESteamNetworkingConnectionState_Linger = -2, + + /// Connection is completely inactive and ready to be destroyed + k_ESteamNetworkingConnectionState_Dead = -3, +}; + +/// Identifier used for a network location point of presence. +/// Typically you won't need to directly manipulate these. +typedef uint32 SteamNetworkingPOPID; + +/// Convert 3- or 4-character ID to 32-bit int. +inline SteamNetworkingPOPID CalculateSteamNetworkingPOPIDFromString( const char *pszCode ) +{ + // OK we made a bad decision when we decided how to pack 3-character codes into a uint32. We'd like to support + // 4-character codes, but we don't want to break compatibility. The migration path has some subtleties that make + // this nontrivial, and there are already some IDs stored in SQL. Ug, so the 4 character code "abcd" will + // be encoded with the digits like "0xddaabbcc" + return + ( uint32(pszCode[3]) << 24U ) + | ( uint32(pszCode[0]) << 16U ) + | ( uint32(pszCode[1]) << 8U ) + | uint32(pszCode[2]); +} + +/// Unpack integer to string representation, including terminating '\0' +template <int N> +inline void GetSteamNetworkingLocationPOPStringFromID( SteamNetworkingPOPID id, char (&szCode)[N] ) +{ + COMPILE_TIME_ASSERT( N >= 5 ); + szCode[0] = ( id >> 16U ); + szCode[1] = ( id >> 8U ); + szCode[2] = ( id ); + szCode[3] = ( id >> 24U ); // See comment above about deep regret and sadness + szCode[4] = 0; +} + +/// A local timestamp. You can subtract two timestamps to get the number of elapsed +/// microseconds. This is guaranteed to increase over time during the lifetime +/// of a process, but not globally across runs. You don't need to worry about +/// the value wrapping around. Note that the underlying clock might not actually have +/// microsecond *resolution*. +typedef int64 SteamNetworkingMicroseconds; + +/// A sequence number. Used to indentify packets and messages +/// and other segments. Typically this is 64 bit internally and +/// has the last 16 bits networked +typedef int64 SteamNetworkingSequenceNumber; + +// maximum amount of pending buffered messages allows +const int k_cbMaxSteamDatagramMessageSize = 512 * 1024; + +/// A message that has been received. +class ISteamNetworkingMessage +{ +public: + + /// You MUST call this when you're done with the object, + /// to free up memory, etc. + virtual void Release() = 0; + + /// Get size of the payload. + inline uint32 GetSize() const { return m_cbSize; } + + /// Get message payload + inline const void *GetData() const { return m_pData; } + + /// Return SteamID that sent this to us. + inline CSteamID GetSenderSteamID() const { return m_steamIDSender; } + + /// Return the channel number the message was received on. + /// (Not used for messages received on "connections" + inline int GetChannel() const { return m_nChannel; } + + /// The socket this came from. (Not used when using the P2P calls) + inline HSteamNetConnection GetConnection() const { return m_conn; } + + /// Get the user data associated with the connection. + /// + /// This is *usually* the same as calling GetConnection() and then + /// fetching the user data associated with that connection, but for + /// the following subtle differences: + /// + /// - This user data will match the connection's user data at the time + /// is captured at the time the message is returned by the API. + /// If you subsequently change the userdata on the connection, + /// this won't be updated. + /// - This is an inline call, so it's *much* faster. + /// - You might have closed the connection, so fetching the user data + /// would not be possible. + inline int64 GetConnectionUserData() const { return m_nConnUserData; } + + /// Get the time that it was received + inline SteamNetworkingMicroseconds GetTimeReceived() const { return m_usecTimeReceived; } + +protected: + CSteamID m_steamIDSender; + void *m_pData; + uint32 m_cbSize; + int m_nChannel; + HSteamNetConnection m_conn; + int64 m_nConnUserData; + SteamNetworkingMicroseconds m_usecTimeReceived; + + inline ~ISteamNetworkingMessage() {}; // Destructor hidden - use Release()! But make it inline and empty, in case you want to derive your own type that satisfies this interface for use in your code. +}; + +/// Object that describes a "location" on the Internet with sufficient +/// detail that we can reasonably estimate an upper bound on the ping between +/// the two hosts, even if a direct route between the hosts is not possible, +/// and the connection must be routed through the Steam Datagram Relay network. +/// This does not contain any information that identifies the host. Indeed, +/// if two hosts are in the same building or otherwise have nearly identical +/// networking characteristics, then it's valid to use the same location +/// object for both of them. +/// +/// NOTE: This object should only be used in memory. If you need to persist +/// it or send it over the wire, convert it to a string representation using +/// the methods in ISteamNetworkingUtils() +struct SteamNetworkPingLocation_t +{ + uint8 m_data[ 64 ]; +}; + +/// Special values that are returned by some functions that return a ping. +const int k_nSteamNetworkingPing_Failed = -1; +const int k_nSteamNetworkingPing_Unknown = -2; + +/// Enumerate various causes of connection termination. These are designed +/// to work sort of like HTTP error codes, in that the numeric range gives you +/// a ballpark as to where the problem is. +enum ESteamNetConnectionEnd +{ + // Invalid/sentinel value + k_ESteamNetConnectionEnd_Invalid = 0, + + // + // Application codes. You can use these codes if you want to + // plumb through application-specific error codes. If you don't need this + // facility, feel free to always use 0, which is a generic + // application-initiated closure. + // + // The distinction between "normal" and "exceptional" termination is + // one you may use if you find useful, but it's not necessary for you + // to do so. The only place where we distinguish between normal and + // exceptional is in connection analytics. If a significant + // proportion of connections terminates in an exceptional manner, + // this can trigger an alert. + // + + // 1xxx: Application ended the connection in a "usual" manner. + // E.g.: user intentionally disconnected from the server, + // gameplay ended normally, etc + k_ESteamNetConnectionEnd_App_Min = 1000, + k_ESteamNetConnectionEnd_App_Generic = k_ESteamNetConnectionEnd_App_Min, + // Use codes in this range for "normal" disconnection + k_ESteamNetConnectionEnd_App_Max = 1999, + + // 2xxx: Application ended the connection in some sort of exceptional + // or unusual manner that might indicate a bug or configuration + // issue. + // + k_ESteamNetConnectionEnd_AppException_Min = 2000, + k_ESteamNetConnectionEnd_AppException_Generic = k_ESteamNetConnectionEnd_AppException_Min, + // Use codes in this range for "unusual" disconnection + k_ESteamNetConnectionEnd_AppException_Max = 2999, + + // + // System codes: + // + + // 3xxx: Connection failed or ended because of problem with the + // local host or their connection to the Internet. + k_ESteamNetConnectionEnd_Local_Min = 3000, + + // You cannot do what you want to do because you're running in offline mode. + k_ESteamNetConnectionEnd_Local_OfflineMode = 3001, + + // We're having trouble contacting many (perhaps all) relays. + // Since it's unlikely that they all went offline at once, the best + // explanation is that we have a problem on our end. Note that we don't + // bother distinguishing between "many" and "all", because in practice, + // it takes time to detect a connection problem, and by the time + // the connection has timed out, we might not have been able to + // actively probe all of the relay clusters, even if we were able to + // contact them at one time. So this code just means that: + // + // * We don't have any recent successful communication with any relay. + // * We have evidence of recent failures to communicate with multiple relays. + k_ESteamNetConnectionEnd_Local_ManyRelayConnectivity = 3002, + + // A hosted server is having trouble talking to the relay + // that the client was using, so the problem is most likely + // on our end + k_ESteamNetConnectionEnd_Local_HostedServerPrimaryRelay = 3003, + + // We're not able to get the network config. This is + // *almost* always a local issue, since the network config + // comes from the CDN, which is pretty darn reliable. + k_ESteamNetConnectionEnd_Local_NetworkConfig = 3004, + + k_ESteamNetConnectionEnd_Local_Max = 3999, + + // 4xxx: Connection failed or ended, and it appears that the + // cause does NOT have to do with the local host or their + // connection to the Internet. It could be caused by the + // remote host, or it could be somewhere in between. + k_ESteamNetConnectionEnd_Remote_Min = 4000, + + // The connection was lost, and as far as we can tell our connection + // to relevant services (relays) has not been disrupted. This doesn't + // mean that the problem is "their fault", it just means that it doesn't + // appear that we are having network issues on our end. + k_ESteamNetConnectionEnd_Remote_Timeout = 4001, + + // Something was invalid with the cert or crypt handshake + // info you gave me, I don't understand or like your key types, + // etc. + k_ESteamNetConnectionEnd_Remote_BadCrypt = 4002, + + // You presented me with a cert that was technically + // valid. But the CA key was missing (and I don't accept + // unsigned certs), or the key isn't one that I trust. + k_ESteamNetConnectionEnd_Remote_CertNotTrusted = 4003, + + k_ESteamNetConnectionEnd_Remote_Max = 4999, + + // 5xxx: Connection failed for some other reason. + k_ESteamNetConnectionEnd_Misc_Min = 5000, + + // A failure that isn't necessarily the result of a software bug, + // but that should happen rarely enough that it isn't worth specifically + // writing UI or making a localized message for. + // The debug string should contain further details. + k_ESteamNetConnectionEnd_Misc_Generic = 5001, + + // Generic failure that is most likely a software bug. + k_ESteamNetConnectionEnd_Misc_InternalError = 5002, + + // The connection to the remote host timed out, but we + // don't know if the problem is on our end, in the middle, + // or on their end. + k_ESteamNetConnectionEnd_Misc_Timeout = 5003, + + // We're having trouble talking to the relevant relay. + // We don't have enough information to say whether the + // problem is on our end or not. + k_ESteamNetConnectionEnd_Misc_RelayConnectivity = 5004, + + // There's some trouble talking to Steam. + k_ESteamNetConnectionEnd_Misc_SteamConnectivity = 5005, + + // A server in a dedicated hosting situation has no relay sessions + // active with which to talk back to a client. (It's the client's + // job to open and maintain those sessions.) + k_ESteamNetConnectionEnd_Misc_NoRelaySessionsToClient = 5006, + + k_ESteamNetConnectionEnd_Misc_Max = 5999, +}; + +/// Max length of diagnostic error message +const int k_cchMaxSteamDatagramErrMsg = 1024; + +/// Used to return English-language diagnostic error messages to caller. +/// (For debugging or spewing to a console, etc. Not intended for UI.) +typedef char SteamDatagramErrMsg[ k_cchMaxSteamDatagramErrMsg ]; + +/// Max length, in bytes (including null terminator) of the reason string +/// when a connection is closed. +const int k_cchSteamNetworkingMaxConnectionCloseReason = 128; + +struct SteamNetConnectionInfo_t +{ + + /// Handle to listen socket this was connected on, or k_HSteamListenSocket_Invalid if we initiated the connection + HSteamListenSocket m_hListenSocket; + + /// Who is on the other end. Depending on the connection type and phase of the connection, we might not know + CSteamID m_steamIDRemote; + + // FIXME - some sort of connection type enum? + + /// Arbitrary user data set by the local application code + int64 m_nUserData; + + /// Remote address. Might be 0 if we don't know it + uint32 m_unIPRemote; + uint16 m_unPortRemote; + uint16 m__pad1; + + /// What data center is the remote host in? (0 if we don't know.) + SteamNetworkingPOPID m_idPOPRemote; + + /// What relay are we using to communicate with the remote host? + /// (0 if not applicable.) + SteamNetworkingPOPID m_idPOPRelay; + + /// Local port that we're bound to for this connection. Might not be applicable + /// for all connection types. + //uint16 m_unPortLocal; + + /// High level state of the connection + int /* ESteamNetworkingConnectionState */ m_eState; + + /// Basic cause of the connection termination or problem. + /// One of ESteamNetConnectionEnd + int /* ESteamNetConnectionEnd */ m_eEndReason; + + /// Human-readable, but non-localized explanation for connection + /// termination or problem. This is intended for debugging / + /// diagnostic purposes only, not to display to users. It might + /// have some details specific to the issue. + char m_szEndDebug[ k_cchSteamNetworkingMaxConnectionCloseReason ]; +}; + +/// Quick connection state, pared down to something you could call +/// more frequently without it being too big of a perf hit. +struct SteamNetworkingQuickConnectionStatus +{ + + /// High level state of the connection + int /* ESteamNetworkingConnectionState */ m_eState; + + /// Current ping (ms) + int m_nPing; + + /// Connection quality measured locally, 0...1. (Percentage of packets delivered + /// end-to-end in order) + float m_flConnectionQualityLocal; + + /// Packet delivery success rate as observed from remote host + float m_flConnectionQualityRemote; + + /// Actual current data rates + float m_flOutPacketsPerSec; + float m_flOutBytesPerSec; + float m_flInPacketsPerSec; + float m_flInBytesPerSec; + + /// Estimate bandwidth of the connection (bytes per second) + int m_nSendRate; + + /// Number of bytes pending to be sent + int m_nPendingBytes; +}; + +#pragma pack( pop ) + +enum ESteamDatagramPartner +{ + k_ESteamDatagramPartner_None = -1, + k_ESteamDatagramPartner_Steam = 0, + k_ESteamDatagramPartner_China = 1, +}; + +#endif // #ifndef STEAMNETWORKINGTYPES |