This commit is contained in:
Riley Labrecque
2014-01-29 13:00:47 +02:00
parent c089bcacfa
commit d733f538e6
36 changed files with 1345 additions and 417 deletions
+34 -2
View File
@@ -1,5 +1,3 @@
This is the Steamworks SDK v1.03
================================================================
Copyright © 1996-2009, Valve Corporation, All rights reserved.
@@ -13,6 +11,40 @@ website at: http://partner.steamgames.com
Revision History:
----------------------------------------------------------------
v1.05 11th June 2009
----------------------------------------------------------------
Matchmaking
* Added the SteamID of the gameserver to the gameserveritem_t structure (returned only by newer game servers)
* Added ISteamUserStats::GetNumberOfCurrentPlayers(), asyncronously returns the number users currently running this game
* Added k_ELobbyComparisonNotEqual comparision functions for filters
* Added option to use comparison functions for string filters
* Added ISteamMatchmaking::AddRequestLobbyListFilterSlotsAvailable( int nSlotsAvailable ) filter function, so you can find a lobby for a group of users to join
* Extended ISteamMatchmaking::CreateLobby() to take the max number of users in the lobby
* Added ISteamMatchmaking::GetLobbyDataCount(), ISteamMatchmaking::GetLobbyDataByIndex() so you can iterate all the data set on a lobby
* Added ISteamMatchmaking::DeleteLobbyData() so you can clear a key from a lobby
* Added ISteamMatchmaking::SetLobbyOwner() so that ownership of a lobby can be transferred
* Added ISteamMatchmaking::SetLobbyJoinable()
* Added ISteamGameServer::SetGameData(), so game server can set more information that can be filtered for in the server pinging API
Networking
* Added a set of connectionless networking functions for easy use for making peer-to-peer (NAT traversal) connections. Includes supports for windowed reliable sendsand fragementation/re-assembly of large packets. See ISteamNetworking.h for more details.
Leaderboards
* Added enum ELeaderboardUploadScoreMethod and changed ISteamUserStats::UploadLeaderboardScore() to take this - lets you force a score to be changed even if it's worse than the prior score
Callbacks
* Added CCallbackManual<> class to steam_api.h, a version of CCallback<> that doesn't register itself automatically in it's the constructor
Downloadable Content
* Added ISteamUser::UserHasLicenseForApp() and ISteamGameServer::UserHasLicenseForApp() to enable checking if a user owns DLC in multiplayer. See the DLC documentation for more info.
Game Overlay
* ISteamFriends::ActivateGameOverlay() now accepts "Stats" and "Achievements"
----------------------------------------------------------------
v1.04 9th Mar 2009
----------------------------------------------------------------
-67
View File
@@ -101,26 +101,9 @@ CGameEngine::CGameEngine( HINSTANCE hInstance, int nShowCommand, int32 nWindowWi
m_dwQuadBufferBatchPos = 0;
m_hTextureWhite = NULL;
m_bDeviceLost = false;
m_bFirstFrame = true;
m_flMaxFPS = 120.0f;
m_hLastTexture = NULL;
m_ulPreviousGameTickCount = 0;
m_ulGameTickCount = 0;
// restrict this main game thread to the first processor, so queryperformance counter won't jump on crappy AMD cpus
DWORD dwThreadAffinityMask = 0x01;
::SetThreadAffinityMask( ::GetCurrentThread(), dwThreadAffinityMask );
LARGE_INTEGER l;
if ( QueryPerformanceFrequency( &l ) )
{
m_ulPerfCounterToMillisecondsDivisor = l.QuadPart/1000;
}
else
{
m_ulPerfCounterToMillisecondsDivisor = 0;
}
m_dwBackgroundColor = D3DCOLOR_ARGB(0, 255, 255, 255 );
if ( !BCreateGameWindow( nShowCommand ) || !m_hWnd )
@@ -518,32 +501,6 @@ void CGameEngine::SetBackgroundColor( short a, short r, short g, short b )
//-----------------------------------------------------------------------------
bool CGameEngine::StartFrame()
{
// Track current game time
// bugbug jmccaskey - use a more reliable timer that won't wrap and has better precision!
if ( m_bFirstFrame )
{
LARGE_INTEGER l;
if ( !QueryPerformanceCounter( &l ) )
OutputDebugString( "QueryPerformanceCounter failed\n" );
m_ulLastQueryPerformanceCounterValue = l.QuadPart;
m_ulPreviousGameTickCount = 0;
m_ulGameTickCount = 0;
m_bFirstFrame = false;
}
else
{
m_ulPreviousGameTickCount = m_ulGameTickCount;
LARGE_INTEGER l;
if ( !QueryPerformanceCounter( &l ) )
OutputDebugString( "QueryPerformanceCounter failed\n" );
m_ulGameTickCount += (l.QuadPart - m_ulLastQueryPerformanceCounterValue)/m_ulPerfCounterToMillisecondsDivisor;
m_ulLastQueryPerformanceCounterValue = l.QuadPart;
}
// Before doing anything else pump messages
MessagePump();
@@ -664,30 +621,6 @@ void CGameEngine::EndFrame()
OutputDebugString( "Present() call failed\n" );
return;
}
// Frame rate limiting
float flDesiredFrameMilliseconds = 1000.0f/m_flMaxFPS;
while( 1 )
{
uint64 ulCurrentTickCount = m_ulGameTickCount;
LARGE_INTEGER l;
if ( !QueryPerformanceCounter( &l ) )
OutputDebugString( "QueryPerformanceCounter failed\n" );
ulCurrentTickCount += (l.QuadPart-m_ulLastQueryPerformanceCounterValue)/m_ulPerfCounterToMillisecondsDivisor;
float flMillisecondsElapsed = (float)(ulCurrentTickCount - m_ulGameTickCount);
if ( flMillisecondsElapsed < flDesiredFrameMilliseconds )
{
// If enough time is left sleep, otherwise just keep spinning so we don't go over the limit...
if ( flDesiredFrameMilliseconds - flMillisecondsElapsed > 2.0f )
Sleep( 2 );
else
continue;
}
else
break;
}
}
//-----------------------------------------------------------------------------
+2 -9
View File
@@ -166,6 +166,8 @@ public:
// way to handle that.
uint64 GetGameTicksFrameDelta() { return m_ulGameTickCount - m_ulPreviousGameTickCount; }
void SetGameTickCount( uint64 ulGameTickCount ) { m_ulPreviousGameTickCount = m_ulGameTickCount; m_ulGameTickCount = ulGameTickCount; }
// Get the game engines hwnd
HWND GetGameHWND() { return m_hWnd; }
@@ -302,15 +304,6 @@ private:
// Game time at the start of the previous frame
uint64 m_ulPreviousGameTickCount;
// Track whether this is the first frame
bool m_bFirstFrame;
// Value to divide from QueryPerformanceCounter results to get milliseconds
uint64 m_ulPerfCounterToMillisecondsDivisor;
// Last value from QueryPerformanceCounter
uint64 m_ulLastQueryPerformanceCounterValue;
// Maximum FPS to run the engine at
float m_flMaxFPS;
+2 -2
View File
@@ -291,7 +291,7 @@ void CLeaderboards::UpdateLeaderboards( CStatsAndAchievements *pStats )
// if the user won, update the leaderboard with the time it took. If the user's previous time was faster, this time will be thrown out.
if ( m_hQuickestWinLeaderboard && SpaceWarClient()->BLocalPlayerWonLastGame() )
{
SteamAPICall_t hSteamAPICall = SteamUserStats()->UploadLeaderboardScore( m_hQuickestWinLeaderboard, (int)pStats->GetGameDurationSeconds(), NULL, 0 );
SteamAPICall_t hSteamAPICall = SteamUserStats()->UploadLeaderboardScore( m_hQuickestWinLeaderboard, k_ELeaderboardUploadScoreMethodKeepBest, (int)pStats->GetGameDurationSeconds(), NULL, 0 );
m_SteamCallResultUploadScore.Set( hSteamAPICall, this, &CLeaderboards::OnUploadScore );
}
@@ -299,7 +299,7 @@ void CLeaderboards::UpdateLeaderboards( CStatsAndAchievements *pStats )
// this value will be thrown out
if ( m_hFeetTraveledLeaderboard )
{
SteamUserStats()->UploadLeaderboardScore( m_hFeetTraveledLeaderboard, (int)pStats->GetGameFeetTraveled(), NULL, 0 );
SteamUserStats()->UploadLeaderboardScore( m_hFeetTraveledLeaderboard, k_ELeaderboardUploadScoreMethodKeepBest, (int)pStats->GetGameFeetTraveled(), NULL, 0 );
}
}
+14
View File
@@ -7,6 +7,8 @@
#include "stdafx.h"
#include "Lobby.h"
#include "SpaceWarClient.h"
#include "p2pauth.h"
//-----------------------------------------------------------------------------
@@ -167,6 +169,18 @@ void CLobby::OnLobbyChatUpdate( LobbyChatUpdate_t *pCallback )
// rebuild the menu
m_pMenu->Rebuild( m_steamIDLobby );
int cLobbyMembers = SteamMatchmaking()->GetNumLobbyMembers( m_steamIDLobby );
for ( int i = 0; i < cLobbyMembers; i++ )
{
CSteamID steamIDLobbyMember = SteamMatchmaking()->GetLobbyMemberByIndex( m_steamIDLobby, i ) ;
// ignore yourself.
if ( SteamUser()->GetSteamID() == steamIDLobbyMember )
continue;
}
}
+1
View File
@@ -18,6 +18,7 @@ class CSpaceWarClient;
class CLobbyBrowserMenu;
class CLobbyMenu;
//-----------------------------------------------------------------------------
// Purpose: Displays the other users in a lobby and allows the game to be started
//-----------------------------------------------------------------------------
+66 -3
View File
@@ -54,6 +54,14 @@ int APIENTRY RealMain(HINSTANCE hInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// Init Steam CEG
if ( !Steamworks_InitCEGLibrary() )
{
OutputDebugString( "Steamworks_InitCEGLibrary() failed\n" );
::MessageBox( NULL, "Steam must be running to play this game (InitDrmLibrary() failed).\n", "Fatal Error", MB_OK );
return -1;
}
// Initialize SteamAPI, if this fails we bail out since we depend on Steam for lots of stuff.
// You don't necessarily have to though if you write your code to check whether all the Steam
// interfaces are NULL before using them and provide alternate paths when they are unavailable.
@@ -76,8 +84,7 @@ int APIENTRY RealMain(HINSTANCE hInstance,
// Generally you should use the default and not call this as users will be most comfortable with
// the default position. The API is provided in case the bottom right creates a serious conflict
// with important UI in your game.
// disabled temporarily until calling issue is resolved
// SteamUtils()->SetOverlayNotificationPosition( k_EPositionTopRight );
SteamUtils()->SetOverlayNotificationPosition( k_EPositionTopRight );
// Look for the +connect ipaddress:port parameter in the command line,
// Steam will pass this when a user has used the Steam Server browser to find
@@ -103,6 +110,8 @@ int APIENTRY RealMain(HINSTANCE hInstance,
pchLobbyID = pchCmdLine + ( pchConnectLobby - pchCmdLine ) + strlen( pchConnectLobbyParam ) + 1;
}
// do a DRM self check
Steamworks_SelfCheck();
// Construct a new instance of the game engine
// bugbug jmccaskey - make screen resolution dynamic, maybe take it on command line?
@@ -147,14 +156,65 @@ int APIENTRY RealMain(HINSTANCE hInstance,
}
}
// test a user specific secret before entering main loop
Steamworks_TestSecret();
static const int m_flMaxFPS = 120;
// restrict this main game thread to the first processor, so queryperformance counter won't jump on crappy AMD cpus
DWORD dwThreadAffinityMask = 0x01;
::SetThreadAffinityMask( ::GetCurrentThread(), dwThreadAffinityMask );
uint64 ulPerfCounterToMillisecondsDivisor;
LARGE_INTEGER l;
::QueryPerformanceFrequency( &l );
ulPerfCounterToMillisecondsDivisor = l.QuadPart/1000;
::QueryPerformanceCounter( &l );
uint64 ulFirstQueryPerformanceCounterValue = l.QuadPart;
uint64 ulGameTickCount = 0;
while( !pGameEngine->BShuttingDown() )
{
if ( pGameEngine->StartFrame() )
{
// update timers
::QueryPerformanceCounter( &l );
ulGameTickCount = (l.QuadPart - ulFirstQueryPerformanceCounterValue) / ulPerfCounterToMillisecondsDivisor;
pGameEngine->SetGameTickCount( ulGameTickCount );
// Run a game frame
pGameClient->RunFrame();
pGameEngine->EndFrame();
// Frame rate limiting
float flDesiredFrameMilliseconds = 1000.0f/m_flMaxFPS;
while( 1 )
{
::QueryPerformanceCounter( &l );
ulGameTickCount = (l.QuadPart - ulFirstQueryPerformanceCounterValue) / ulPerfCounterToMillisecondsDivisor;
float flMillisecondsElapsed = (float)(ulGameTickCount - pGameEngine->GetGameTickCount());
if ( flMillisecondsElapsed < flDesiredFrameMilliseconds )
{
// run networking each frame first
pGameClient->ReceiveNetworkData();
// If enough time is left sleep, otherwise just keep spinning so we don't go over the limit...
if ( flDesiredFrameMilliseconds - flMillisecondsElapsed > 2.0f )
{
Sleep( 2 );
}
else
{
continue;
}
}
else
break;
}
}
}
@@ -167,6 +227,9 @@ int APIENTRY RealMain(HINSTANCE hInstance,
// Shutdown the SteamAPI
SteamAPI_Shutdown();
// Shutdown Steam CEG
Steamworks_TermCEGLibrary();
// exit
return 0;
}
+30 -3
View File
@@ -20,6 +20,7 @@ enum EMessage
k_EMsgServerPassAuthentication = k_EMsgServerBegin+3,
k_EMsgServerUpdateWorld = k_EMsgServerBegin+4,
k_EMsgServerExiting = k_EMsgServerBegin+5,
k_EMsgServerPingResponse = k_EMsgServerBegin+6,
// Client messages
k_EMsgClientBegin = 500,
@@ -27,6 +28,11 @@ enum EMessage
k_EMsgClientBeginAuthentication = k_EMsgClientBegin+2,
k_EMsgClientSendLocalUpdate = k_EMsgClientBegin+3,
k_EMsgClientLeavingServer = k_EMsgClientBegin+4,
k_EMsgClientPing = k_EMsgClientBegin+5,
// P2P messages
k_EMsgP2PBegin = 500,
k_EMsgP2PSendingTicket = k_EMsgP2PBegin+1,
// force 32-bit size enum so the wire protocol doesn't get outgrown later
@@ -78,6 +84,13 @@ struct MsgServerExiting_t
const DWORD m_dwMessageType;
};
// Msg from server to clients when it is exiting
struct MsgServerPingResponse_t
{
MsgServerPingResponse_t() : m_dwMessageType( k_EMsgServerPingResponse ) {}
const DWORD m_dwMessageType;
};
// Msg from client to server when trying to connect
struct MsgClientInitiateConnection_t
{
@@ -94,9 +107,8 @@ struct MsgClientBeginAuthentication_t
uint32 m_uTokenLen;
#ifdef USE_GS_AUTH_API
char m_rgchToken[1024];
#else
CSteamID m_SteamID;
#endif
CSteamID m_SteamID;
};
// Msg from client to server when sending state update
@@ -109,7 +121,6 @@ struct MsgClientSendLocalUpdate_t
ClientSpaceWarUpdateData_t m_ClientUpdateData;
};
// Msg from the client telling the server it is about to leave
struct MsgClientLeavingServer_t
{
@@ -117,5 +128,21 @@ struct MsgClientLeavingServer_t
const DWORD m_dwMessageType;
};
// server ping
struct MsgClientPing_t
{
MsgClientPing_t() : m_dwMessageType( k_EMsgClientPing ) {}
const DWORD m_dwMessageType;
};
// Msg from client to server when trying to connect
struct MsgP2PSendingTicket_t
{
MsgP2PSendingTicket_t() : m_dwMessageType( k_EMsgP2PSendingTicket ) {}
const DWORD m_dwMessageType;
uint32 m_uTokenLen;
char m_rgchToken[1024];
CSteamID m_SteamID;
};
#endif // MESSAGES_H
+3
View File
@@ -29,6 +29,7 @@ CGameServer::CGameServer( gameserveritem_t *pGameServerItem )
m_nServerVersion = pGameServerItem->m_nServerVersion;
strncpy( m_szServerName, pGameServerItem->GetName(), ARRAYSIZE( m_szServerName ) );
_snprintf( m_szServerString, ARRAYSIZE( m_szServerString ), "%s (%i/%i) at %s ping(%d)", pGameServerItem->GetName(), pGameServerItem->m_nPlayers, pGameServerItem->m_nMaxPlayers, pGameServerItem->m_NetAdr.GetConnectionAddressString(), pGameServerItem->m_nPing );
m_steamID = pGameServerItem->m_steamID;
}
@@ -78,6 +79,8 @@ void CServerBrowser::RefreshInternetServers()
m_pMenu->Rebuild( m_ListGameServers, m_bRequestingServers );
Steamworks_TestSecret();
// Allocate some filters, there are some common pre-defined values that can be used:
//
// "gamedir" -- this is used to specify mods inside or a single product/appid
+2 -1
View File
@@ -31,6 +31,7 @@ public:
uint32 GetIP() { return m_unIPAddress; }
int32 GetPort() { return m_nConnectionPort; }
CSteamID GetSteamID() { return m_steamID; }
private:
uint32 m_unIPAddress; // IP address for the server
@@ -46,7 +47,7 @@ private:
int m_nServerVersion; // server version as reported to Steam
char m_szServerName[64]; // Game server name
char m_szServerString[128]; // String to show in server browser
CSteamID m_steamID;
};
class CServerBrowser : public ISteamMatchmakingServerListResponse
+1 -2
View File
@@ -27,8 +27,7 @@ public:
for( iter = List.begin(); iter != List.end(); ++iter )
{
data.m_eStateToTransitionTo = k_EClientGameConnecting;
data.m_unIPAddress = iter->GetIP();
data.m_nConnectionPort = iter->GetPort();
data.m_steamIDGameServer = iter->GetSteamID();
AddMenuItem( MenuItem_t( iter->GetDisplayString(), data ) );
}
+2
View File
@@ -502,6 +502,8 @@ void CShip::SetExploding( bool bExploding )
if ( m_bExploding == bExploding )
return;
Steamworks_TestSecret();
// Track that we are exploding, and disable collision detection
m_bExploding = bExploding;
SetCollisionDetectionDisabled( m_bExploding );
+259 -146
View File
@@ -15,6 +15,7 @@
#include "ServerBrowser.h"
#include "Leaderboards.h"
#include "Lobby.h"
#include "p2pauth.h"
CSpaceWarClient *g_pSpaceWarClient = NULL;
CSpaceWarClient* SpaceWarClient() { return g_pSpaceWarClient; }
@@ -23,7 +24,7 @@ CSpaceWarClient* SpaceWarClient() { return g_pSpaceWarClient; }
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSpaceWarClient::CSpaceWarClient( CGameEngine *pGameEngine, CSteamID steamIDUser ) :
m_SocketStatusCallback( this, &CSpaceWarClient::OnSocketStatusCallback ),
m_CallbackP2PSessionConnectFail( this, &CSpaceWarClient::OnP2PSessionConnectFail ),
m_LobbyGameCreated( this, &CSpaceWarClient::OnLobbyGameCreated ),
m_IPCFailureCallback( this, &CSpaceWarClient::OnIPCFailure ),
m_SteamShutdownCallback( this, &CSpaceWarClient::OnSteamShutdown )
@@ -39,11 +40,11 @@ CSpaceWarClient::CSpaceWarClient( CGameEngine *pGameEngine, CSteamID steamIDUser
m_uPlayerShipIndex = 0;
m_SteamIDLocalUser = steamIDUser;
m_eConnectedStatus = k_EClientNotConnected;
m_hSocketClient = NULL;
m_bTransitionedGameState = false;
m_rgchErrorText[0] = 0;
m_unServerIP = 0;
m_usServerPort = 0;
m_ulPingSentTime = 0;
for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i )
{
@@ -74,11 +75,15 @@ CSpaceWarClient::CSpaceWarClient( CGameEngine *pGameEngine, CSteamID steamIDUser
// Initialize sun
m_pSun = new CSun( pGameEngine );
// initialize P2P auth engine
m_pP2PAuthedGame = new CP2PAuthedGame( m_pGameEngine );
// Create matchmaking menus
m_pServerBrowser = new CServerBrowser( m_pGameEngine );
m_pLobbyBrowser = new CLobbyBrowser( m_pGameEngine );
m_pLobby = new CLobby( m_pGameEngine );
// Init stats
m_pStatsAndAchievements = new CStatsAndAchievements( pGameEngine );
m_pLeaderboards = new CLeaderboards( pGameEngine );
@@ -95,6 +100,13 @@ CSpaceWarClient::~CSpaceWarClient()
{
DisconnectFromServer();
if ( m_pP2PAuthedGame )
{
m_pP2PAuthedGame->EndGame();
delete m_pP2PAuthedGame;
m_pP2PAuthedGame = NULL;
}
if ( m_pServer )
{
delete m_pServer;
@@ -127,13 +139,6 @@ CSpaceWarClient::~CSpaceWarClient()
m_rgpShips[i] = NULL;
}
}
// Close down our socket if it appears to be valid
if ( m_hSocketClient )
{
SteamNetworking()->DestroySocket( m_hSocketClient, true );
m_hSocketClient = NULL;
}
}
@@ -147,9 +152,20 @@ void CSpaceWarClient::DisconnectFromServer()
SteamUser()->TerminateGameConnection( m_unServerIP, m_usServerPort );
MsgClientLeavingServer_t msg;
BSendServerData( (char*)&msg, sizeof(msg), true );
BSendServerData( &msg, sizeof(msg) );
m_eConnectedStatus = k_EClientNotConnected;
}
if ( m_pP2PAuthedGame )
{
m_pP2PAuthedGame->EndGame();
}
// forget the game server ID
if ( m_steamIDGameServer.IsValid() )
{
SteamNetworking()->CloseP2PSessionWithUser( m_steamIDGameServer );
m_steamIDGameServer = CSteamID();
}
}
@@ -160,6 +176,13 @@ void CSpaceWarClient::OnReceiveServerInfo( CSteamID steamIDGameServer, bool bVAC
{
m_eConnectedStatus = k_EClientConnectedPendingAuthentication;
m_pQuitMenu->SetHeading( pchServerName );
m_steamIDGameServer = steamIDGameServer;
// look up the servers IP and Port from the connection
P2PSessionState_t p2pSessionState;
SteamNetworking()->GetP2PSessionState( steamIDGameServer, &p2pSessionState );
m_unServerIP = p2pSessionState.m_nRemoteIP;
m_usServerPort = p2pSessionState.m_nRemotePort;
MsgClientBeginAuthentication_t msg;
#ifdef USE_GS_AUTH_API
@@ -171,10 +194,12 @@ void CSpaceWarClient::OnReceiveServerInfo( CSteamID steamIDGameServer, bool bVAC
msg.m_SteamID = SteamUser()->GetSteamID();
#endif
Steamworks_TestSecret();
if ( msg.m_uTokenLen < 1 )
OutputDebugString( "Warning: Looks like InitiateGameConnection didn't give us a good token\n" );
BSendServerData( (char*)&msg, sizeof(msg), true );
BSendServerData( &msg, sizeof(msg) );
}
@@ -197,6 +222,11 @@ void CSpaceWarClient::OnReceiveServerAuthenticationResponse( bool bSuccess, uint
m_uPlayerShipIndex = uPlayerPosition;
m_eConnectedStatus = k_EClientConnectedAndAuthenticated;
// send a ping, to measure round-trip time
m_ulPingSentTime = m_pGameEngine->GetGameTickCount();
MsgClientPing_t msg;
BSendServerData( &msg, sizeof( msg ) );
}
}
@@ -264,6 +294,56 @@ void CSpaceWarClient::OnReceiveServerUpdate( ServerSpaceWarUpdateData_t UpdateDa
// Update who won last
m_uPlayerWhoWonGame = UpdateData.m_uPlayerWhoWonGame;
if ( m_pP2PAuthedGame )
{
// has the player list changed?
if ( m_pServer )
{
// if i am the server owner i need to auth everyone who wants to play
// assume i am in slot 0, so start at slot 1
for( uint32 i=1; i < MAX_PLAYERS_PER_SERVER; ++i )
{
CSteamID steamIDNew( UpdateData.m_rgPlayerSteamIDs[i] );
if ( steamIDNew == SteamUser()->GetSteamID() )
{
OutputDebugStringA( "Server player slot 0 is not server owner.\n" );
}
else if ( steamIDNew != m_rgSteamIDPlayers[i] )
{
if ( m_rgSteamIDPlayers[i].IsValid() )
{
m_pP2PAuthedGame->PlayerDisconnect( i );
}
if ( steamIDNew.IsValid() )
{
m_pP2PAuthedGame->RegisterPlayer( i, steamIDNew );
}
}
}
}
else
{
// i am just a client, i need to auth the game owner ( slot 0 )
CSteamID steamIDNew( UpdateData.m_rgPlayerSteamIDs[0] );
if ( steamIDNew == SteamUser()->GetSteamID() )
{
OutputDebugStringA( "Server player slot 0 is not server owner.\n" );
}
else if ( steamIDNew != m_rgSteamIDPlayers[0] )
{
if ( m_rgSteamIDPlayers[0].IsValid() )
{
OutputDebugStringA( "Server player slot 0 has disconnected - but thats the server owner.\n" );
m_pP2PAuthedGame->PlayerDisconnect( 0 );
}
if ( steamIDNew.IsValid() )
{
m_pP2PAuthedGame->StartAuthPlayer( 0, steamIDNew );
}
}
}
}
// Update the ships
for( uint32 i=0; i < MAX_PLAYERS_PER_SERVER; ++i )
{
@@ -336,9 +416,9 @@ void CSpaceWarClient::SetConnectionFailureText( const char *pchErrorText )
//-----------------------------------------------------------------------------
// Purpose: Send data to the current server
//-----------------------------------------------------------------------------
bool CSpaceWarClient::BSendServerData( char *pData, uint32 nSizeOfData, bool bSendReliably )
bool CSpaceWarClient::BSendServerData( const void *pData, uint32 nSizeOfData )
{
if ( !SteamNetworking()->SendDataOnSocket( m_hSocketClient, pData, nSizeOfData, bSendReliably ) )
if ( !SteamNetworking()->SendP2PPacket( m_steamIDGameServer, pData, nSizeOfData, k_EP2PSendUnreliable ) )
{
OutputDebugString( "Failed sending data to server\n" );
return false;
@@ -364,14 +444,10 @@ void CSpaceWarClient::InitiateServerConnection( uint32 unServerAddress, const in
// and so we will retry at appropriate intervals if packets drop
m_ulLastNetworkDataReceivedTime = m_ulLastConnectionAttemptRetryTime = m_pGameEngine->GetGameTickCount();
// Create UDP socket for the client to talk to the server over
m_hSocketClient = SteamNetworking()->CreateConnectionSocket( unServerAddress, nPort, 10 );
if ( !m_hSocketClient )
{
OutputDebugString( "Failed creating socket for CSpaceWarClient -- won't be able to talk to server\n" );
}
// wait for the callback before initiating connection
// ping the server to find out what it's steamID is
m_unServerIP = unServerAddress;
m_usServerPort = (uint16)nPort;
m_GameServerPing.RetrieveSteamIDFromGameServer( this, m_unServerIP, m_usServerPort );
}
@@ -387,52 +463,28 @@ void CSpaceWarClient::InitiateServerConnection( CSteamID steamIDGameServer )
SetGameState( k_EClientGameConnecting );
m_steamIDGameServer = steamIDGameServer;
// Update when we last retried the connection, as well as the last packet received time so we won't timeout too soon,
// and so we will retry at appropriate intervals if packets drop
m_ulLastNetworkDataReceivedTime = m_ulLastConnectionAttemptRetryTime = m_pGameEngine->GetGameTickCount();
// Create UDP socket for the client to talk to the server over
m_hSocketClient = SteamNetworking()->CreateP2PConnectionSocket( steamIDGameServer, 0, 10, true );
if ( !m_hSocketClient )
{
OutputDebugString( "Failed creating socket for CSpaceWarClient -- won't be able to talk to server\n" );
}
// wait for the callback before initiating connection
// send the packet to the server
MsgClientInitiateConnection_t msg;
BSendServerData( &msg, sizeof( msg ) );
}
//-----------------------------------------------------------------------------
// Purpose: steam callback, trigger when our connection state changes
//-----------------------------------------------------------------------------
void CSpaceWarClient::OnSocketStatusCallback( SocketStatusCallback_t *pCallback )
void CSpaceWarClient::OnP2PSessionConnectFail( P2PSessionConnectFail_t *pCallback )
{
// make sure the callback is for our socket
if ( pCallback->m_hSocket != m_hSocketClient )
return;
// Update out network data received time even though we haven't really received anything yet,
// this will help us make sure we don't time out prematurely during the handshake
m_ulLastNetworkDataReceivedTime = m_pGameEngine->GetGameTickCount();
// if we've finished connecting to the server, send the connect message
if ( pCallback->m_eSNetSocketState == k_ESNetSocketStateConnected )
{
MsgClientInitiateConnection_t msg;
BSendServerData( (char*)&msg, sizeof( msg ), true );
}
// handling being disconnect from the server signal via network breakage, or the remote end dying
if ( pCallback->m_eSNetSocketState == k_ESNetSocketStateRemoteEndDisconnected
|| pCallback->m_eSNetSocketState == k_ESNetSocketStateConnectionBroken )
if ( pCallback->m_steamIDRemote == m_steamIDGameServer )
{
// failed, error out
OutputDebugString( "Failed to make P2P connection, quiting server" );
OnReceiveServerExiting();
m_hSocketClient = NULL;
}
if ( pCallback->m_eSNetSocketState >= k_ESNetSocketStateDisconnecting )
{
m_hSocketClient = NULL;
}
}
@@ -442,9 +494,6 @@ void CSpaceWarClient::OnSocketStatusCallback( SocketStatusCallback_t *pCallback
//-----------------------------------------------------------------------------
void CSpaceWarClient::ReceiveNetworkData()
{
if ( !m_hSocketClient )
return;
char rgchRecvBuf[MAX_SPACEWAR_PACKET_SIZE];
char *pchRecvBuf = rgchRecvBuf;
uint32 cubMsgSize;
@@ -458,90 +507,115 @@ void CSpaceWarClient::ReceiveNetworkData()
}
// see if there is any data waiting on the socket
if ( !SteamNetworking()->RetrieveDataFromSocket( m_hSocketClient, rgchRecvBuf, sizeof( rgchRecvBuf ), &cubMsgSize ) )
if ( !SteamNetworking()->IsP2PPacketAvailable( &cubMsgSize ) )
break;
// not enough space in buffer, so message won't have been popped off of list
// not enough space in default buffer
// alloc custom size and try again
if ( cubMsgSize > sizeof(rgchRecvBuf) )
{
pchRecvBuf = (char *)malloc( cubMsgSize );
SteamNetworking()->RetrieveDataFromSocket( m_hSocketClient, pchRecvBuf, cubMsgSize, &cubMsgSize );
}
CSteamID steamIDRemote;
if ( !SteamNetworking()->ReadP2PPacket( pchRecvBuf, cubMsgSize, &cubMsgSize, &steamIDRemote ) )
break;
m_ulLastNetworkDataReceivedTime = m_pGameEngine->GetGameTickCount();
// make sure we're connected
if ( m_eConnectedStatus == k_EClientNotConnected && m_eGameState != k_EClientGameConnecting )
continue;
if ( cubMsgSize < sizeof( EMessage ) )
// see if it's from the game server
if ( steamIDRemote == m_steamIDGameServer )
{
OutputDebugString( "Got garbage on client socket, too short\n" );
}
m_ulLastNetworkDataReceivedTime = m_pGameEngine->GetGameTickCount();
EMessage *pEMsg = (EMessage*)pchRecvBuf;
switch ( *pEMsg )
// make sure we're connected
if ( m_eConnectedStatus == k_EClientNotConnected && m_eGameState != k_EClientGameConnecting )
continue;
if ( cubMsgSize < sizeof( EMessage ) )
{
OutputDebugString( "Got garbage on client socket, too short\n" );
}
EMessage *pEMsg = (EMessage*)pchRecvBuf;
switch ( *pEMsg )
{
case k_EMsgServerSendInfo:
{
if ( cubMsgSize != sizeof( MsgServerSendInfo_t ) )
{
OutputDebugString ("Bad server info msg\n" );
continue;
}
MsgServerSendInfo_t *pMsg = (MsgServerSendInfo_t*)pchRecvBuf;
// pull the IP address of the user from the socket
OnReceiveServerInfo( CSteamID( pMsg->m_ulSteamIDServer ), pMsg->m_bIsVACSecure, pMsg->m_rgchServerName );
}
break;
case k_EMsgServerPassAuthentication:
{
if ( cubMsgSize != sizeof( MsgServerPassAuthentication_t ) )
{
OutputDebugString( "Bad accept connection msg\n" );
continue;
}
MsgServerPassAuthentication_t *pMsg = (MsgServerPassAuthentication_t*)pchRecvBuf;
// Our game client doesn't really care about whether the server is secure, or what its
// steamID is, but if it did we would pass them in here as they are part of the accept message
OnReceiveServerAuthenticationResponse( true, pMsg->m_uPlayerPosition );
}
break;
case k_EMsgServerFailAuthentication:
{
OnReceiveServerAuthenticationResponse( false, 0 );
}
break;
case k_EMsgServerUpdateWorld:
{
if ( cubMsgSize != sizeof( MsgServerUpdateWorld_t ) )
{
OutputDebugString( "Bad server world update msg\n" );
continue;
}
MsgServerUpdateWorld_t *pMsg = (MsgServerUpdateWorld_t*)pchRecvBuf;
OnReceiveServerUpdate( pMsg->m_ServerUpdateData );
}
break;
case k_EMsgServerExiting:
{
if ( cubMsgSize != sizeof( MsgServerExiting_t ) )
{
OutputDebugString( "Bad server exiting msg\n" );
}
OnReceiveServerExiting();
}
break;
case k_EMsgServerPingResponse:
{
uint64 ulTimePassedMS = m_pGameEngine->GetGameTickCount() - m_ulPingSentTime;
char rgchT[256];
_snprintf( rgchT, sizeof(rgchT), "Round-trip ping time to server %d ms\n", (int)ulTimePassedMS );
OutputDebugString( rgchT );
m_ulPingSentTime = 0;
}
break;
default:
OutputDebugString( "Unhandled message from server\n" );
break;
}
}
else
{
case k_EMsgServerSendInfo:
{
if ( cubMsgSize != sizeof( MsgServerSendInfo_t ) )
{
OutputDebugString ("Bad server info msg\n" );
continue;
}
MsgServerSendInfo_t *pMsg = (MsgServerSendInfo_t*)pchRecvBuf;
// pull the IP address of the user from the socket
SteamNetworking()->GetSocketInfo( m_hSocketClient, NULL, NULL, &m_unServerIP, &m_usServerPort );
OnReceiveServerInfo( CSteamID( pMsg->m_ulSteamIDServer ), pMsg->m_bIsVACSecure, pMsg->m_rgchServerName );
}
break;
case k_EMsgServerPassAuthentication:
{
if ( cubMsgSize != sizeof( MsgServerPassAuthentication_t ) )
{
OutputDebugString( "Bad accept connection msg\n" );
continue;
}
MsgServerPassAuthentication_t *pMsg = (MsgServerPassAuthentication_t*)pchRecvBuf;
// Our game client doesn't really care about whether the server is secure, or what its
// steamID is, but if it did we would pass them in here as they are part of the accept message
OnReceiveServerAuthenticationResponse( true, pMsg->m_uPlayerPosition );
}
break;
case k_EMsgServerFailAuthentication:
{
OnReceiveServerAuthenticationResponse( false, 0 );
}
break;
case k_EMsgServerUpdateWorld:
{
if ( cubMsgSize != sizeof( MsgServerUpdateWorld_t ) )
{
OutputDebugString( "Bad server world update msg\n" );
continue;
}
MsgServerUpdateWorld_t *pMsg = (MsgServerUpdateWorld_t*)pchRecvBuf;
OnReceiveServerUpdate( pMsg->m_ServerUpdateData );
}
break;
case k_EMsgServerExiting:
{
if ( cubMsgSize != sizeof( MsgServerExiting_t ) )
{
OutputDebugString( "Bad server exiting msg\n" );
}
OnReceiveServerExiting();
}
break;
default:
OutputDebugString( "Unhandled message from server\n" );
break;
EMessage *pEMsg = (EMessage*)pchRecvBuf;
m_pP2PAuthedGame->HandleMessage( *pEMsg, pchRecvBuf );
}
}
// if we're running a server, do that as well
if ( m_pServer )
{
m_pServer->ReceiveNetworkData();
}
}
@@ -550,6 +624,8 @@ void CSpaceWarClient::ReceiveNetworkData()
//-----------------------------------------------------------------------------
void CSpaceWarClient::OnReceiveServerExiting()
{
if ( m_pP2PAuthedGame )
m_pP2PAuthedGame->EndGame();
if ( m_eGameState != k_EClientGameActive )
return;
@@ -611,6 +687,18 @@ void CSpaceWarClient::OnLobbyEntered( LobbyEnter_t *pCallback, bool bIOFailure )
}
void CSpaceWarClient::OnLobbyDataChange( LobbyDataUpdate_t *pCallback, bool bIOFailure )
{
OutputDebugStringA( "OnLobbyDataChange ");
CSteamID steamIDLobbyMember( pCallback->m_ulSteamIDMember );
}
void CSpaceWarClient::OnLobbyChatUpdate( LobbyChatUpdate_t *pCallback, bool bIOFailure )
{
CSteamID steamIDLobbyMember( pCallback->m_ulSteamIDUserChanged );
CSteamID steamIDLobbyMember2( pCallback->m_ulSteamIDMakingChange );
OutputDebugStringA( "OnLobbyChatUpdate ");
}
//-----------------------------------------------------------------------------
// Purpose: Joins a game from a lobby
@@ -625,11 +713,6 @@ void CSpaceWarClient::OnLobbyGameCreated( LobbyGameCreated_t *pCallback )
{
InitiateServerConnection( CSteamID( pCallback->m_ulSteamIDGameServer ) );
}
else
{
// fall back to using the IP address directly
InitiateServerConnection( pCallback->m_unIP, pCallback->m_usPort );
}
}
@@ -701,7 +784,7 @@ void CSpaceWarClient::OnGameStateChanged( EClientGameState eGameStateNew )
if ( !m_SteamCallResultLobbyCreated.IsActive() )
{
// ask steam to create a lobby
SteamAPICall_t hSteamAPICall = SteamMatchmaking()->CreateLobby( k_ELobbyTypePublic /* public lobby, anyone can find it */ );
SteamAPICall_t hSteamAPICall = SteamMatchmaking()->CreateLobby( k_ELobbyTypePublic /* public lobby, anyone can find it */, 4 );
// set the function to call when this completes
m_SteamCallResultLobbyCreated.Set( hSteamAPICall, this, &CSpaceWarClient::OnLobbyCreated );
}
@@ -838,11 +921,9 @@ void CSpaceWarClient::RunFrame()
if ( m_pServer && m_pServer->IsConnectedToSteam() )
{
// server is up; tell everyone else to connect
SteamMatchmaking()->SetLobbyGameServer( m_steamIDLobby, m_pServer->GetIP(), m_pServer->GetPort(), m_pServer->GetSteamID() );
SteamMatchmaking()->SetLobbyGameServer( m_steamIDLobby, 0, 0, m_pServer->GetSteamID() );
// start connecting ourself via localhost (this will automatically leave the lobby)
uint32 unIPAddress = ( 1 ) + ( 0 << 8 ) + ( 0 << 16 ) + ( 127 << 24 );
InitiateServerConnection( unIPAddress, m_pServer->GetPort() );
InitiateServerConnection( m_pServer->GetSteamID() );
}
break;
@@ -884,9 +965,12 @@ void CSpaceWarClient::RunFrame()
// Check if we've waited too long and should time out the connection
if ( m_pGameEngine->GetGameTickCount() - m_ulStateTransitionTime > MILLISECONDS_CONNECTION_TIMEOUT )
{
if ( m_pP2PAuthedGame )
m_pP2PAuthedGame->EndGame();
if ( m_eConnectedStatus == k_EClientConnectedAndAuthenticated )
SteamUser()->TerminateGameConnection( m_unServerIP, m_usServerPort );
m_GameServerPing.CancelPing();
SetConnectionFailureText( "Timed out connecting to game server" );
SetGameState( k_EClientGameConnectionFailure );
}
@@ -941,14 +1025,13 @@ void CSpaceWarClient::RunFrame()
if ( !m_pServer )
{
m_pServer = new CSpaceWarServer( m_pGameEngine );
uint32 unIPAddress = ( 1 ) + ( 0 << 8 ) + ( 0 << 16 ) + ( 127 << 24 );
InitiateServerConnection( unIPAddress, SPACEWAR_SERVER_PORT );
}
else
{
OutputDebugString( "Server already started locally when in k_EClientGameStartServer state!\n" );
}
if ( m_pServer && m_pServer->IsConnectedToSteam() )
{
// server is ready, connect to it
InitiateServerConnection( m_pServer->GetSteamID() );
}
break;
case k_EClientGameDraw:
case k_EClientGameWinner:
@@ -1004,7 +1087,37 @@ void CSpaceWarClient::RunFrame()
// If this fails, it probably just means its not time to send an update yet
if ( m_rgpShips[ m_uPlayerShipIndex ]->BGetClientUpdateData( &msg.m_ClientUpdateData ) )
BSendServerData( (char*)&msg, sizeof( msg ), false );
BSendServerData( &msg, sizeof( msg ) );
}
if ( m_pP2PAuthedGame )
{
if ( m_pServer )
{
// Now if we are the owner of the game, lets make sure all of our players are legit.
// if they are not, we tell the server to kick them off
// Start at 1 to skip myself
for ( int i = 1; i < MAX_PLAYERS_PER_SERVER; i++ )
{
if ( m_pP2PAuthedGame->m_rgpP2PAuthPlayer[i] && !m_pP2PAuthedGame->m_rgpP2PAuthPlayer[i]->BIsAuthOk() )
{
m_pServer->KickPlayerOffServer( m_pP2PAuthedGame->m_rgpP2PAuthPlayer[i]->m_steamID );
}
}
}
else
{
// If we are not the owner of the game, lets make sure the game owner is legit
// if he is not, we leave the game
if ( m_pP2PAuthedGame->m_rgpP2PAuthPlayer[0] )
{
if ( !m_pP2PAuthedGame->m_rgpP2PAuthPlayer[0]->BIsAuthOk() )
{
// leave the game
SetGameState( k_EClientGameMenu );
}
}
}
}
// If we've started a local server run it
+65 -9
View File
@@ -28,6 +28,8 @@ class CServerBrowser;
class CLobbyBrowser;
class CLobby;
class CLeaderboards;
class CP2PAuthPlayer;
class CP2PAuthedGame;
// Height of the HUD font
#define HUD_FONT_HEIGHT 18
@@ -47,8 +49,7 @@ enum EClientConnectionState
struct ServerBrowserMenuData_t
{
EClientGameState m_eStateToTransitionTo;
uint32 m_unIPAddress;
int32 m_nConnectionPort;
CSteamID m_steamIDGameServer;
};
// a lobby as shown in the find lobbies menu
@@ -92,11 +93,11 @@ public:
void ReceiveNetworkData();
// Connect to a server at a given IP address or game server steamID
void InitiateServerConnection( uint32 unServerAddress, const int32 nPort );
void InitiateServerConnection( CSteamID steamIDGameServer );
void InitiateServerConnection( uint32 unServerAddress, const int32 nPort );
// Send data to a client at the given ship index
bool BSendServerData( char *pData, uint32 nSizeOfData, bool bSendReliably );
bool BSendServerData( const void *pData, uint32 nSizeOfData );
// Menu callback handler (handles a bunch of menus that just change state with no extra data)
void OnMenuSelection( EClientGameState eState ) { SetGameState( eState ); }
@@ -106,7 +107,7 @@ public:
{
if ( selection.m_eStateToTransitionTo == k_EClientGameConnecting )
{
InitiateServerConnection( selection.m_unIPAddress, selection.m_nConnectionPort );
InitiateServerConnection( selection.m_steamIDGameServer );
}
else
{
@@ -122,6 +123,8 @@ public:
SteamAPICall_t hSteamAPICall = SteamMatchmaking()->JoinLobby( selection.m_steamIDLobby );
// set the function to call when this API completes
m_SteamCallResultLobbyEntered.Set( hSteamAPICall, this, &CSpaceWarClient::OnLobbyEntered );
m_SteamCallResultLobbyDataChange.Set( hSteamAPICall, this, &CSpaceWarClient::OnLobbyDataChange );
m_SteamCallResultLobbyChatUpdate.Set( hSteamAPICall, this, &CSpaceWarClient::OnLobbyChatUpdate );
}
SetGameState( selection.m_eStateToTransitionTo );
@@ -232,16 +235,62 @@ private:
// Time we last got data from the server
uint64 m_ulLastNetworkDataReceivedTime;
// Time when we sent our ping
uint64 m_ulPingSentTime;
// Text to display if we are in an error state
char m_rgchErrorText[256];
// Socket to use when communicating with servers
SNetSocket_t m_hSocketClient;
// Server address data
CSteamID m_steamIDGameServer;
uint32 m_unServerIP;
uint16 m_usServerPort;
// simple class to marshal callbacks from pinging a game server
class CGameServerPing : public ISteamMatchmakingPingResponse
{
public:
CGameServerPing()
{
m_hGameServerQuery = HSERVERQUERY_INVALID;
m_pSpaceWarsClient = NULL;
}
void RetrieveSteamIDFromGameServer( CSpaceWarClient *pSpaceWarClient, uint32 unIP, uint16 unPort )
{
m_pSpaceWarsClient = pSpaceWarClient;
m_hGameServerQuery = SteamMatchmakingServers()->PingServer( unIP, unPort, this );
}
void CancelPing()
{
m_hGameServerQuery = HSERVERQUERY_INVALID;
}
// Server has responded successfully and has updated data
virtual void ServerResponded( gameserveritem_t &server )
{
if ( m_hGameServerQuery != HSERVERQUERY_INVALID && server.m_steamID.IsValid() )
{
m_pSpaceWarsClient->InitiateServerConnection( server.m_steamID );
}
m_hGameServerQuery = HSERVERQUERY_INVALID;
}
// Server failed to respond to the ping request
virtual void ServerFailedToRespond()
{
m_hGameServerQuery = HSERVERQUERY_INVALID;
}
private:
HServerQuery m_hGameServerQuery; // we're ping a game server, so we can convert IP:Port to a steamID
CSpaceWarClient *m_pSpaceWarsClient;
};
CGameServerPing m_GameServerPing;
// Track whether we are connected to a server (and what specific state that connection is in)
EClientConnectionState m_eConnectedStatus;
@@ -281,6 +330,11 @@ private:
void OnLobbyEntered( LobbyEnter_t *pCallback, bool bIOFailure );
CCallResult<CSpaceWarClient, LobbyEnter_t> m_SteamCallResultLobbyEntered;
void OnLobbyDataChange( LobbyDataUpdate_t *pCallback, bool bIOFailure );
CCallResult<CSpaceWarClient, LobbyDataUpdate_t> m_SteamCallResultLobbyDataChange;
void OnLobbyChatUpdate( LobbyChatUpdate_t *pCallback, bool bIOFailure );
CCallResult<CSpaceWarClient, LobbyChatUpdate_t> m_SteamCallResultLobbyChatUpdate;
// callback for when the lobby game server has started
STEAM_CALLBACK( CSpaceWarClient, OnLobbyGameCreated, LobbyGameCreated_t, m_LobbyGameCreated );
@@ -289,9 +343,11 @@ private:
// local lobby display
CLobby *m_pLobby;
// p2p game auth manager
CP2PAuthedGame *m_pP2PAuthedGame;
// connection handler
STEAM_CALLBACK( CSpaceWarClient, OnSocketStatusCallback, SocketStatusCallback_t, m_SocketStatusCallback );
STEAM_CALLBACK( CSpaceWarClient, OnP2PSessionConnectFail, P2PSessionConnectFail_t, m_CallbackP2PSessionConnectFail );
// ipc failure handler
STEAM_CALLBACK( CSpaceWarClient, OnIPCFailure, IPCFailure_t, m_IPCFailureCallback );
+80 -113
View File
@@ -21,7 +21,8 @@ CSpaceWarServer::CSpaceWarServer( CGameEngine *pGameEngine ) :
m_CallbackGSClientApprove( this, &CSpaceWarServer::OnGSClientApprove ),
m_CallbackGSClientDeny( this, &CSpaceWarServer::OnGSClientDeny ),
m_CallbackGSClientKick( this, &CSpaceWarServer::OnGSClientKick ),
m_CallbackSocketStatus( this, &CSpaceWarServer::OnSocketStatusCallback )
m_CallbackP2PSessionConnectFail( this, &CSpaceWarServer::OnP2PSessionConnectFail ),
m_CallbackP2PSessionRequest( this, &CSpaceWarServer::OnP2PSessionRequest )
{
m_bConnectedToSteam = false;
@@ -92,21 +93,8 @@ CSpaceWarServer::CSpaceWarServer( CGameEngine *pGameEngine ) :
// Initialize sun
m_pSun = new CSun( pGameEngine );
// We don't create our socket to listen for clients yet, because we have to be logged
// into Steam and have our own game server SteamID before we can really handle them.
//
// We'll setup our socket and start listening after that happens.
m_hSocketServer = NULL;
// Initialize ships
ResetPlayerShips();
// Start listening for client connections, as we now have enough info to really handle them
m_hSocketServer = SteamGameServerNetworking()->CreateListenSocket( 0, 0, SPACEWAR_SERVER_PORT, true );
if ( !m_hSocketServer )
{
OutputDebugString( "Failed creating socket for CSpaceWarServer -- no one will be able to connect\n" );
}
}
@@ -140,39 +128,38 @@ CSpaceWarServer::~CSpaceWarServer()
SteamGameServer()->LogOff();
#endif
// Close down our socket if it appears to be valid
if ( m_hSocketServer )
{
SteamGameServerNetworking()->DestroyListenSocket( m_hSocketServer, true );
}
// release our reference to the steam client library
SteamGameServer_Shutdown();
}
//-----------------------------------------------------------------------------
// Purpose: Handle sending data to a client at a given index
// Purpose: Handle clients connecting
//-----------------------------------------------------------------------------
void CSpaceWarServer::OnSocketStatusCallback( SocketStatusCallback_t *pCallback )
void CSpaceWarServer::OnP2PSessionRequest( P2PSessionRequest_t *pCallback )
{
// make sure the callback involves our listen socket (the callbacks go to all listeners, so you have to filter)
if ( pCallback->m_hListenSocket != m_hSocketServer )
return;
// we'll accept a connection from anyone
SteamGameServerNetworking()->AcceptP2PSessionWithUser( pCallback->m_steamIDRemote );
}
// lookup disconnect users
if ( pCallback->m_eSNetSocketState >= k_ESNetSocketStateDisconnecting )
//-----------------------------------------------------------------------------
// Purpose: Handle clients disconnecting
//-----------------------------------------------------------------------------
void CSpaceWarServer::OnP2PSessionConnectFail( P2PSessionConnectFail_t *pCallback )
{
// socket has closed, kick the user associated with it
for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i )
{
// socket has closed, kick the user associated with it
for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i )
{
// If there is no ship, skip
if ( !m_rgClientData[i].m_bActive )
continue;
// If there is no ship, skip
if ( !m_rgClientData[i].m_bActive )
continue;
if ( m_rgClientData[i].m_hSocket == pCallback->m_hSocket )
{
OutputDebugString( "Disconnected dropped user\n" );
RemovePlayerFromServer( i );
break;
}
if ( m_rgClientData[i].m_SteamIDUser == pCallback->m_steamIDRemote )
{
OutputDebugString( "Disconnected dropped user\n" );
RemovePlayerFromServer( i );
break;
}
}
}
@@ -187,7 +174,7 @@ bool CSpaceWarServer::BSendDataToClient( uint32 uShipIndex, char *pData, uint32
if ( uShipIndex >= MAX_PLAYERS_PER_SERVER )
return false;
if ( !SteamGameServerNetworking()->SendDataOnSocket( m_rgClientData[uShipIndex].m_hSocket, pData, nSizeOfData, false ) )
if ( !SteamGameServerNetworking()->SendP2PPacket( m_rgClientData[uShipIndex].m_SteamIDUser, pData, nSizeOfData, k_EP2PSendUnreliable ) )
{
OutputDebugString( "Failed sending data to a client\n" );
return false;
@@ -206,7 +193,7 @@ bool CSpaceWarServer::BSendDataToPendingClient( uint32 uShipIndex, char *pData,
if ( uShipIndex >= MAX_PLAYERS_PER_SERVER )
return false;
if ( !SteamGameServerNetworking()->SendDataOnSocket( m_rgPendingClientData[uShipIndex].m_hSocket, pData, nSizeOfData, false ) )
if ( !SteamGameServerNetworking()->SendP2PPacket( m_rgPendingClientData[uShipIndex].m_SteamIDUser, pData, nSizeOfData, k_EP2PSendReliable ) )
{
OutputDebugString( "Failed sending data to a pending client\n" );
return false;
@@ -216,30 +203,15 @@ bool CSpaceWarServer::BSendDataToPendingClient( uint32 uShipIndex, char *pData,
}
//-----------------------------------------------------------------------------
// Purpose: Handle sending data to a client at a given address
//-----------------------------------------------------------------------------
bool CSpaceWarServer::BSendDataToClientOnSocket( SNetSocket_t hSocket, char *pData, uint32 nSizeOfData )
{
if ( !SteamGameServerNetworking()->SendDataOnSocket( hSocket, pData, nSizeOfData, false ) )
{
OutputDebugString( "Failed sending data to a client\n" );
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handle a new client connecting
//-----------------------------------------------------------------------------
void CSpaceWarServer::OnClientBeginAuthentication( SNetSocket_t hSocketClient, void *pToken, uint32 uTokenLen )
void CSpaceWarServer::OnClientBeginAuthentication( CSteamID steamIDClient, void *pToken, uint32 uTokenLen )
{
// First, check this isn't a duplicate and we already have a user logged on from the same ip/port combo
for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i )
{
if ( m_rgClientData[i].m_hSocket == hSocketClient )
if ( m_rgClientData[i].m_SteamIDUser == steamIDClient )
{
// We already logged them on... (should maybe tell them again incase they don't know?)
return;
@@ -261,7 +233,7 @@ void CSpaceWarServer::OnClientBeginAuthentication( SNetSocket_t hSocketClient, v
if ( nPendingOrActivePlayerCount >= MAX_PLAYERS_PER_SERVER )
{
MsgServerFailAuthentication_t msg;
BSendDataToClientOnSocket( hSocketClient, (char*)&msg, sizeof( msg ) );
SteamGameServerNetworking()->SendP2PPacket( steamIDClient, &msg, sizeof( msg ), k_EP2PSendReliable );
}
// If we get here there is room, add the player as pending
@@ -269,20 +241,20 @@ void CSpaceWarServer::OnClientBeginAuthentication( SNetSocket_t hSocketClient, v
{
if ( !m_rgPendingClientData[i].m_bActive )
{
m_rgPendingClientData[i].m_hSocket = hSocketClient;
m_rgPendingClientData[i].m_ulTickCountLastData = m_pGameEngine->GetGameTickCount();
// pull the IP address of the user from the socket
uint32 unIPClient = 0;
SteamGameServerNetworking()->GetSocketInfo( hSocketClient, NULL, NULL, &unIPClient, NULL );
#ifdef USE_GS_AUTH_API
// authenticate the user with the Steam back-end servers
m_rgPendingClientData[i].m_bActive = SteamGameServer()->SendUserConnectAndAuthenticate( unIPClient, pToken, uTokenLen, &m_rgPendingClientData[i].m_SteamIDUser );
// we use the server-seen IP address of the client as an extra bit of data for the authentication
P2PSessionState_t p2pSessionState;
SteamNetworking()->GetP2PSessionState( m_rgPendingClientData[i].m_SteamIDUser, &p2pSessionState );
m_rgPendingClientData[i].m_bActive = SteamGameServer()->SendUserConnectAndAuthenticate( p2pSessionState.m_nRemoteIP, pToken, uTokenLen, &m_rgPendingClientData[i].m_SteamIDUser );
if ( !m_rgPendingClientData[i].m_bActive )
{
MsgServerFailAuthentication_t msg;
BSendDataToClientOnSocket( hSocketClient, (char*)&msg, sizeof( msg ) );
SteamGameServerNetworking()->SendP2PPacket( m_rgPendingClientData[i].m_SteamIDUser, &msg, sizeof( msg ), k_EP2PSendReliable );
}
break;
#else
@@ -317,7 +289,7 @@ void CSpaceWarServer::OnAuthCompleted( bool bAuthSuccessful, uint32 iPendingAuth
#endif
// Send a deny for the client, and zero out the pending data
MsgServerFailAuthentication_t msg;
BSendDataToClientOnSocket( m_rgPendingClientData[iPendingAuthIndex].m_hSocket, (char*)&msg, sizeof( msg ) );
SteamGameServerNetworking()->SendP2PPacket( m_rgPendingClientData[iPendingAuthIndex].m_SteamIDUser, &msg, sizeof( msg ), k_EP2PSendReliable );
memset( &m_rgPendingClientData[iPendingAuthIndex], 0, sizeof( ClientConnectionData_t ) );
return;
}
@@ -511,13 +483,10 @@ void CSpaceWarServer::SetGameState( EServerGameState eState )
//-----------------------------------------------------------------------------
void CSpaceWarServer::ReceiveNetworkData()
{
if ( !m_hSocketServer )
return;
char *pchRecvBuf = NULL;
SNetSocket_t hSocketClient;
uint32 cubMsgSize;
while ( SteamGameServerNetworking()->IsDataAvailable( m_hSocketServer, &cubMsgSize, &hSocketClient ) )
CSteamID steamIDRemote;
while ( SteamGameServerNetworking()->IsP2PPacketAvailable( &cubMsgSize ) )
{
// free any previous receive buffer
if ( pchRecvBuf )
@@ -527,7 +496,7 @@ void CSpaceWarServer::ReceiveNetworkData()
pchRecvBuf = (char *)malloc( cubMsgSize );
// see if there is any data waiting on the socket
if ( !SteamGameServerNetworking()->RetrieveData( m_hSocketServer, pchRecvBuf, cubMsgSize, &cubMsgSize, &hSocketClient ) )
if ( !SteamGameServerNetworking()->ReadP2PPacket( pchRecvBuf, cubMsgSize, &cubMsgSize, &steamIDRemote ) )
break;
if ( cubMsgSize < sizeof( EMessage ) )
@@ -550,7 +519,7 @@ void CSpaceWarServer::ReceiveNetworkData()
msg.m_bIsVACSecure = SteamGameServer()->BSecure();
#endif
_snprintf( msg.m_rgchServerName, sizeof( msg.m_rgchServerName ), "%s", m_sServerName.c_str() );
BSendDataToClientOnSocket( hSocketClient, (char*)&msg, sizeof( MsgServerSendInfo_t ) );
SteamGameServerNetworking()->SendP2PPacket( steamIDRemote, &msg, sizeof( MsgServerSendInfo_t ), k_EP2PSendReliable );
}
break;
case k_EMsgClientBeginAuthentication:
@@ -562,9 +531,9 @@ void CSpaceWarServer::ReceiveNetworkData()
}
MsgClientBeginAuthentication_t *pMsg = (MsgClientBeginAuthentication_t*)pchRecvBuf;
#ifdef USE_GS_AUTH_API
OnClientBeginAuthentication( hSocketClient, &pMsg->m_rgchToken, pMsg->m_uTokenLen );
OnClientBeginAuthentication( steamIDRemote, &pMsg->m_rgchToken, pMsg->m_uTokenLen );
#else
OnClientBeginAuthentication( hSocketClient, &pMsg->m_SteamID, 0 );
OnClientBeginAuthentication( steamIDRemote, 0 );
#endif
}
break;
@@ -580,7 +549,7 @@ void CSpaceWarServer::ReceiveNetworkData()
bool bFound = false;
for( uint32 i=0; i<MAX_PLAYERS_PER_SERVER; ++i )
{
if ( m_rgClientData[i].m_hSocket == hSocketClient )
if ( m_rgClientData[i].m_SteamIDUser == steamIDRemote )
{
bFound = true;
MsgClientSendLocalUpdate_t *pMsg = (MsgClientSendLocalUpdate_t*)pchRecvBuf;
@@ -592,6 +561,13 @@ void CSpaceWarServer::ReceiveNetworkData()
OutputDebugString( "Got a client data update, but couldn't find a matching client\n" );
}
break;
case k_EMsgClientPing:
{
// send back a response
MsgServerPingResponse_t msg;
SteamGameServerNetworking()->SendP2PPacket( steamIDRemote, &msg, sizeof( msg ), k_EP2PSendUnreliable );
}
break;
case k_EMsgClientLeavingServer:
if ( cubMsgSize != sizeof( MsgClientLeavingServer_t ) )
{
@@ -602,7 +578,7 @@ void CSpaceWarServer::ReceiveNetworkData()
bool bFound = false;
for( uint32 i=0; i<MAX_PLAYERS_PER_SERVER; ++i )
{
if ( m_rgClientData[i].m_hSocket == hSocketClient )
if ( m_rgClientData[i].m_SteamIDUser == steamIDRemote )
{
bFound = true;
RemovePlayerFromServer( i );
@@ -610,7 +586,7 @@ void CSpaceWarServer::ReceiveNetworkData()
}
// Also check for pending connections that may match
if ( m_rgPendingClientData[i].m_hSocket == hSocketClient )
if ( m_rgPendingClientData[i].m_SteamIDUser == steamIDRemote )
{
#ifdef USE_GS_AUTH_API
// Tell the GS the user is leaving the server
@@ -638,9 +614,6 @@ void CSpaceWarServer::RunFrame()
// Run any Steam Game Server API callbacks
SteamGameServer_RunCallbacks();
// Get any new data off the network to begin with
ReceiveNetworkData();
// Update our server details
SendUpdatedServerDetailsToSteam();
@@ -1033,38 +1006,6 @@ void CSpaceWarServer::OnGSClientKick( GSClientKick_t *pGSClientKick )
}
//-----------------------------------------------------------------------------
// Purpose: Returns the public IP address of the game server
//-----------------------------------------------------------------------------
uint32 CSpaceWarServer::GetIP()
{
if ( m_hSocketServer )
{
uint32 unIP;
uint16 unPort;
SteamGameServerNetworking()->GetListenSocketInfo( m_hSocketServer, &unIP, &unPort );
return unIP;
}
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the public port of the game server
//-----------------------------------------------------------------------------
uint16 CSpaceWarServer::GetPort()
{
if ( m_hSocketServer )
{
uint32 unIP;
uint16 unPort;
SteamGameServerNetworking()->GetListenSocketInfo( m_hSocketServer, &unIP, &unPort );
return unPort;
}
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the SteamID of the game server
//-----------------------------------------------------------------------------
@@ -1077,3 +1018,29 @@ CSteamID CSpaceWarServer::GetSteamID()
return k_steamIDNonSteamGS;
#endif
}
void CSpaceWarServer::KickPlayerOffServer( CSteamID steamID )
{
uint32 uPlayerCount = 0;
for( uint32 i=0; i < MAX_PLAYERS_PER_SERVER; ++i )
{
// If there is no ship, skip
if ( !m_rgClientData[i].m_bActive )
continue;
if ( m_rgClientData[i].m_SteamIDUser == steamID )
{
OutputDebugString( "Kicking player\n" );
RemovePlayerFromServer( i );
// send him a kick message
MsgServerFailAuthentication_t msg;
SteamGameServerNetworking()->SendP2PPacket( steamID, &msg, sizeof( msg ), k_EP2PSendReliable );
}
else
{
++uPlayerCount;
}
}
m_uPlayerCount = uPlayerCount;
}
+8 -15
View File
@@ -23,8 +23,6 @@ class CSpaceWarClient;
struct ClientConnectionData_t
{
bool m_bActive; // Is this slot in use? Or is it available for new connections?
SNetSocket_t m_hSocket; // Socket to send data on to get to this client
CSteamID m_SteamIDUser; // What is the steamid of the player?
uint64 m_ulTickCountLastData; // What was the last time we got data from the player?
};
@@ -44,6 +42,9 @@ public:
// Set game state
void SetGameState( EServerGameState eState );
// Checks for any incoming network data, then dispatches it
void ReceiveNetworkData();
// Reset player scores (occurs when starting a new game)
void ResetScores();
@@ -53,10 +54,10 @@ public:
// Checks various game objects for collisions and updates state appropriately if they have occurred
void CheckForCollisions();
void KickPlayerOffServer( CSteamID steamID );
// data accessors
bool IsConnectedToSteam() { return m_bConnectedToSteam; }
uint32 GetIP();
uint16 GetPort();
CSteamID GetSteamID();
private:
@@ -90,7 +91,8 @@ private:
STEAM_GAMESERVER_CALLBACK( CSpaceWarServer, OnGSClientKick, GSClientKick_t, m_CallbackGSClientKick );
// client connection state
STEAM_GAMESERVER_CALLBACK( CSpaceWarServer, OnSocketStatusCallback, SocketStatusCallback_t, m_CallbackSocketStatus );
STEAM_GAMESERVER_CALLBACK( CSpaceWarServer, OnP2PSessionRequest, P2PSessionRequest_t, m_CallbackP2PSessionRequest );
STEAM_GAMESERVER_CALLBACK( CSpaceWarServer, OnP2PSessionConnectFail, P2PSessionConnectFail_t, m_CallbackP2PSessionConnectFail );
// Function to tell Steam about our servers details
void SendUpdatedServerDetailsToSteam();
@@ -98,20 +100,14 @@ private:
// Receive updates from client
void OnReceiveClientUpdateData( uint32 uShipIndex, ClientSpaceWarUpdateData_t UpdateData );
// Checks for any incoming network data, then dispatches it
void ReceiveNetworkData();
// Send data to a client at the given ship index
bool BSendDataToClient( uint32 uShipIndex, char *pData, uint32 nSizeOfData );
// Send data to a client at the given pending index
bool BSendDataToPendingClient( uint32 uShipIndex, char *pData, uint32 nSizeOfData );
// Send data to a client at a given address
bool BSendDataToClientOnSocket( SNetSocket_t hSocket, char *pData, uint32 nSizeOfData );
// Connect a client, will send a success/failure response to the client
void OnClientBeginAuthentication( SNetSocket_t hSocketClient, void *pToken, uint32 uTokenLen );
void OnClientBeginAuthentication( CSteamID steamIDClient, void *pToken, uint32 uTokenLen );
// Handles authentication completing for a client
void OnAuthCompleted( bool bAuthSuccess, uint32 iPendingAuthIndex );
@@ -156,9 +152,6 @@ private:
// Sun instance
CSun *m_pSun;
// Server socket (which has been bound to our known port)
SNetSocket_t m_hSocketServer;
// pointer to game engine instance we are running under
CGameEngine *m_pGameEngine;
@@ -226,6 +226,10 @@
RelativePath=".\MainMenu.cpp"
>
</File>
<File
RelativePath=".\p2pauth.cpp"
>
</File>
<File
RelativePath=".\PhotonBeam.cpp"
>
@@ -316,6 +320,10 @@
RelativePath=".\Messages.h"
>
</File>
<File
RelativePath=".\p2pauth.h"
>
</File>
<File
RelativePath=".\PhotonBeam.h"
>
+413
View File
@@ -0,0 +1,413 @@
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "stdafx.h"
#include "SpaceWarClient.h"
#include "p2pauth.h"
//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CNetworkTransport::CNetworkTransport() :
m_CallbackP2PSessionRequest( this, &CNetworkTransport::OnP2PSessionRequest ),
m_CallbackP2PSessionConnectFail( this, &CNetworkTransport::OnP2PSessionConnectFail )
{
}
//-----------------------------------------------------------------------------
// Purpose: send ticket message to steamIDTo
//-----------------------------------------------------------------------------
bool CNetworkTransport::SendTicket( CSteamID steamIDFrom, CSteamID steamIDTo, uint32 cubTicket, uint8 *pubTicket )
{
MsgP2PSendingTicket_t msg;
msg.m_uTokenLen = cubTicket;
memcpy( msg.m_rgchToken, pubTicket, cubTicket );
msg.m_SteamID = steamIDFrom;
return SendNetMessage( steamIDTo, (void*)&msg, sizeof( msg ) );
}
//-----------------------------------------------------------------------------
// Purpose: actual implementation
//-----------------------------------------------------------------------------
bool CNetworkTransport::SendNetMessage( CSteamID steamIDTo, void *pMessage, int cubMessage )
{
return SteamNetworking()->SendP2PPacket( steamIDTo, pMessage, cubMessage, k_EP2PSendReliable );
}
//-----------------------------------------------------------------------------
// Purpose: close connection to steamID
//-----------------------------------------------------------------------------
void CNetworkTransport::CloseConnection( CSteamID steamID )
{
SteamNetworking()->CloseP2PSessionWithUser( steamID );
}
//-----------------------------------------------------------------------------
// Purpose: another user has sent us a packet - do we accept?
//-----------------------------------------------------------------------------
void CNetworkTransport::OnP2PSessionRequest( P2PSessionRequest_t *pP2PSessionRequest )
{
// always accept packets
// the packet itself will come through when you call SteamNetworking()->ReadP2PPacket()
SteamNetworking()->AcceptP2PSessionWithUser( pP2PSessionRequest->m_steamIDRemote );
}
//-----------------------------------------------------------------------------
// Purpose: another user has sent us a packet - do we accept?
//-----------------------------------------------------------------------------
void CNetworkTransport::OnP2PSessionConnectFail( P2PSessionConnectFail_t *pP2PSessionConnectFail )
{
// we've sent a packet to the user, but it never got through
// we can just use the normal timeout
}
//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CP2PAuthPlayer::CP2PAuthPlayer( CGameEngine *pGameEngine, CNetworkTransport *pNetworkTransport ):m_CallbackBeginAuthResponse( this, &CP2PAuthPlayer::OnBeginAuthResponse )
{
m_pGameEngine = pGameEngine;
m_pNetworkTransport = pNetworkTransport;
m_bSentTicket = false;
m_bSubmittedHisTicket = false;
m_bHaveAnswer = false;
m_ulConnectTime = GetGameTimeInSeconds();
m_cubTicketIGaveThisUser = 0;
m_cubTicketHeGaveMe = 0;
}
//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CP2PAuthPlayer::~CP2PAuthPlayer()
{
EndGame();
m_pNetworkTransport->CloseConnection( SteamUser()->GetSteamID() );
m_pNetworkTransport = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: the steam backend has responded
//-----------------------------------------------------------------------------
void CP2PAuthPlayer::OnBeginAuthResponse( ValidateAuthTicketResponse_t *pCallback )
{
if ( m_steamID == pCallback->m_SteamID )
{
char rgch[128];
sprintf( rgch, "P2P:: Received steam response for account=%d\n", m_steamID.GetAccountID() );
OutputDebugStringA( rgch );
m_ulAnswerTime = GetGameTimeInSeconds();
m_bHaveAnswer = true;
m_eAuthSessionResponse = pCallback->m_eAuthSessionResponse;
}
}
//-----------------------------------------------------------------------------
// Purpose: internal implementation
//-----------------------------------------------------------------------------
void CP2PAuthPlayer::InitPlayer( CSteamID steamID )
{
m_steamID = steamID;
m_bSentTicket = false;
m_bSubmittedHisTicket = false;
m_bHaveAnswer = false;
m_ulConnectTime = GetGameTimeInSeconds();
m_cubTicketIGaveThisUser = 0;
m_cubTicketHeGaveMe = 0;
}
void CP2PAuthPlayer::StartAuthPlayer()
{
// send him the ticket if we havent yet
if ( m_cubTicketIGaveThisUser == 0 )
{
m_hAuthTicketIGaveThisUser = SteamUser()->GetAuthSessionTicket( m_rgubTicketIGaveThisUser, sizeof( m_rgubTicketIGaveThisUser ), &m_cubTicketIGaveThisUser );
}
// sample has no retry here
m_bSentTicket = m_pNetworkTransport->SendTicket( SteamUser()->GetSteamID(), m_steamID, m_cubTicketIGaveThisUser, m_rgubTicketIGaveThisUser );
// start a timer on this, if we dont get a ticket back within reasonable time, mark him timed out
m_ulTicketTime = GetGameTimeInSeconds();
}
//-----------------------------------------------------------------------------
// Purpose: is this auth ok?
//-----------------------------------------------------------------------------
bool CP2PAuthPlayer::BIsAuthOk()
{
if ( m_steamID.IsValid() )
{
// Timeout if we fail to establish communication with this player
if ( !m_bSentTicket && !m_bSubmittedHisTicket )
{
if ( GetGameTimeInSeconds() - m_ulConnectTime > 30 )
{
char rgch[128];
sprintf( rgch, "P2P:: Nothing received for account=%d\n", m_steamID.GetAccountID() );
OutputDebugStringA( rgch );
return false;
}
}
// first ticket check: if i submitted his ticket - was it good?
if ( m_bSubmittedHisTicket && m_eBeginAuthSessionResult != k_EBeginAuthSessionResultOK )
{
char rgch[128];
sprintf( rgch, "P2P:: Ticket from account=%d was bad\n", m_steamID.GetAccountID() );
OutputDebugStringA( rgch );
return false;
}
// second ticket check: if the steam backend replied, was that good?
if ( m_bHaveAnswer && m_eAuthSessionResponse != k_EAuthSessionResponseOK )
{
char rgch[128];
sprintf( rgch, "P2P:: Steam response for account=%d was bad\n", m_steamID.GetAccountID() );
OutputDebugStringA( rgch );
return false;
}
// last: if i sent him a ticket and he has not reciprocated, time out after 30 sec
if ( m_bSentTicket && !m_bSubmittedHisTicket )
{
if ( GetGameTimeInSeconds() - m_ulTicketTime > 30 )
{
char rgch[128];
sprintf( rgch, "P2P:: No ticket received for account=%d\n", m_steamID.GetAccountID() );
OutputDebugStringA( rgch );
return false;
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: the game engine is telling us about someone who left the game
//-----------------------------------------------------------------------------
void CP2PAuthPlayer::EndGame()
{
if ( m_bSentTicket )
{
SteamUser()->CancelAuthTicket( m_hAuthTicketIGaveThisUser );
m_bSentTicket = false;
}
if ( m_bSubmittedHisTicket )
{
SteamUser()->EndAuthSession( m_steamID );
m_bSubmittedHisTicket = false;
}
if ( m_steamID.IsValid() )
m_pNetworkTransport->CloseConnection( m_steamID );
}
//-----------------------------------------------------------------------------
// Purpose: message handler
//-----------------------------------------------------------------------------
bool CP2PAuthPlayer::HandleMessage( EMessage eMsg, void *pMessage )
{
switch ( eMsg )
{
// message from another player providing his ticket
case k_EMsgP2PSendingTicket:
{
MsgP2PSendingTicket_t *pMsg = (MsgP2PSendingTicket_t*)pMessage;
// is this message for me?
if ( pMsg->m_SteamID != m_steamID )
return false;
m_cubTicketHeGaveMe = pMsg->m_uTokenLen;
memcpy( m_rgubTicketHeGaveMe, pMsg->m_rgchToken, m_cubTicketHeGaveMe );
m_eBeginAuthSessionResult = SteamUser()->BeginAuthSession( m_rgubTicketHeGaveMe, m_cubTicketHeGaveMe, m_steamID );
m_bSubmittedHisTicket = true;
char rgch[128];
sprintf( rgch, "P2P:: ReceivedTicket from account=%d \n", m_steamID.GetAccountID() );
OutputDebugStringA( rgch );
if ( !m_bSentTicket )
StartAuthPlayer();
return true;
}
default:
{
OutputDebugStringA( "P2P:: Received unknown message on our listen socket\n" );
break;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: utility wrapper
//-----------------------------------------------------------------------------
CSteamID CP2PAuthPlayer::GetSteamID()
{
return SteamUser()->GetSteamID();
}
//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CP2PAuthedGame::CP2PAuthedGame( CGameEngine *pGameEngine )
{
m_pNetworkTransport = new CNetworkTransport;
m_pGameEngine = pGameEngine;
// no players yet
for ( int i = 0; i < MAX_PLAYERS_PER_SERVER; i++ )
{
m_rgpP2PAuthPlayer[i] = NULL;
}
// no queued messages
for ( int i = 0; i < MAX_PLAYERS_PER_SERVER; i++ )
{
m_rgpQueuedMessage[i] = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: game with this player is over
//-----------------------------------------------------------------------------
void CP2PAuthedGame::PlayerDisconnect( int iSlot )
{
if ( m_rgpP2PAuthPlayer[iSlot] )
{
m_rgpP2PAuthPlayer[iSlot]->EndGame();
delete m_rgpP2PAuthPlayer[iSlot];
m_rgpP2PAuthPlayer[iSlot] = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: game is over, disconnect everyone
//-----------------------------------------------------------------------------
void CP2PAuthedGame::EndGame()
{
for ( int i = 0; i < MAX_PLAYERS_PER_SERVER; i++ )
{
if ( m_rgpP2PAuthPlayer[i] )
{
m_rgpP2PAuthPlayer[i]->EndGame();
delete m_rgpP2PAuthPlayer[i];
m_rgpP2PAuthPlayer[i] = NULL;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: initialize player
//-----------------------------------------------------------------------------
void CP2PAuthedGame::InternalInitPlayer( int iSlot, CSteamID steamID, bool bStartAuthProcess )
{
char rgch[128];
sprintf( rgch, "P2P:: StartAuthPlayer slot=%d account=%d \n", iSlot, steamID.GetAccountID() );
OutputDebugStringA( rgch );
m_rgpP2PAuthPlayer[iSlot] = new CP2PAuthPlayer( m_pGameEngine, m_pNetworkTransport );
m_rgpP2PAuthPlayer[iSlot]->InitPlayer( steamID );
if ( bStartAuthProcess )
m_rgpP2PAuthPlayer[iSlot]->StartAuthPlayer();
// check our queued messages list to see if this guys ticket message arrived before we noticed him
for ( int i = 0; i < MAX_PLAYERS_PER_SERVER; i++ )
{
if ( m_rgpQueuedMessage[i] != NULL )
{
if ( m_rgpP2PAuthPlayer[i]->HandleMessage( k_EMsgP2PSendingTicket, m_rgpQueuedMessage[i] ) )
{
OutputDebugStringA( "P2P:: Consumed queued message \n" );
free( m_rgpQueuedMessage[i] );
m_rgpQueuedMessage[i] = NULL;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: game host register this player, we wait for this player
// to start the auth process by sending us his ticket, then we will
// reciprocate
//-----------------------------------------------------------------------------
void CP2PAuthedGame::RegisterPlayer( int iSlot, CSteamID steamID )
{
if ( iSlot < MAX_PLAYERS_PER_SERVER )
InternalInitPlayer( iSlot, steamID, false );
}
//-----------------------------------------------------------------------------
// Purpose: start the auth process by sending ticket to this player
// he will reciprocate
//-----------------------------------------------------------------------------
void CP2PAuthedGame::StartAuthPlayer( int iSlot, CSteamID steamID )
{
if ( iSlot < MAX_PLAYERS_PER_SERVER )
InternalInitPlayer( iSlot, steamID, true );
}
//-----------------------------------------------------------------------------
// Purpose: message handler
//-----------------------------------------------------------------------------
bool CP2PAuthedGame::HandleMessage( EMessage eMsg, void *pMessage )
{
switch ( eMsg )
{
// message from another player providing his ticket
case k_EMsgP2PSendingTicket:
{
MsgP2PSendingTicket_t *pMsg = (MsgP2PSendingTicket_t*)pMessage;
for ( int i = 0; i < MAX_PLAYERS_PER_SERVER; i++ )
{
if ( m_rgpP2PAuthPlayer[i] )
{
if ( m_rgpP2PAuthPlayer[i]->HandleMessage( eMsg, pMessage ) )
{
return true;
}
}
}
// if we dont have the player in our list yet, lets queue the message and assume he will show up soon
OutputDebugStringA( "P2P:: HandleMessage queueing message \n" );
for ( int i = 0; i < MAX_PLAYERS_PER_SERVER; i++ )
{
if ( m_rgpQueuedMessage[i] == NULL )
{
m_rgpQueuedMessage[i] = pMsg;
return false;
}
}
break;
}
default:
{
OutputDebugStringA( "Received unknown message on our listen socket\n" );
break;
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: utility wrapper
//-----------------------------------------------------------------------------
CSteamID CP2PAuthedGame::GetSteamID()
{
return SteamUser()->GetSteamID();
}
+93
View File
@@ -0,0 +1,93 @@
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
const int k_cMaxSockets = 16;
class CP2PAuthPlayer;
class CNetworkTransport;
//-----------------------------------------------------------------------------
// Purpose: one player p2p auth process state machine
//-----------------------------------------------------------------------------
class CP2PAuthPlayer
{
public:
CP2PAuthPlayer( CGameEngine *pGameEngine, CNetworkTransport *pNetworkTransport );
~CP2PAuthPlayer();
void EndGame();
void InitPlayer( CSteamID steamID );
void StartAuthPlayer();
bool BIsAuthOk();
virtual bool HandleMessage( EMessage eMsg, void *pMessage );
virtual CSteamID GetSteamID();
STEAM_CALLBACK( CP2PAuthPlayer, OnBeginAuthResponse, ValidateAuthTicketResponse_t, m_CallbackBeginAuthResponse );
CSteamID m_steamID;
private:
uint64 GetGameTimeInSeconds()
{
return m_pGameEngine->GetGameTickCount()/1000;
}
bool m_bSentTicket;
bool m_bSubmittedHisTicket;
bool m_bHaveAnswer;
uint64 m_ulConnectTime;
uint64 m_ulTicketTime;
uint64 m_ulAnswerTime;
uint32 m_cubTicketIGaveThisUser;
uint8 m_rgubTicketIGaveThisUser[1024];
uint32 m_cubTicketHeGaveMe;
uint8 m_rgubTicketHeGaveMe[1024];
HAuthTicket m_hAuthTicketIGaveThisUser;
EBeginAuthSessionResult m_eBeginAuthSessionResult;
EAuthSessionResponse m_eAuthSessionResponse;
CNetworkTransport *m_pNetworkTransport;
CGameEngine *m_pGameEngine;
};
//-----------------------------------------------------------------------------
// Purpose: simple wrapper for multiple players
//-----------------------------------------------------------------------------
class CP2PAuthedGame
{
public:
CP2PAuthedGame( CGameEngine *pGameEngine );
void PlayerDisconnect( int iSlot );
void EndGame();
void StartAuthPlayer( int iSlot, CSteamID steamID );
void RegisterPlayer( int iSlot, CSteamID steamID );
virtual bool HandleMessage( EMessage eMsg, void *pMessage );
virtual CSteamID GetSteamID();
void InternalInitPlayer( int iSlot, CSteamID steamID, bool bStartAuthProcess );
CP2PAuthPlayer *m_rgpP2PAuthPlayer[MAX_PLAYERS_PER_SERVER];
MsgP2PSendingTicket_t *m_rgpQueuedMessage[MAX_PLAYERS_PER_SERVER];
CGameEngine *m_pGameEngine;
CNetworkTransport *m_pNetworkTransport;
};
//-----------------------------------------------------------------------------
// Purpose: trivial wrapper on network messaging
//-----------------------------------------------------------------------------
class CNetworkTransport
{
public:
CNetworkTransport();
bool SendTicket( CSteamID steamIDFrom, CSteamID steamIDTo, uint32 cubTicket, uint8 *pubTicket );
void CloseConnection( CSteamID steamID );
bool SendNetMessage( CSteamID steamIDTo, void *pMessage, int cubMessage );
STEAM_CALLBACK( CNetworkTransport, OnP2PSessionRequest, P2PSessionRequest_t, m_CallbackP2PSessionRequest );
STEAM_CALLBACK( CNetworkTransport, OnP2PSessionConnectFail, P2PSessionConnectFail_t, m_CallbackP2PSessionConnectFail );
};
+13 -2
View File
@@ -44,7 +44,7 @@
#include <tchar.h>
// Winsock
#include <winsock.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib" )
// d3d header files
@@ -59,4 +59,15 @@ typedef __int64 int64;
typedef unsigned __int64 uint64;
// steam api header file
#include "steam/steam_api.h"
#include "steam/steam_api.h"
#ifdef STEAM_CEG
// Steam DRM header file
#include "cegclient.h"
#else
#define Steamworks_InitCEGLibrary() (true)
#define Steamworks_TermCEGLibrary() (true)
#define Steamworks_TestSecret() (true)
#define Steamworks_SelfCheck() (true)
#endif
BIN
View File
Binary file not shown.
+2 -1
View File
@@ -13,6 +13,7 @@
#include "isteamclient.h"
#include "steamclientpublic.h"
//-----------------------------------------------------------------------------
// Purpose: set of relationships to other users
//-----------------------------------------------------------------------------
@@ -163,7 +164,7 @@ public:
virtual void SetInGameVoiceSpeaking( CSteamID steamIDUser, bool bSpeaking ) = 0;
// activates the game overlay, with an optional dialog to open
// valid options are "Friends", "Community", "Players", "Settings", "LobbyInvite", "OfficialGameGroup"
// valid options are "Friends", "Community", "Players", "Settings", "LobbyInvite", "OfficialGameGroup", "Stats", "Achievements"
virtual void ActivateGameOverlay( const char *pchDialog ) = 0;
// activates game overlay to a specific place
+10 -1
View File
@@ -102,9 +102,18 @@ public:
// connect to
virtual uint32 GetPublicIP() = 0;
// Sets a string defining the "gamedata" for this server, this is optional, but if it is set
// it allows users to filter in the matchmaking/server-browser interfaces based on the value
// don't set this unless it actually changes, its only uploaded to the master once (when
// acknowledged)
virtual void SetGameData( const char *pchGameData) = 0;
// After receiving a user's authentication data, and passing it to SendUserConnectAndAuthenticate, use this function
// to determine if the user owns downloadable content specified by the provided AppID.
virtual EUserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID ) = 0;
};
#define STEAMGAMESERVER_INTERFACE_VERSION "SteamGameServer008"
#define STEAMGAMESERVER_INTERFACE_VERSION "SteamGameServer009"
// game server flags
const uint32 k_unServerFlagNone = 0x00;
+38 -33
View File
@@ -16,15 +16,17 @@
#include "isteamclient.h"
#include "isteamfriends.h"
//-----------------------------------------------------------------------------
// Purpose: Functions for match making services for clients to get to favorites
//-----------------------------------------------------------------------------
// lobby type description
enum ELobbyType
{
k_ELobbyTypeFriendsOnly = 1, // shows for friends or invitees, but not in lobby list
k_ELobbyTypePublic = 2, // visible for friends and in lobby list
k_ELobbyTypeInvisible = 3, // returned by search, but not visible to other friends
// useful if you want a user in two lobbies, for example matching groups together
// a user can be in only one regular lobby, and up to two invisible lobbies
};
// lobby search filter tools
enum ELobbyComparison
{
k_ELobbyComparisonEqualToOrLessThan = -2,
@@ -32,8 +34,10 @@ enum ELobbyComparison
k_ELobbyComparisonEqual = 0,
k_ELobbyComparisonGreaterThan = 1,
k_ELobbyComparisonEqualToOrGreaterThan = 2,
k_ELobbyComparisonNotEqual = 3,
};
//-----------------------------------------------------------------------------
// Purpose: Functions for match making services for clients to get to favorites
// and to operate on game lobbies.
@@ -91,11 +95,13 @@ public:
// filters for lobbies
// this needs to be called before RequestLobbyList() to take effect
// these are cleared on each call to RequestLobbyList()
virtual void AddRequestLobbyListFilter( const char *pchKeyToMatch, const char *pchValueToMatch ) = 0;
virtual void AddRequestLobbyListStringFilter( const char *pchKeyToMatch, const char *pchValueToMatch, ELobbyComparison eComparisonType ) = 0;
// numerical comparison
virtual void AddRequestLobbyListNumericalFilter( const char *pchKeyToMatch, int nValueToMatch, int nComparisonType ) = 0;
virtual void AddRequestLobbyListNumericalFilter( const char *pchKeyToMatch, int nValueToMatch, ELobbyComparison eComparisonType ) = 0;
// returns results closest to the specified value. Multiple near filters can be added, with early filters taking precedence
virtual void AddRequestLobbyListNearValueFilter( const char *pchKeyToMatch, int nValueToBeCloseTo ) = 0;
// returns only lobbies with the specified number of slots available
virtual void AddRequestLobbyListFilterSlotsAvailable( int nSlotsAvailable ) = 0;
// returns the CSteamID of a lobby, as retrieved by a RequestLobbyList call
// should only be called after a LobbyMatchList_t callback is received
@@ -109,7 +115,7 @@ public:
// this is an asynchronous request
// results will be returned by LobbyCreated_t callback and call result; lobby is joined & ready to use at this pointer
// a LobbyEnter_t callback will also be received (since the local user is joining their own lobby)
virtual SteamAPICall_t CreateLobby( ELobbyType eLobbyType ) = 0;
virtual SteamAPICall_t CreateLobby( ELobbyType eLobbyType, int cMaxMembers ) = 0;
// Joins an existing lobby
// this is an asynchronous request
@@ -125,6 +131,8 @@ public:
// the target user will receive a LobbyInvite_t callback
// will return true if the invite is successfully sent, whether or not the target responds
// returns false if the local user is not connected to the Steam servers
// if the other user clicks the join link, a GameLobbyJoinRequested_t will be posted if the user is in-game,
// or if the game isn't running yet the game will be launched with the parameter +connect_lobby <64-bit lobby id>
virtual bool InviteUserToLobby( CSteamID steamIDLobby, CSteamID steamIDInvitee ) = 0;
// Lobby iteration, for viewing details of users in a lobby
@@ -149,7 +157,16 @@ public:
// other users in the lobby will receive notification of the lobby data change via a LobbyDataUpdate_t callback
virtual bool SetLobbyData( CSteamID steamIDLobby, const char *pchKey, const char *pchValue ) = 0;
// As above, but gets per-user data for someone in this lobby
// returns the number of metadata keys set on the specified lobby
virtual int GetLobbyDataCount( CSteamID steamIDLobby ) = 0;
// returns a lobby metadata key/values pair by index, of range [0, GetLobbyDataCount())
virtual bool GetLobbyDataByIndex( CSteamID steamIDLobby, int iLobbyData, char *pchKey, int cchKeyBufferSize, char *pchValue, int cchValueBufferSize ) = 0;
// removes a metadata key from the lobby
virtual bool DeleteLobbyData( CSteamID steamIDLobby, const char *pchKey ) = 0;
// Gets per-user metadata for someone in this lobby
virtual const char *GetLobbyMemberData( CSteamID steamIDLobby, CSteamID steamIDUser, const char *pchKey ) = 0;
// Sets per-user metadata (for the local user implicitly)
virtual void SetLobbyMemberData( CSteamID steamIDLobby, const char *pchKey, const char *pchValue ) = 0;
@@ -188,16 +205,25 @@ public:
virtual int GetLobbyMemberLimit( CSteamID steamIDLobby ) = 0;
// updates which type of lobby it is
// only lobbies that are k_ELobbyTypePublic will be returned by RequestLobbyList() calls
// only lobbies that are k_ELobbyTypePublic or k_ELobbyTypeInvisible, and are set to joinable, will be returned by RequestLobbyList() calls
virtual bool SetLobbyType( CSteamID steamIDLobby, ELobbyType eLobbyType ) = 0;
// sets whether or not a lobby is joinable - defaults to true for a new lobby
// if set to false, no user can join, even if they are a friend or have been invited
virtual bool SetLobbyJoinable( CSteamID steamIDLobby, bool bLobbyJoinable ) = 0;
// returns the current lobby owner
// you must be a member of the lobby to access this
// there always one lobby owner - if the current owner leaves, another user will become the owner
// it is possible (bur rare) to join a lobby just as the owner is leaving, thus entering a lobby with self as the owner
virtual CSteamID GetLobbyOwner( CSteamID steamIDLobby ) = 0;
// changes who the lobby owner is
// you must be the lobby owner for this to succeed, and steamIDNewOwner must be in the lobby
// after completion, the local user will no longer be the owner
virtual bool SetLobbyOwner( CSteamID steamIDLobby, CSteamID steamIDNewOwner ) = 0;
};
#define STEAMMATCHMAKING_INTERFACE_VERSION "SteamMatchMaking006"
#define STEAMMATCHMAKING_INTERFACE_VERSION "SteamMatchMaking007"
//-----------------------------------------------------------------------------
@@ -432,7 +458,9 @@ struct FavoritesListChanged_t
// Purpose: Someone has invited you to join a Lobby
// normally you don't need to do anything with this, since
// the Steam UI will also display a '<user> has invited you to the lobby, join?' dialog
// if the user outside a game chooses to join, your game will be launched with the parameter "+connect_lobby <64-bit lobby id>"
//
// if the user outside a game chooses to join, your game will be launched with the parameter "+connect_lobby <64-bit lobby id>",
// or with the callback GameLobbyJoinRequested_t if they're already in-game
//-----------------------------------------------------------------------------
struct LobbyInvite_t
{
@@ -532,29 +560,6 @@ struct LobbyMatchList_t
};
//-----------------------------------------------------------------------------
// Purpose: Called when the lobby is being forcefully closed
// lobby details functions will no longer be updated
//-----------------------------------------------------------------------------
struct LobbyClosing_t
{
enum { k_iCallback = k_iSteamMatchmakingCallbacks + 11 };
uint64 m_ulSteamIDLobby; // Lobby
};
//-----------------------------------------------------------------------------
// Purpose: Called when the local user has been kicked from the lobby
// lobby details functions will no longer be updated
//-----------------------------------------------------------------------------
struct LobbyKicked_t
{
enum { k_iCallback = k_iSteamMatchmakingCallbacks + 12 };
uint64 m_ulSteamIDLobby; // Lobby
uint64 m_ulSteamIDAdmin; // User who kicked you
};
//-----------------------------------------------------------------------------
// Purpose: Result of our request to create a Lobby
// m_eResult == k_EResultOK on success
+129 -5
View File
@@ -13,12 +13,67 @@
#include "steamtypes.h"
#include "steamclientpublic.h"
// list of possible errors returned by SendP2PPacket() API
// these will be posted in the P2PSessionConnectFail_t callback
enum EP2PSessionError
{
k_EP2PSessionErrorNone = 0,
k_EP2PSessionErrorNotRunningApp = 1, // target is not running the same game
k_EP2PSessionErrorNoRightsToApp = 2, // local user doesn't own the app that is running
k_EP2PSessionErrorDestinationNotLoggedIn = 3, // target user isn't connected to Steam
k_EP2PSessionErrorTimeout = 4, // target isn't responding, perhaps not calling AcceptP2PSessionWithUser()
};
// SendP2PPacket() send types
// Typically k_EP2PSendUnreliable is what you want for UDP-like packets, k_EP2PSendReliable for TCP-like packets
enum EP2PSend
{
// Basic UDP send. Packets can't be bigger than 1200 bytes (your typical MTU size). Can be lost, or arrive out of order (rare).
// 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_EP2PSendUnreliable = 0,
// As above, but if the underlying p2p connection isn't yet established the packet will just be thrown away. Using this on the first
// packet sent to a remote host almost guarantees the packet will be dropped.
// This is only really useful for kinds of data that should never buffer up, i.e. voice payload packets
k_EP2PSendUnreliableNoDelay = 1,
// Reliable message send. Can send up to 1MB 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_EP2PSendReliable = 2,
// As above, but applies the Nagle algorithm to the send - sends will accumulate
// until the current MTU size (typically ~1200 bytes, but can change) or ~200ms has passed (Nagle algorithm).
// Useful if you want to send a set of smaller messages but have the coalesced into a single packet
// Since the reliable stream is all ordered, you can do several small message sends with k_EP2PSendReliableWithBuffering and then
// do a normal k_EP2PSendReliable to force all the buffered data to be sent.
k_EP2PSendReliableWithBuffering = 3,
};
// connection state to a specified user, returned by GetP2PSessionState()
// this is under-the-hood info about what's going on with a SendP2PPacket(), shouldn't be needed except for debuggin
struct P2PSessionState_t
{
uint8 m_bConnectionActive; // true if we've got an active open connection
uint8 m_bConnecting; // true if we're currently trying to establish a connection
uint8 m_eP2PSessionError; // last error recorded (see enum above)
uint8 m_bUsingRelay; // true if it's going through a relay server (TURN)
int32 m_nBytesQueuedForSend;
int32 m_nPacketsQueuedForSend;
uint32 m_nRemoteIP; // potential IP:Port of remote host. Could be TURN server.
uint16 m_nRemotePort; // Only exists for compatibility with older authentication api's
};
// handle to a socket
typedef uint32 SNetSocket_t;
typedef uint32 SNetListenSocket_t;
typedef uint32 SNetSocket_t; // CreateP2PConnectionSocket()
typedef uint32 SNetListenSocket_t; // CreateListenSocket()
// connection progress indicators
// connection progress indicators, used by CreateP2PConnectionSocket()
enum ESNetSocketState
{
k_ESNetSocketStateInvalid = 0,
@@ -61,6 +116,54 @@ enum ESNetSocketConnectionType
class ISteamNetworking
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Session-less connection functions
// automatically establishes NAT-traversing or Relay server connections
// Sends a P2P packet to the specified user
// UDP-like, unreliable and a max packet size of 1200 bytes
// the first packet send may be delayed as the NAT-traversal code runs
// if we can't get through to the user, an error will be posted via the callback P2PSessionConnectFail_t
// see EP2PSend enum above for the descriptions of the different ways of sending packets
virtual bool SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData, EP2PSend eP2PSendType ) = 0;
// returns true if any data is available for read, and the amount of data that will need to be read
virtual bool IsP2PPacketAvailable( uint32 *pcubMsgSize ) = 0;
// reads in a packet that has been sent from another user via SendP2PPacket()
// returns the size of the message and the steamID of the user who sent it in the last two parameters
// if the buffer passed in is too small, the message will be truncated
// this call is not blocking, and will return false if no data is available
virtual bool ReadP2PPacket( void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, CSteamID *psteamIDRemote ) = 0;
// AcceptP2PSessionWithUser() should only be called in response to a P2PSessionRequest_t callback
// P2PSessionRequest_t will be posted if another user tries to send you a packet that you haven't talked to yet
// if you don't want to talk to the user, just ignore the request
// if the user continues to send you packets, another P2PSessionRequest_t will be posted periodically
// this may be called multiple times for a single user
// (if you've called SendP2PPacket() on the other user, this implicitly accepts the session request)
virtual bool AcceptP2PSessionWithUser( CSteamID steamIDRemote ) = 0;
// call CloseP2PSessionWithUser() when you're done talking to a user, will free up resources under-the-hood
// if the remote user tries to send data to you again, another P2PSessionRequest_t callback will be posted
virtual bool CloseP2PSessionWithUser( CSteamID steamIDRemote ) = 0;
// fills out P2PSessionState_t structure with details about the underlying connection to the user
// should only needed for debugging purposes
// returns false if no connection exists to the specified user
virtual bool GetP2PSessionState( CSteamID steamIDRemote, P2PSessionState_t *pConnectionState ) = 0;
////////////////////////////////////////////////////////////////////////////////////////////
// LISTEN / CONNECT style interface functions
//
// This is an older set of functions designed around the Berkeley TCP sockets model
// it's preferential that you use the above P2P functions, they're more robust
// and these older functions will be removed eventually
//
////////////////////////////////////////////////////////////////////////////////////////////
// creates a socket and listens others to connect
// will trigger a SocketStatusCallback_t callback on another client connecting
// nVirtualP2PPort is the unique ID that the client will connect to, in case you have multiple ports
@@ -130,10 +233,31 @@ public:
// max packet size, in bytes
virtual int GetMaxPacketSize( SNetSocket_t hSocket ) = 0;
};
#define STEAMNETWORKING_INTERFACE_VERSION "SteamNetworking002"
#define STEAMNETWORKING_INTERFACE_VERSION "SteamNetworking003"
// callback notification - a user wants to talk to us over the P2P channel via the SendP2PPacket() API
// in response, a call to AcceptP2PPacketsFromUser() needs to be made, if you want to talk with them
struct P2PSessionRequest_t
{
enum { k_iCallback = k_iSteamNetworkingCallbacks + 2 };
CSteamID m_steamIDRemote; // user who wants to talk to us
};
// callback notification - packets can't get through to the specified user via the SendP2PPacket() API
// all packets queued packets unsent at this point will be dropped
// further attempts to send will retry making the connection (but will be dropped if we fail again)
struct P2PSessionConnectFail_t
{
enum { k_iCallback = k_iSteamNetworkingCallbacks + 3 };
CSteamID m_steamIDRemote; // user we were sending packets to
uint8 m_eP2PSessionError; // EP2PSessionError indicating why we're having trouble
};
// callback notification - status of a socket has changed
// used as part of the CreateListenSocket() / CreateP2PConnectionSocket()
struct SocketStatusCallback_t
{
enum { k_iCallback = k_iSteamNetworkingCallbacks + 1 };
+4
View File
@@ -105,6 +105,10 @@ public:
// Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to
virtual void CancelAuthTicket( HAuthTicket hAuthTicket ) = 0;
// After receiving a user's authentication data, and passing it to BeginAuthSession, use this function
// to determine if the user owns downloadable content specified by the provided AppID.
virtual EUserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID ) = 0;
};
#define STEAMUSER_INTERFACE_VERSION "SteamUser012"
+20 -3
View File
@@ -52,6 +52,13 @@ enum ELeaderboardDisplayType
k_ELeaderboardDisplayTypeTimeMilliSeconds = 3, // the score represents a time, in milliseconds
};
enum ELeaderboardUploadScoreMethod
{
k_ELeaderboardUploadScoreMethodNone = 0,
k_ELeaderboardUploadScoreMethodKeepBest = 1, // Leaderboard will keep user's best score
k_ELeaderboardUploadScoreMethodForceUpdate = 2, // Leaderboard will always replace score with specified
};
// a single entry in a leaderboard, as returned by GetDownloadedLeaderboardEntry()
struct LeaderboardEntry_t
{
@@ -166,13 +173,16 @@ public:
// Uploads a user score to the Steam back-end.
// This call is asynchronous, with the result returned in LeaderboardScoreUploaded_t
// If the score passed in is no better than the existing score this user has in the leaderboard, then the leaderboard will not be updated.
// Details are extra game-defined information regarding how the user got that score
// pScoreDetails points to an array of int32's, cScoreDetailsCount is the number of int32's in the list
virtual SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, int32 nScore, int32 *pScoreDetails, int cScoreDetailsCount ) = 0;
virtual SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount ) = 0;
// Retrieves the number of players currently playing your game (online + offline)
// This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t
virtual SteamAPICall_t GetNumberOfCurrentPlayers() = 0;
};
#define STEAMUSERSTATS_INTERFACE_VERSION "STEAMUSERSTATS_INTERFACE_VERSION005"
#define STEAMUSERSTATS_INTERFACE_VERSION "STEAMUSERSTATS_INTERFACE_VERSION006"
//-----------------------------------------------------------------------------
@@ -256,5 +266,12 @@ struct LeaderboardScoreUploaded_t
int m_nGlobalRankPrevious; // the previous global rank of the user in this leaderboard; 0 if the user had no existing entry in the leaderboard
};
struct NumberOfCurrentPlayers_t
{
enum { k_iCallback = k_iSteamUserStatsCallbacks + 7 };
uint8 m_bSuccess; // 1 if the call was successful
int32 m_cPlayers; // Number of players currently playing
};
#endif // ISTEAMUSER_H
+1
View File
@@ -212,6 +212,7 @@ private:
// For data added after SteamMatchMaking001 add it here
public:
char m_szGameTags[128]; // the tags this server exposes
CSteamID m_steamID; // steamID of the game server - invalid if it's doesn't have one (old server, or not connected to Steam)
};
+10
View File
@@ -270,10 +270,20 @@ private:
func_t m_Func;
};
// Allows you to defer registration of the callback
template< class T, class P, bool bGameServer >
class CCallbackManual : public CCallback< T, P, bGameServer >
{
public:
CCallbackManual() : CCallback< T, P, bGameServer >( NULL, NULL ) {}
};
// utility macro for declaring the function and callback object together
#define STEAM_CALLBACK( thisclass, func, param, var ) CCallback< thisclass, param, false > var; void func( param *pParam )
// same as above, but lets you defer the callback binding by calling Register later
#define STEAM_CALLBACK_MANUAL( thisclass, func, param, var ) CCallbackManual< thisclass, param, false > var; void func( param *pParam )
#ifdef _WIN32
// disable this warning; this pattern need for steam callback registration
+27
View File
@@ -69,6 +69,11 @@ enum EResult
k_EResultNoMatch = 42, // nothing matching the request found
k_EResultAccountDisabled = 43,
k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now
k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available
k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin
k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol.
k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another.
};
// Error codes for use with the voice functions
@@ -132,6 +137,14 @@ typedef enum
k_EAuthSessionResponseAuthTicketInvalid = 8, // This ticket is not from a user instance currently connected to steam.
} EAuthSessionResponse;
// results from UserHasLicenseForApp
typedef enum
{
k_EUserHasLicenseResultHasLicense = 0, // User has a license for specified app
k_EUserHasLicenseResultDoesNotHaveLicense = 1, // User does not have a license for the specified app
k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated
} EUserHasLicenseForAppResult;
// Steam universes. Each universe is a self-contained Steam instance.
enum EUniverse
@@ -186,11 +199,13 @@ enum ESteamUserStatType
//-----------------------------------------------------------------------------
enum EChatEntryType
{
k_EChatEntryTypeInvalid = 0,
k_EChatEntryTypeChatMsg = 1, // Normal text message from another user
k_EChatEntryTypeTyping = 2, // Another user is typing (not used in multi-user chat)
k_EChatEntryTypeInviteGame = 3, // Invite from other user into that users current game
k_EChatEntryTypeEmote = 4, // text emote message
k_EChatEntryTypeLobbyGameStart = 5, // lobby game is starting
k_EChatEntryTypeLeftConversation = 6, // user has left the conversation ( closed chat window )
// Above are previous FriendMsgType entries, now merged into more generic chat entry types
};
@@ -209,6 +224,18 @@ enum EChatRoomEnterResponse
};
//-----------------------------------------------------------------------------
// Purpose: Status of a given depot version, these are stored in the DB, don't renumber
//-----------------------------------------------------------------------------
enum EStatusDepotVersion
{
k_EStatusDepotVersionInvalid = 0,
k_EStatusDepotVersionCompleteDisabled = 1,
k_EStatusDepotVersionCompleteEnabledBeta = 2,
k_EStatusDepotVersionCompleteEnabledPublic = 3,
};
typedef void (*PFNLegacyKeyRegistration)( const char *pchCDKey, const char *pchInstallPath );
typedef bool (*PFNLegacyKeyInstalled)();
+8
View File
@@ -74,6 +74,14 @@ const PackageId_t k_uPackageIdInvalid = 0xFFFFFFFF;
typedef uint32 AppId_t;
const AppId_t k_uAppIdInvalid = 0x0;
// this is baked into client messages and interfaces as an int,
// make sure we never break this. AppIds and DepotIDs also presently
// share the same namespace, but since we'd like to change that in the future
// I've defined it seperately here.
typedef uint32 DepotId_t;
const DepotId_t k_uDepotIdInvalid = 0x0;
// RTime32
// We use this 32 bit time representing real world time.
// It offers 1 second resolution beginning on January 1, 1970 (Unix time)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.