diff --git a/Readme.txt b/Readme.txt index a2e23b9..7adb4d7 100644 --- a/Readme.txt +++ b/Readme.txt @@ -11,6 +11,37 @@ website at: http://partner.steamgames.com Revision History: +---------------------------------------------------------------- +v1.12 10th November 2010 +---------------------------------------------------------------- + +Cloud +* added a set of function to handle publishing User Generated Content (UGC) files to the backend, and to download others users UGC files. This enables games to have users easily publish & share content with each other. + +Leaderboards +* added ISteamUserStats::DownloadLeaderboardEntriesForUsers(), which downloads scores for an arbitrary set of users +* added ISteamUserStats::AttachLeaderboardUGC(), to attach a clouded file to a leaderboard entry + +Friends +* added ISteamFriends::RequestUserInformation(), to asynchronously request a users persona name & avatar by steamID +* added ISteamFriends::RequestClanOfficerList(), to asynchronously download the set of officers for a clan. GetClanOwner(), GetClanOfficerCount(), and GetClanOfficerByIndex() can then be used to access the data. + +Matchmaking +* added k_ELobbyTypePrivate option to creating lobbies - this means that the lobby won't show up to friends or be returned in searches +* added LobbyDataUpdate_t::m_bSuccess, to easily check if a RequestLobbyData() call failed to find the specified lobby + +Authentication +* added ISteamApps::GetEarliestPurchaseUnixTime(), for games that want to reward users who have played for a long time +* added ISteamApps::BIsSubscribedFromFreeWeekend(), so games can show different offers or information for users who currently only have rights to play the game due to a free weekend promotion +* added ISteamGameServer::GetAuthSessionTicket(), BeginAuthSession(), EndAuthSession(), and CancelAuthTicket(), matching what exists in ISteamUser. This allows game servers and clients to authenticate each other in a unified manner. + +Steamworks Example +* added OpenGL and OSX support + +PS3 +* added several functions regarding PS3 support. This is still a work in progress, and no PS3 binaries are included. + + ---------------------------------------------------------------- v1.11 23rd August 2010 ---------------------------------------------------------------- diff --git a/SteamworksExample/GameEngine.h b/SteamworksExample/GameEngine.h index f87a44d..f7b2dec 100644 --- a/SteamworksExample/GameEngine.h +++ b/SteamworksExample/GameEngine.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ // // Purpose: Main class for the game engine // @@ -21,6 +21,9 @@ typedef int HGAMEVERTBUF; typedef int HGAMETEXTURE; +// Typedef for voice channels +typedef int HGAMEVOICECHANNEL; + // BDrawText position flags #define TEXTPOS_TOP 0x00000000 #define TEXTPOS_LEFT 0x00000000 @@ -29,6 +32,11 @@ typedef int HGAMETEXTURE; #define TEXTPOS_VCENTER 0x00000004 #define TEXTPOS_BOTTOM 0x00000008 + +#define VOICE_OUTPUT_SAMPLE_RATE 11000 // real sample rate is 11025 but for XAudio2 it must be a multiple of XAUDIO2_QUANTUM_DENOMINATOR +#define VOICE_OUTPUT_SAMPLE_RATE_IDEAL 11025 +#define BYTES_PER_SAMPLE 2 + // // Interface that needs to be implemented for game engines on all platforms // @@ -36,7 +44,7 @@ class IGameEngine { public: - // Just here to stop warnings on non-virtual destructor in PS3 build + // Just here to stop warnings on non-virtual destructor in gcc builds virtual ~IGameEngine() {}; // Check if the game engine is initialized ok and ready for use @@ -121,6 +129,10 @@ public: // Check if the game engine hwnd currently has focus (and a working d3d device) virtual bool BGameEngineHasFocus() = 0; + // Voice chat functions + virtual HGAMEVOICECHANNEL HCreateVoiceChannel() = 0; + virtual void DestroyVoiceChannel( HGAMEVOICECHANNEL hChannel ) = 0; + virtual bool AddVoiceData( HGAMEVOICECHANNEL hChannel, const uint8 *pVoiceData, uint32 uLength ) = 0; }; #endif // GAMEENGINE_H \ No newline at end of file diff --git a/SteamworksExample/Leaderboards.cpp b/SteamworksExample/Leaderboards.cpp index e832dca..3206fc2 100644 --- a/SteamworksExample/Leaderboards.cpp +++ b/SteamworksExample/Leaderboards.cpp @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2009, Valve LLC, All rights reserved. ============ +//========= Copyright � 1996-2009, Valve LLC, All rights reserved. ============ // // Purpose: Class for tracking leaderboards // @@ -6,6 +6,7 @@ #include "stdafx.h" #include "leaderboards.h" +#include "BaseMenu.h" #include //----------------------------------------------------------------------------- diff --git a/SteamworksExample/Main.cpp b/SteamworksExample/Main.cpp index 32590aa..2728637 100644 --- a/SteamworksExample/Main.cpp +++ b/SteamworksExample/Main.cpp @@ -1,4 +1,4 @@ -//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +//====== Copyright � 1996-2008, Valve Corporation, All rights reserved. ======= // // Purpose: Main file for the SteamworksExample app // @@ -6,20 +6,21 @@ #include "stdafx.h" -#ifdef WIN32 -#include "gameenginewin32.h" -#elif _PS3 -#include "gameengineps3.h" +#if defined(WIN32) + #include "gameenginewin32.h" + #define atoll _atoi64 +#elif defined(OSX) + #include "GameEngine.h" + extern IGameEngine *CreateGameEngineOSX(); #endif #include "SpaceWarClient.h" - //----------------------------------------------------------------------------- // Purpose: Wrapper around SteamAPI_WriteMiniDump which can be used directly // as a se translator //----------------------------------------------------------------------------- -#ifndef _PS3 +#ifdef _WIN32 void MiniDumpFunction( unsigned int nExceptionCode, EXCEPTION_POINTERS *pException ) { // You can build and set an arbitrary comment to embed in the minidump here, @@ -32,6 +33,14 @@ void MiniDumpFunction( unsigned int nExceptionCode, EXCEPTION_POINTERS *pExcepti } #endif +static int Alert( const char *lpCaption, const char *lpText ) +{ +#ifndef _WIN32 + fprintf( stderr, "Message: '%s', Detail: '%s'\n", lpCaption, lpText ); +#else + return ::MessageBox( NULL, lpText, lpCaption, MB_OK ); +#endif +} //----------------------------------------------------------------------------- // Purpose: callback hook for debug text emitted from the Steam API @@ -50,16 +59,17 @@ extern "C" void __cdecl SteamAPIDebugTextHook( int nSeverity, const char *pchDeb } } + //----------------------------------------------------------------------------- // Purpose: Main loop code shared between all platforms //----------------------------------------------------------------------------- -void RunGameLoop( IGameEngine *pGameEngine, char *pchServerAddress, char *pchLobbyID ) +void RunGameLoop( IGameEngine *pGameEngine, const char *pchServerAddress, const char *pchLobbyID ) { // Make sure it initialized ok if ( pGameEngine->BReadyForUse() ) { // Initialize the game - CSpaceWarClient *pGameClient = new CSpaceWarClient( pGameEngine, SteamUser()->GetSteamID() ); + CSpaceWarClient *pGameClient = new CSpaceWarClient( pGameEngine ); // Black background pGameEngine->SetBackgroundColor( 0, 0, 0, 0 ); @@ -82,11 +92,7 @@ void RunGameLoop( IGameEngine *pGameEngine, char *pchServerAddress, char *pchLob // if +connect_lobby was used to specify a lobby to join, connect now if ( pchLobbyID ) { -#ifndef _PS3 - CSteamID steamIDLobby( (uint64)_atoi64( pchLobbyID ) ); -#else CSteamID steamIDLobby( (uint64)atoll( pchLobbyID ) ); -#endif if ( steamIDLobby.IsValid() ) { // act just like we had selected it from the menu @@ -96,9 +102,7 @@ void RunGameLoop( IGameEngine *pGameEngine, char *pchServerAddress, char *pchLob } // test a user specific secret before entering main loop -#ifndef _PS3 Steamworks_TestSecret(); -#endif pGameClient->RetrieveEncryptedAppTicket(); @@ -132,11 +136,8 @@ void RunGameLoop( IGameEngine *pGameEngine, char *pchServerAddress, char *pchLob //----------------------------------------------------------------------------- // Purpose: Real main entry point for the program //----------------------------------------------------------------------------- -#ifdef WIN32 -int APIENTRY RealMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPSTR lpCmdLine, - int nCmdShow) +#ifndef _PS3 +static int RealMain( const char *pchCmdLine, HINSTANCE hInstance, int nCmdShow ) { if ( SteamAPI_RestartAppIfNecessary( k_uAppIdInvalid ) ) @@ -155,7 +156,7 @@ int APIENTRY RealMain(HINSTANCE hInstance, 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 ); + Alert( "Fatal Error", "Steam must be running to play this game (InitDrmLibrary() failed).\n" ); return EXIT_FAILURE; } @@ -169,7 +170,7 @@ int APIENTRY RealMain(HINSTANCE hInstance, if ( !SteamAPI_Init() ) { OutputDebugString( "SteamAPI_Init() failed\n" ); - ::MessageBox( NULL, "Steam must be running to play this game (SteamAPI_Init() failed).\n", "Fatal Error", MB_OK ); + Alert( "Fatal Error", "Steam must be running to play this game (SteamAPI_Init() failed).\n" ); return EXIT_FAILURE; } @@ -187,9 +188,8 @@ int APIENTRY RealMain(HINSTANCE hInstance, // Steam will pass this when a user has used the Steam Server browser to find // a server for our game and is trying to join it. const char *pchConnectParam = "+connect"; - char *pchCmdLine = ::GetCommandLine(); - char *pchConnect = strstr( pchCmdLine, pchConnectParam ); - char *pchServerAddress = NULL; + const char *pchConnect = strstr( pchCmdLine, pchConnectParam ); + const char *pchServerAddress = NULL; if ( pchConnect && strlen( pchCmdLine ) > (pchConnect - pchCmdLine) + strlen( pchConnectParam ) + 1 ) { // Address should be right after the +connect, +1 on the end to skip the space @@ -199,8 +199,8 @@ int APIENTRY RealMain(HINSTANCE hInstance, // look for +connect_lobby lobbyid paramter on the command line // Steam will pass this in if a user taken up an invite to a lobby const char *pchConnectLobbyParam = "+connect_lobby"; - char *pchConnectLobby = strstr( pchCmdLine, pchConnectParam ); - char *pchLobbyID = NULL; + const char *pchConnectLobby = strstr( pchCmdLine, pchConnectParam ); + const char *pchLobbyID = NULL; if ( pchConnectLobby && strlen( pchCmdLine ) > (pchConnectLobby - pchCmdLine) + strlen( pchConnectLobbyParam ) + 1 ) { // Address should be right after the +connect, +1 on the end to skip the space @@ -212,8 +212,13 @@ int APIENTRY RealMain(HINSTANCE hInstance, // Construct a new instance of the game engine // bugbug jmccaskey - make screen resolution dynamic, maybe take it on command line? - CGameEngineWin32 *pGameEngine = new CGameEngineWin32( hInstance, nCmdShow, 1024, 768 ); - + IGameEngine *pGameEngine = +#if defined(_WIN32) + new CGameEngineWin32( hInstance, nCmdShow, 1024, 768 ); +#else if defined(OSX) + CreateGameEngineOSX(); +#endif + // This call will block and run until the game exits RunGameLoop( pGameEngine, pchServerAddress, pchLobbyID ); @@ -248,13 +253,13 @@ int APIENTRY WinMain(HINSTANCE hInstance, // We don't want to mask exceptions (or report them to Steam!) when debugging. // If you would like to step through the exception handler, attach a debugger // after running the game outside of the debugger. - return RealMain( hInstance, hPrevInstance, lpCmdLine, nCmdShow ); + return RealMain( lpCmdLine, hInstance, nCmdShow ); } _set_se_translator( MiniDumpFunction ); try // this try block allows the SE translator to work { - return RealMain( hInstance, hPrevInstance, lpCmdLine, nCmdShow ); + return RealMain( lpCmdLine, hInstance, nCmdShow ); } catch( ... ) { @@ -263,143 +268,32 @@ int APIENTRY WinMain(HINSTANCE hInstance, } #endif - -//----------------------------------------------------------------------------- -// Purpose: Main entry point for the program -- ps3 -//----------------------------------------------------------------------------- -#ifdef _PS3 - - -//----------------------------------------------------------------------------- -// Purpose: Loads the Steam PS3 module -//----------------------------------------------------------------------------- -sys_prx_id_t g_sys_prx_id_steam = -1; -static bool LoadSteamPS3Module() +#ifdef OSX +int main(int argc, const char **argv) { - g_sys_prx_id_steam = sys_prx_load_module( SYS_APP_HOME "/steam_api_ps3.sprx", 0, NULL ); - if ( g_sys_prx_id_steam < CELL_OK ) - { - OutputDebugString( "LoadSteamModule() - failed to load steam_api_ps3\n" ); - return false; - } + char szCmdLine[1024]; + char *pszStart = szCmdLine; + char * const pszEnd = szCmdLine + ARRAYSIZE(szCmdLine); - int modres; - int res = sys_prx_start_module( g_sys_prx_id_steam, 0, NULL, &modres, 0, NULL); - if ( res < CELL_OK ) - { - OutputDebugString( "LoadSteamModule() - failed to start steam_api_ps3\n" ); - return false; - } - return true; + *szCmdLine = '\0'; + + for ( int i = 1; i < argc; i++ ) + { + const char *parm = argv[i]; + while ( *parm && (pszStart < pszEnd) ) + { + *pszStart++ = *parm++; + } + + if ( pszStart >= pszEnd ) + break; + + if ( i < argc-1 ) + *pszStart++ = ' '; + } + + szCmdLine[ARRAYSIZE(szCmdLine) - 1] = '\0'; + + return RealMain( szCmdLine, 0, 0 ); } - -//----------------------------------------------------------------------------- -// Purpose: Unloads the Steam PS3 module -//----------------------------------------------------------------------------- -static bool UnloadSteamPS3Module() -{ - // check if loaded - if ( g_sys_prx_id_steam < CELL_OK ) - return false; - - int modres; - int res = sys_prx_stop_module( g_sys_prx_id_steam, 0, NULL, &modres, 0, NULL); - if ( res < CELL_OK ) - { - OutputDebugString( "LoadSteamModule() - failed to stop steam_api_ps3\n" ); - return false; - } - - res = sys_prx_unload_module( g_sys_prx_id_steam, 0, NULL ); - if ( res < CELL_OK ) - { - OutputDebugString( "LoadSteamModule() - failed to unload steam_api_ps3\n" ); - return false; - } - - g_sys_prx_id_steam = -1; - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Initializes Steam PS3 params for our application -//----------------------------------------------------------------------------- -bool SetSteamPS3Params( SteamPS3Params_t *pParams ) -{ - // Steam needs the system cache path - CellSysCacheParam sysCacheParams; - memset( &sysCacheParams, 0, sizeof( CellSysCacheParam ) ); - if ( cellSysCacheMount( &sysCacheParams ) < 0 ) - return false; - - // configure the Steamworks PS3 parameters. All params need to be set - strncpy( pParams->m_rgchInstallationPath, SYS_APP_HOME, STEAM_PS3_PATH_MAX ); - strncpy( pParams->m_rgchSystemCache, sysCacheParams.getCachePath, STEAM_PS3_PATH_MAX ); - - return true; -} - -// Global for PS3 params -SteamPS3Params_t g_SteamPS3Params; - - -//----------------------------------------------------------------------------- -// Purpose: Main entry point for the program -- ps3 -//----------------------------------------------------------------------------- -int main( int argc, char *argv[] ) -{ - printf( "PS3 main\n" ); - - // Initialize 6 SPUs but reserve 1 SPU as a raw SPU for PSGL - sys_spu_initialize(6, 1); - - // Load Steam - if ( !LoadSteamPS3Module() ) - return EXIT_FAILURE; - - // No restart app if necessary, or CEG initialization on PS3 - - // 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. - - if ( !SetSteamPS3Params( &g_SteamPS3Params ) ) - { - OutputDebugString( "SetSteamPS3Params() failed\n" ); - return EXIT_FAILURE; - } - - if ( !SteamAPI_Init( &g_SteamPS3Params ) ) - { - OutputDebugString( "SteamAPI_Init() failed\n" ); - return EXIT_FAILURE; - } - - // set our debug handler - SteamClient()->SetWarningMessageHook( &SteamAPIDebugTextHook ); - - // bugbug ps3 - SteamUser()->LogOn( "jmccaskeybeta", "test123" ); - - // No overlay notification position setting on PS3 - - // No +connect support on PS3 since Steam isn't launching us - - // Construct a new instance of the game engine - // bugbug jmccaskey - make screen resolution dynamic, maybe take it on command line? - CGameEnginePS3 *pGameEngine = new CGameEnginePS3(); - - // This call will block and run until the game exits - RunGameLoop( pGameEngine, NULL, NULL ); - - // Shutdown the SteamAPI - SteamAPI_Shutdown(); - - // Unload Steam - UnloadSteamPS3Module(); - - // exit - return 0; -} -#endif \ No newline at end of file +#endif diff --git a/SteamworksExample/Messages.h b/SteamworksExample/Messages.h index d15f8fa..8041b5b 100644 --- a/SteamworksExample/Messages.h +++ b/SteamworksExample/Messages.h @@ -32,10 +32,16 @@ enum EMessage k_EMsgClientLeavingServer = k_EMsgClientBegin+4, k_EMsgClientPing = k_EMsgClientBegin+5, - // P2P messages - k_EMsgP2PBegin = 500, + // P2P authentication messages + k_EMsgP2PBegin = 600, k_EMsgP2PSendingTicket = k_EMsgP2PBegin+1, + // voice chat messages + k_EMsgVoiceChatBegin = 700, + k_EMsgVoiceChatPing = k_EMsgVoiceChatBegin+1, // just a keep alive message + k_EMsgVoiceChatData = k_EMsgVoiceChatBegin+2, // voice data from another player + + // force 32-bit size enum so the wire protocol doesn't get outgrown later k_EForceDWORD = 0x7fffffff, @@ -211,6 +217,30 @@ private: uint64 m_ulSteamID; }; +// voice chat ping +struct MsgVoiceChatPing_t +{ + MsgVoiceChatPing_t() : m_dwMessageType( LittleDWord( k_EMsgVoiceChatPing ) ) {} + DWORD GetMessageType() const { return LittleDWord( m_dwMessageType ); } + +private: + const DWORD m_dwMessageType; +}; + +// voice chat data +struct MsgVoiceChatData_t +{ + MsgVoiceChatData_t() : m_dwMessageType( LittleDWord( k_EMsgVoiceChatData ) ) {} + DWORD GetMessageType() const { return LittleDWord( m_dwMessageType ); } + + void SetDataLength( uint32 unLength ) { m_uDataLength = LittleDWord( unLength ); } + uint32 GetDataLength() const { return LittleDWord( m_uDataLength ); } + +private: + const DWORD m_dwMessageType; + uint32 m_uDataLength; +}; + #pragma pack( pop ) #endif // MESSAGES_H \ No newline at end of file diff --git a/SteamworksExample/RemoteStorage.cpp b/SteamworksExample/RemoteStorage.cpp index 6f55495..5644728 100644 --- a/SteamworksExample/RemoteStorage.cpp +++ b/SteamworksExample/RemoteStorage.cpp @@ -7,7 +7,8 @@ #include "stdafx.h" #include "RemoteStorage.h" -#include "BaseMenu.h" +#include "RemoteStorageSync.h" +#include #define CLOUDDISP_FONT_HEIGHT 20 #define CLOUDDISP_COLUMN_WIDTH 600 @@ -30,11 +31,135 @@ // //----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: CRemoteStorage implementation +//----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- -CRemoteStorage::CRemoteStorage( IGameEngine *pGameEngine ) -: m_pGameEngine( pGameEngine ) +CRemoteStorage::CRemoteStorage( IGameEngine *pGameEngine ) : m_pGameEngine( pGameEngine ), m_pRemoteStorageSync(NULL) +{ + m_pRemoteStorageScreen = new CRemoteStorageScreen( pGameEngine ); + +#ifdef _PS3 + m_pRemoteStorageSync = new CRemoteStorageSync( pGameEngine ); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CRemoteStorage::~CRemoteStorage() +{ + if ( m_pRemoteStorageSync ) + { + assert( m_pRemoteStorageSync->BFinished() ); + delete m_pRemoteStorageSync; + } + + delete m_pRemoteStorageScreen; +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when the user selects view remote storage files +//----------------------------------------------------------------------------- +void CRemoteStorage::Show() +{ + m_eState = k_ERemoteStorageStateIdle; + CheckState(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Check's if we have finished our current step and should advance to the next +// +// NOTE: These steps are not necessary for platforms where the Steam client is running. On those platforms, +// the Steam client will take care of synchronization and m_pRemoteStorageSync will be NULL +//----------------------------------------------------------------------------- +void CRemoteStorage::CheckState() +{ + switch( m_eState ) + { + default: + case k_ERemoteStorageStateIdle: + if ( m_pRemoteStorageSync ) + m_pRemoteStorageSync->SynchronizeToClient(); + + // advance to next state + m_eState = k_ERemoteStorageStateSyncToClient; + + // fall through + + case k_ERemoteStorageStateSyncToClient: + if ( m_pRemoteStorageSync && !m_pRemoteStorageSync->BFinished() ) + break; + + // advance to next state + m_eState = k_ERemoteStorageStateDisplayMessage; + m_pRemoteStorageScreen->Show(); + break; + + case k_ERemoteStorageStateDisplayMessage: + if ( !m_pRemoteStorageScreen->BFinished() ) + break; + + // advance to next state + m_eState = k_ERemoteStorageStateSyncToServer; + if ( m_pRemoteStorageSync ) + m_pRemoteStorageSync->SynchronizeToServer(); + + // fall through + + case k_ERemoteStorageStateSyncToServer: + if ( m_pRemoteStorageSync && !m_pRemoteStorageSync->BFinished() ) + break; + + // complete. Return to the main menu + m_eState = k_ERemoteStorageStateIdle; + SpaceWarClient()->SetGameState( k_EClientGameMenu ); + + break; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Called once per frame +//----------------------------------------------------------------------------- +void CRemoteStorage::Render() +{ + if ( m_pRemoteStorageSync && (m_eState == k_ERemoteStorageStateSyncToClient || m_eState == k_ERemoteStorageStateSyncToServer) ) + m_pRemoteStorageSync->Render(); + + if ( m_eState == k_ERemoteStorageStateDisplayMessage ) + m_pRemoteStorageScreen->Render(); + + CheckState(); +} + + +//----------------------------------------------------------------------------- +// Purpose: A sync menu item has been selected +//----------------------------------------------------------------------------- +void CRemoteStorage::OnMenuSelection( ERemoteStorageSyncMenuCommand selection ) +{ + if ( m_pRemoteStorageSync ) + m_pRemoteStorageSync->OnMenuSelection( selection ); +} + + +//----------------------------------------------------------------------------- +// Purpose: CRemoteStorageScreen implementation +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CRemoteStorageScreen::CRemoteStorageScreen( IGameEngine *pGameEngine ) : m_pGameEngine( pGameEngine ) { m_rgchGreeting[0] = 0; strncpy( m_rgchGreeting, "", sizeof( m_rgchGreeting ) ); @@ -46,24 +171,30 @@ CRemoteStorage::CRemoteStorage( IGameEngine *pGameEngine ) if ( !m_hDisplayFont ) OutputDebugString( "RemoteStorage font was not created properly, text won't draw\n" ); - GetFileStats(); + GetFileStats(); +} - if ( m_pSteamRemoteStorage->FileExists( MESSAGE_FILE_NAME ) ) + +//----------------------------------------------------------------------------- +// Purpose: Load the user's saved message +//----------------------------------------------------------------------------- +void CRemoteStorageScreen::LoadMessage() +{ + if ( !m_pSteamRemoteStorage->FileExists( MESSAGE_FILE_NAME ) ) + return; + + int32 cubFile = m_pSteamRemoteStorage->GetFileSize( MESSAGE_FILE_NAME ); + if ( cubFile >= sizeof( m_rgchGreeting ) ) { - int32 cubFile = m_pSteamRemoteStorage->GetFileSize( MESSAGE_FILE_NAME ); - - if ( cubFile >= sizeof( m_rgchGreeting ) ) - { - // ?? too big, nuke it - char c = 0; - OutputDebugString( "RemoteStorage: File was larger than expected. . .\n" ); - m_pSteamRemoteStorage->FileWrite( MESSAGE_FILE_NAME, &c, 1 ); - } - else - { - int32 cubRead = m_pSteamRemoteStorage->FileRead( MESSAGE_FILE_NAME, m_rgchGreeting, sizeof( m_rgchGreeting ) - 1 ); - m_rgchGreeting[cubRead] = 0; // null-terminate - } + // ?? too big, nuke it + char c = 0; + OutputDebugString( "RemoteStorage: File was larger than expected. . .\n" ); + m_pSteamRemoteStorage->FileWrite( MESSAGE_FILE_NAME, &c, 1 ); + } + else + { + int32 cubRead = m_pSteamRemoteStorage->FileRead( MESSAGE_FILE_NAME, m_rgchGreeting, sizeof( m_rgchGreeting ) - 1 ); + m_rgchGreeting[cubRead] = 0; // null-terminate } } @@ -71,7 +202,7 @@ CRemoteStorage::CRemoteStorage( IGameEngine *pGameEngine ) //----------------------------------------------------------------------------- // Purpose: Update stats on our files in the Cloud //----------------------------------------------------------------------------- -void CRemoteStorage::GetFileStats() +void CRemoteStorageScreen::GetFileStats() { m_nBytesQuota = 0; m_nAvailableBytes = 0; @@ -80,10 +211,20 @@ void CRemoteStorage::GetFileStats() } +//----------------------------------------------------------------------------- +// Purpose: Called when the user selects view remote storage files +//----------------------------------------------------------------------------- +void CRemoteStorageScreen::Show() +{ + GetFileStats(); + LoadMessage(); +} + + //----------------------------------------------------------------------------- // Purpose: Render the Remote Storage page //----------------------------------------------------------------------------- -void CRemoteStorage::Render() +void CRemoteStorageScreen::Render() { uint64 ulCurrentTickCount = m_pGameEngine->GetGameTickCount(); @@ -241,8 +382,5 @@ void CRemoteStorage::Render() _snprintf( rgchBuffer, sizeof( rgchBuffer), "!! QUOTA EXCEEDED !!" ); m_pGameEngine->BDrawString( m_hDisplayFont, rect, D3DCOLOR_ARGB( 255, 25, 200, 25 ), TEXTPOS_CENTER|TEXTPOS_VCENTER, rgchBuffer ); } - } } - - diff --git a/SteamworksExample/RemoteStorage.h b/SteamworksExample/RemoteStorage.h index bd886a7..76301e9 100644 --- a/SteamworksExample/RemoteStorage.h +++ b/SteamworksExample/RemoteStorage.h @@ -13,26 +13,78 @@ class ISteamUser; class CSpaceWarClient; +class IRemoteStorageSync; +class CRemoteStorageScreen; +enum ERemoteStorageSyncMenuCommand +{ + k_EMenuCommandNone = 0, + k_EMenuCommandProgress = 1, + k_EMenuCommandSyncComplete = 2, + k_EMenuCommandKeepClient = 3, + k_EMenuCommandKeepServer = 4 +}; + +//----------------------------------------------------------------------------- +// Purpose: Example of Steam Cloud +//----------------------------------------------------------------------------- class CRemoteStorage { public: // Constructor CRemoteStorage( IGameEngine *pGameEngine ); + ~CRemoteStorage(); + + // call when user changes to this menu + void Show(); + + // Display the remote storage screen + void Render(); + + // A sync menu item has been selected + void OnMenuSelection( ERemoteStorageSyncMenuCommand selection ); + +private: + void CheckState(); + + enum ERemoteStorageState + { + k_ERemoteStorageStateIdle, + k_ERemoteStorageStateSyncToClient, + k_ERemoteStorageStateDisplayMessage, + k_ERemoteStorageStateSyncToServer, + }; + + IGameEngine *m_pGameEngine; + ERemoteStorageState m_eState; + IRemoteStorageSync *m_pRemoteStorageSync; + CRemoteStorageScreen *m_pRemoteStorageScreen; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Screen where user can enter their custom messasge +//----------------------------------------------------------------------------- +class CRemoteStorageScreen +{ +public: + CRemoteStorageScreen( IGameEngine *pGameEngine ); + + // call when user changes to this menu + void Show(); // Display the remote storage screen void Render(); // Done showing this page? bool BFinished() { return m_bFinished; } - private: - void GetFileStats(); + void LoadMessage(); - // Engine + // Game engine IGameEngine *m_pGameEngine; // Display font @@ -55,4 +107,6 @@ private: int32 m_nAvailableBytes; }; + + #endif diff --git a/SteamworksExample/ServerBrowser.cpp b/SteamworksExample/ServerBrowser.cpp index 408c3d4..a91e5ae 100644 --- a/SteamworksExample/ServerBrowser.cpp +++ b/SteamworksExample/ServerBrowser.cpp @@ -89,9 +89,7 @@ void CServerBrowser::RefreshInternetServers() m_pMenu->Rebuild( m_ListGameServers, m_bRequestingServers ); -#ifndef _PS3 Steamworks_TestSecret(); -#endif // Allocate some filters, there are some common pre-defined values that can be used: // diff --git a/SteamworksExample/Ship.cpp b/SteamworksExample/Ship.cpp index ec8016e..4b79144 100644 --- a/SteamworksExample/Ship.cpp +++ b/SteamworksExample/Ship.cpp @@ -502,9 +502,7 @@ void CShip::SetExploding( bool bExploding ) if ( m_bExploding == bExploding ) return; -#ifndef _PS3 Steamworks_TestSecret(); -#endif // Track that we are exploding, and disable collision detection m_bExploding = bExploding; diff --git a/SteamworksExample/SpaceWar.h b/SteamworksExample/SpaceWar.h index 2f202d5..4d8ec69 100644 --- a/SteamworksExample/SpaceWar.h +++ b/SteamworksExample/SpaceWar.h @@ -120,18 +120,18 @@ inline T QWordSwap( T dw ) #ifndef _PS3 -#define LittleShort( val ) ( val ) +#define LittleInt16( val ) ( val ) #define LittleWord( val ) ( val ) -#define LittleLong( val ) ( val ) +#define LittleInt32( val ) ( val ) #define LittleDWord( val ) ( val ) #define LittleQWord( val ) ( val ) #define LittleFloat( val ) ( val ) #else -#define LittleShort( val ) WordSwap( val ) +#define LittleInt16( val ) WordSwap( val ) #define LittleWord( val ) WordSwap( val ) -#define LittleLong( val ) DWordSwap( val ) +#define LittleInt32( val ) DWordSwap( val ) #define LittleDWord( val ) DWordSwap( val ) #define LittleQWord( val ) QWordSwap( val ) #define LittleFloat( val ) DWordSwap( val ) @@ -178,6 +178,10 @@ enum EClientGameState k_EClientRemoteStorage, k_EClientLeaderboards, k_EClientMinidump, + k_EClientConnectingToSteam, + k_EClientLinkSteamAccount, + k_EClientAutoCreateAccount, + k_EClientRetrySteamConnection, }; diff --git a/SteamworksExample/SpaceWarClient.cpp b/SteamworksExample/SpaceWarClient.cpp index efa7dc4..6318b08 100644 --- a/SteamworksExample/SpaceWarClient.cpp +++ b/SteamworksExample/SpaceWarClient.cpp @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ // // Purpose: Main class for the space war game client // @@ -8,6 +8,7 @@ #include "stdafx.h" #include "SpaceWarClient.h" #include "SpaceWarServer.h" +#include "connectingmenu.h" #include "MainMenu.h" #include "QuitMenu.h" #include "stdlib.h" @@ -16,6 +17,7 @@ #include "Leaderboards.h" #include "Lobby.h" #include "p2pauth.h" +#include "voicechat.h" #include "steam/steamencryptedappticket.h" CSpaceWarClient *g_pSpaceWarClient = NULL; @@ -24,14 +26,15 @@ CSpaceWarClient* SpaceWarClient() { return g_pSpaceWarClient; } //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- -CSpaceWarClient::CSpaceWarClient( IGameEngine *pGameEngine, CSteamID steamIDUser ) : +CSpaceWarClient::CSpaceWarClient( IGameEngine *pGameEngine ) : m_CallbackP2PSessionConnectFail( this, &CSpaceWarClient::OnP2PSessionConnectFail ), m_LobbyGameCreated( this, &CSpaceWarClient::OnLobbyGameCreated ), m_AvatarImageLoadedCreated( this, &CSpaceWarClient::OnAvatarImageLoaded ), m_IPCFailureCallback( this, &CSpaceWarClient::OnIPCFailure ), - m_SteamShutdownCallback( this, &CSpaceWarClient::OnSteamShutdown ) + m_SteamShutdownCallback( this, &CSpaceWarClient::OnSteamShutdown ), + m_SteamServersConnected( this, &CSpaceWarClient::OnSteamServersConnected ), + m_SteamServerConnectFailure( this, &CSpaceWarClient::OnSteamServerConnectFailure ) { - m_SteamIDLocalUser = steamIDUser; Init( pGameEngine ); } @@ -42,9 +45,25 @@ CSpaceWarClient::CSpaceWarClient( IGameEngine *pGameEngine, CSteamID steamIDUser //----------------------------------------------------------------------------- void CSpaceWarClient::Init( IGameEngine *pGameEngine ) { + // On PC/OSX we always know the user has a SteamID and is logged in already, + // as Steam enforces this before game launch. On PS3 however the game must + // initiate the logon and we need to check the state here and block the user + // while Steam connects. + if ( SteamUser()->BLoggedOn() ) + { + m_SteamIDLocalUser = SteamUser()->GetSteamID(); + m_eGameState = k_EClientGameMenu; + } +#ifdef _PS3 + else + { + m_eGameState = k_EClientConnectingToSteam; + SteamUser()->LogOn( true ); + } +#endif + g_pSpaceWarClient = this; m_pGameEngine = pGameEngine; - m_eGameState = k_EClientGameMenu; m_uPlayerWhoWonGame = 0; m_ulStateTransitionTime = m_pGameEngine->GetGameTickCount(); m_ulLastNetworkDataReceivedTime = 0; @@ -80,6 +99,9 @@ void CSpaceWarClient::Init( IGameEngine *pGameEngine ) // Initialize main menu m_pMainMenu = new CMainMenu( pGameEngine ); + // Initialize connecting menu + m_pConnectingMenu = new CConnectingMenu( pGameEngine ); + // Initialize pause menu m_pQuitMenu = new CQuitMenu( pGameEngine ); @@ -101,6 +123,9 @@ void CSpaceWarClient::Init( IGameEngine *pGameEngine ) // Remote Storage page m_pRemoteStorage = new CRemoteStorage( pGameEngine ); + + // P2P voice chat + m_pVoiceChat = new CVoiceChat( pGameEngine ); } @@ -130,6 +155,9 @@ CSpaceWarClient::~CSpaceWarClient() if ( m_pMainMenu ) delete m_pMainMenu; + if ( m_pConnectingMenu ) + delete m_pConnectingMenu; + if ( m_pQuitMenu ) delete m_pQuitMenu; @@ -142,6 +170,9 @@ CSpaceWarClient::~CSpaceWarClient() if ( m_pServerBrowser ) delete m_pServerBrowser; + if ( m_pVoiceChat ) + delete m_pVoiceChat; + for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgpShips[i] ) @@ -171,6 +202,11 @@ void CSpaceWarClient::DisconnectFromServer() m_pP2PAuthedGame->EndGame(); } + if ( m_pVoiceChat ) + { + m_pVoiceChat->StopVoiceChat(); + } + // forget the game server ID if ( m_steamIDGameServer.IsValid() ) { @@ -207,9 +243,7 @@ void CSpaceWarClient::OnReceiveServerInfo( CSteamID steamIDGameServer, bool bVAC msg.SetSteamID( SteamUser()->GetSteamID().ConvertToUint64() ); #endif -#ifndef _PS3 Steamworks_TestSecret(); -#endif if ( msg.GetTokenLen() < 1 ) OutputDebugString( "Warning: Looks like InitiateGameConnection didn't give us a good token\n" ); @@ -362,7 +396,11 @@ void CSpaceWarClient::OnReceiveServerUpdate( ServerSpaceWarUpdateData_t *pUpdate } } - // Update the ships + // update all players that are active + if ( m_pVoiceChat ) + m_pVoiceChat->MarkAllPlayersInactive(); + + // Update the players for( uint32 i=0; i < MAX_PLAYERS_PER_SERVER; ++i ) { // Update steamid array with data from server @@ -392,6 +430,9 @@ void CSpaceWarClient::OnReceiveServerUpdate( ServerSpaceWarUpdateData_t *pUpdate m_rgpShips[i]->SetIsLocalPlayer( false ); m_rgpShips[i]->OnReceiveServerUpdate( pUpdateData->AccessShipUpdateData( i ) ); + + if ( m_pVoiceChat ) + m_pVoiceChat->MarkPlayerAsActive( m_rgSteamIDPlayers[i] ); } else { @@ -622,10 +663,19 @@ void CSpaceWarClient::ReceiveNetworkData() break; } } - else + else { + // the message is from another player EMessage eMsg = (EMessage)LittleDWord( *(DWORD*)pchRecvBuf ); - m_pP2PAuthedGame->HandleMessage( eMsg, pchRecvBuf ); + + if ( m_pP2PAuthedGame->HandleMessage( eMsg, pchRecvBuf ) ) + continue; // this was a P2P auth message + + if ( m_pVoiceChat->HandleMessage( steamIDRemote, eMsg, pchRecvBuf ) ) + continue; + + // Unhandled message + OutputDebugString( "Received unknown message on our listen socket\n" ); } } @@ -791,6 +841,15 @@ void CSpaceWarClient::OnMenuSelection( LeaderboardMenuItem_t selection ) } +//----------------------------------------------------------------------------- +// Purpose: Handles menu actions when viewing the remote storage sync screen +//----------------------------------------------------------------------------- +void CSpaceWarClient::OnMenuSelection( ERemoteStorageSyncMenuCommand selection ) +{ + m_pRemoteStorage->OnMenuSelection( selection ); +} + + //----------------------------------------------------------------------------- // Purpose: does work on transitioning from one game state to another //----------------------------------------------------------------------------- @@ -844,6 +903,16 @@ void CSpaceWarClient::OnGameStateChanged( EClientGameState eGameStateNew ) // we've switched to the leaderboard menu m_pLeaderboards->Show(); } + else if ( m_eGameState == k_EClientGameActive ) + { + // start voice chat + m_pVoiceChat->StartVoiceChat(); + } + else if ( m_eGameState == k_EClientRemoteStorage ) + { + // we've switched to the remote storage menu + m_pRemoteStorage->Show(); + } } //----------------------------------------------------------------------------- @@ -857,7 +926,7 @@ void CSpaceWarClient::OnIPCFailure( IPCFailure_t *failure ) if ( !bExiting ) { OutputDebugString( "Steam IPC Failure, shutting down\n" ); -#if defined( WIN32 ) +#if defined( _WIN32 ) ::MessageBoxA( NULL, "Connection to Steam Lost, Exiting", "Steam Connection Error", MB_OK ); #endif m_pGameEngine->Shutdown(); @@ -882,6 +951,32 @@ void CSpaceWarClient::OnSteamShutdown( SteamShutdown_t *callback ) } +//----------------------------------------------------------------------------- +// Purpose: Handles notification that we are now connected to Steam +//----------------------------------------------------------------------------- +void CSpaceWarClient::OnSteamServersConnected( SteamServersConnected_t *callback ) +{ + if ( SteamUser()->BLoggedOn() ) + m_eGameState = k_EClientGameMenu; + else + { + OutputDebugString( "Got SteamServersConnected_t, but not logged on?\n" ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles notification that we are now connected to Steam +//----------------------------------------------------------------------------- +void CSpaceWarClient::OnSteamServerConnectFailure( SteamServerConnectFailure_t *callback ) +{ + char rgchString[256]; + _snprintf( rgchString, 256, "SteamServerConnectFailure_t: %d\n", callback->m_eResult ); + + m_pConnectingMenu->OnConnectFailure(); +} + + //----------------------------------------------------------------------------- // Purpose: Main frame function, updates the state of the world and performs rendering //----------------------------------------------------------------------------- @@ -926,6 +1021,40 @@ void CSpaceWarClient::RunFrame() // Update state for everything switch ( m_eGameState ) { + case k_EClientConnectingToSteam: + m_pStarField->Render(); + m_pConnectingMenu->RunFrame(); + break; + case k_EClientRetrySteamConnection: +#ifdef _PS3 + m_pStarField->Render(); + SteamUser()->LogOn( true ); + m_pConnectingMenu->Reset(); + SetGameState( k_EClientConnectingToSteam ); +#else + OutputDebugString( "Invalidate state k_EClientRetrySteamConnection hit on non-PS3 platform" ); +#endif + break; + case k_EClientLinkSteamAccount: +#ifdef _PS3 + m_pStarField->Render(); + SteamUser()->LogOnAndLinkSteamAccountToPSN( true, "jmccaskeybeta", "test123" ); + m_pConnectingMenu->Reset(); + SetGameState( k_EClientConnectingToSteam ); +#else + OutputDebugString( "Invalidate state k_EClientLinkSteamAccount hit on non-PS3 platform" ); +#endif + break; + case k_EClientAutoCreateAccount: +#ifdef _PS3 + m_pStarField->Render(); + m_pConnectingMenu->Reset(); + SteamUser()->LogOnAndCreateNewSteamAccountIfNeeded( true ); + SetGameState( k_EClientConnectingToSteam ); +#else + OutputDebugString( "Invalidate state k_EClientAutoCreateAccount hit on non-PS3 platform" ); +#endif + break; case k_EClientGameMenu: m_pStarField->Render(); m_pMainMenu->RunFrame(); @@ -1045,13 +1174,10 @@ void CSpaceWarClient::RunFrame() case k_EClientRemoteStorage: m_pStarField->Render(); m_pRemoteStorage->Render(); - - if ( m_pRemoteStorage->BFinished() ) - SetGameState( k_EClientGameMenu ); break; case k_EClientMinidump: -#ifndef _PS3 +#ifdef _WIN32 RaiseException( EXCEPTION_NONCONTINUABLE_EXCEPTION, EXCEPTION_NONCONTINUABLE, 0, NULL ); @@ -1088,12 +1214,17 @@ void CSpaceWarClient::RunFrame() DrawHUDText(); DrawWinnerDrawOrWaitingText(); + m_pVoiceChat->RunFrame(); + if ( bEscapePressed ) SetGameState( k_EClientGameQuitMenu ); + break; case k_EClientGameActive: m_pStarField->Render(); + + // Update all the entities... m_pSun->RunFrame(); @@ -1105,9 +1236,12 @@ void CSpaceWarClient::RunFrame() DrawHUDText(); m_pStatsAndAchievements->RunFrame(); + + m_pVoiceChat->RunFrame(); if ( bEscapePressed ) SetGameState( k_EClientGameQuitMenu ); + break; case k_EClientGameExiting: DisconnectFromServer(); @@ -1233,9 +1367,13 @@ void CSpaceWarClient::DrawHUDText() // players on the same game server as us (if using the Steam game server auth API) // and on ourself. char rgchPlayerName[128]; + CSteamID playerSteamID( m_rgSteamIDPlayers[i] ); + + const char *pszVoiceState = m_pVoiceChat->IsPlayerTalking( playerSteamID ) ? "(VoiceChat)" : ""; + if ( m_rgSteamIDPlayers[i].IsValid() ) { - _snprintf( rgchPlayerName, ARRAYSIZE( rgchPlayerName ), "%s", SteamFriends()->GetFriendPersonaName( m_rgSteamIDPlayers[i] ) ); + _snprintf( rgchPlayerName, ARRAYSIZE( rgchPlayerName ), "%s", SteamFriends()->GetFriendPersonaName( playerSteamID ) ); } else { @@ -1245,7 +1383,7 @@ void CSpaceWarClient::DrawHUDText() // We also want to use the Steam Avatar image inside the HUD if it is available. // We look it up via GetMediumFriendAvatar, which returns an image index we use // to look up the actual RGBA data below. - int iImage = SteamFriends()->GetMediumFriendAvatar( m_rgSteamIDPlayers[i] ); + int iImage = SteamFriends()->GetMediumFriendAvatar( playerSteamID ); HGAMETEXTURE hTexture = NULL; if ( iImage != -1 ) hTexture = GetSteamImageAsTexture( iImage ); @@ -1267,7 +1405,7 @@ void CSpaceWarClient::DrawHUDText() rect.right += nAvatarWidth + nSpaceBetweenAvatarAndScore; } - _snprintf( rgchBuffer, sizeof( rgchBuffer), "%s\nScore: %2u", rgchPlayerName, m_rguPlayerScores[i] ); + _snprintf( rgchBuffer, sizeof( rgchBuffer), "%s\nScore: %2u %s", rgchPlayerName, m_rguPlayerScores[i], pszVoiceState ); m_pGameEngine->BDrawString( m_hHUDFont, rect, g_rgPlayerColors[i], TEXTPOS_LEFT|TEXTPOS_VCENTER, rgchBuffer ); break; case 1: @@ -1285,7 +1423,7 @@ void CSpaceWarClient::DrawHUDText() rect.left -= nAvatarWidth + nSpaceBetweenAvatarAndScore; } - _snprintf( rgchBuffer, sizeof( rgchBuffer), "%s\nScore: %2u", rgchPlayerName, m_rguPlayerScores[i] ); + _snprintf( rgchBuffer, sizeof( rgchBuffer), "%s\nScore: %2u ", rgchPlayerName, m_rguPlayerScores[i], pszVoiceState ); m_pGameEngine->BDrawString( m_hHUDFont, rect, g_rgPlayerColors[i], TEXTPOS_RIGHT|TEXTPOS_VCENTER, rgchBuffer ); break; case 2: @@ -1302,7 +1440,7 @@ void CSpaceWarClient::DrawHUDText() rect.left += nAvatarWidth + nSpaceBetweenAvatarAndScore; } - _snprintf( rgchBuffer, sizeof( rgchBuffer), "%s\nScore: %2u", rgchPlayerName, m_rguPlayerScores[i] ); + _snprintf( rgchBuffer, sizeof( rgchBuffer), "%s\nScore: %2u %s", rgchPlayerName, m_rguPlayerScores[i], pszVoiceState ); m_pGameEngine->BDrawString( m_hHUDFont, rect, g_rgPlayerColors[i], TEXTPOS_LEFT|TEXTPOS_BOTTOM, rgchBuffer ); break; case 3: @@ -1319,7 +1457,7 @@ void CSpaceWarClient::DrawHUDText() rect.left -= nAvatarWidth + nSpaceBetweenAvatarAndScore; } - _snprintf( rgchBuffer, sizeof( rgchBuffer), "%s\nScore: %2u", rgchPlayerName, m_rguPlayerScores[i] ); + _snprintf( rgchBuffer, sizeof( rgchBuffer), "%s\nScore: %2u %s", rgchPlayerName, m_rguPlayerScores[i], pszVoiceState ); m_pGameEngine->BDrawString( m_hHUDFont, rect, g_rgPlayerColors[i], TEXTPOS_RIGHT|TEXTPOS_BOTTOM, rgchBuffer ); break; default: @@ -1582,6 +1720,7 @@ void CSpaceWarClient::OnRequestEncryptedAppTicket( EncryptedAppTicketResponse_t SteamUser()->GetEncryptedAppTicket( rgubTicket, sizeof( rgubTicket), &cubTicket ); +#ifdef _WIN32 // normally at this point you transmit the encrypted ticket to the service that knows the decryption key // this code is just to demonstrate the ticket cracking library @@ -1608,6 +1747,7 @@ void CSpaceWarClient::OnRequestEncryptedAppTicket( EncryptedAppTicketResponse_t uint32 *punSecretData = (uint32 *)SteamEncryptedAppTicket_GetUserVariableData( rgubDecrypted, cubDecrypted, &cubData ); if ( cubData != sizeof( uint32 ) || *punSecretData != k_unSecretData ) OutputDebugString( "Failed to retrieve secret data\n" ); +#endif } else if ( pEncryptedAppTicketResponse->m_eResult == k_EResultLimitExceeded ) { diff --git a/SteamworksExample/SpaceWarClient.h b/SteamworksExample/SpaceWarClient.h index 02785fe..7f1bee5 100644 --- a/SteamworksExample/SpaceWarClient.h +++ b/SteamworksExample/SpaceWarClient.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ // // Purpose: Main class for the space war game client // @@ -18,14 +18,8 @@ #include "StatsAndAchievements.h" #include "RemoteStorage.h" -// Must define this stuff before BaseMenu.h as it depends on calling back into us through these accessors -class CSpaceWarClient; -extern CSpaceWarClient *g_pSpaceWarClient; -CSpaceWarClient *SpaceWarClient(); - -#include "BaseMenu.h" - // Forward class declaration +class CConnectingMenu; class CMainMenu; class CQuitMenu; class CSpaceWarServer; @@ -35,6 +29,7 @@ class CLobby; class CLeaderboards; class CP2PAuthPlayer; class CP2PAuthedGame; +class CVoiceChat; // Height of the HUD font #define HUD_FONT_HEIGHT 18 @@ -89,12 +84,11 @@ struct LeaderboardMenuItem_t }; - class CSpaceWarClient { public: //Constructor - CSpaceWarClient( IGameEngine *pEngine, CSteamID steamIDUser ); + CSpaceWarClient( IGameEngine *pEngine ); // Shared init for all constructors void Init( IGameEngine *pGameEngine ); @@ -146,6 +140,7 @@ public: void OnMenuSelection( LobbyMenuItem_t selection ); void OnMenuSelection( LeaderboardMenuItem_t selection ); + void OnMenuSelection( ERemoteStorageSyncMenuCommand selection ); // Set game state void SetGameState( EClientGameState eState ); @@ -322,6 +317,9 @@ private: // Main menu instance CMainMenu *m_pMainMenu; + // Connecting menu instance + CConnectingMenu *m_pConnectingMenu; + // Pause menu instance CQuitMenu *m_pQuitMenu; @@ -352,14 +350,22 @@ private: // callback for when the lobby game server has started STEAM_CALLBACK( CSpaceWarClient, OnLobbyGameCreated, LobbyGameCreated_t, m_LobbyGameCreated ); STEAM_CALLBACK( CSpaceWarClient, OnAvatarImageLoaded, AvatarImageLoaded_t, m_AvatarImageLoadedCreated ); + + // callbacks for Steam connection state + STEAM_CALLBACK( CSpaceWarClient, OnSteamServersConnected, SteamServersConnected_t, m_SteamServersConnected ); + STEAM_CALLBACK( CSpaceWarClient, OnSteamServerConnectFailure, SteamServerConnectFailure_t, m_SteamServerConnectFailure ); // lobby browser menu CLobbyBrowser *m_pLobbyBrowser; // local lobby display CLobby *m_pLobby; + // p2p game auth manager CP2PAuthedGame *m_pP2PAuthedGame; + + // p2p voice chat + CVoiceChat *m_pVoiceChat; // connection handler STEAM_CALLBACK( CSpaceWarClient, OnP2PSessionConnectFail, P2PSessionConnectFail_t, m_CallbackP2PSessionConnectFail ); @@ -375,4 +381,8 @@ private: CCallResult< CSpaceWarClient, EncryptedAppTicketResponse_t > m_SteamCallResultEncryptedAppTicket; }; +// Must define this stuff before BaseMenu.h as it depends on calling back into us through these accessors +extern CSpaceWarClient *g_pSpaceWarClient; +CSpaceWarClient *SpaceWarClient(); + #endif // SPACEWARCLIENT_H diff --git a/SteamworksExample/StarField.cpp b/SteamworksExample/StarField.cpp index b75994e..9cdac10 100644 --- a/SteamworksExample/StarField.cpp +++ b/SteamworksExample/StarField.cpp @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ // // Purpose: Class for rendering the starfield // @@ -16,31 +16,42 @@ CStarField::CStarField( IGameEngine *pGameEngine ) { m_pGameEngine = pGameEngine; - StarVertex_t StarVertex; - int32 width = pGameEngine->GetViewportWidth(); - int32 height = pGameEngine->GetViewportHeight(); + Init(); +} +void CStarField::Init() +{ + StarVertex_t StarVertex; + m_nWidth = m_pGameEngine->GetViewportWidth(); + m_nHeight = m_pGameEngine->GetViewportHeight(); + + m_VecStars.clear(); + // Generate star field data for( int i=0; i < STARFIELD_STAR_COUNT; ++i ) { int32 nRand = (rand()%(255-50))+50; //value between 50 and 255 for shades of gray StarVertex.color = D3DCOLOR_ARGB( 255, nRand, nRand, nRand ); - - StarVertex.x = (float)(rand()%width); - StarVertex.y = (float)(rand()%height); - + + StarVertex.x = (float)(rand()%m_nWidth); + StarVertex.y = (float)(rand()%m_nHeight); + m_VecStars.push_back( StarVertex ); - + // bugbug jmccaskey - sometimes make "big stars" which are 4 points right next to each other? } } - //----------------------------------------------------------------------------- // Purpose: Render the star field //----------------------------------------------------------------------------- void CStarField::Render() { + if ( ( m_pGameEngine->GetViewportWidth() != m_nWidth ) || ( m_pGameEngine->GetViewportHeight() != m_nHeight ) ) + { + Init(); + } + for( size_t i = 0; i < m_VecStars.size(); ++i ) { m_pGameEngine->BDrawPoint( m_VecStars[i].x, m_VecStars[i].y, m_VecStars[i].color ); diff --git a/SteamworksExample/StarField.h b/SteamworksExample/StarField.h index 364e781..7372835 100644 --- a/SteamworksExample/StarField.h +++ b/SteamworksExample/StarField.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ // // Purpose: Class for rendering the starfield // @@ -29,6 +29,12 @@ public: void Render(); private: + + void Init(); + +private: + int m_nWidth; + int m_nHeight; // Game engine instance we are running under IGameEngine *m_pGameEngine; diff --git a/SteamworksExample/StatsAndAchievements.cpp b/SteamworksExample/StatsAndAchievements.cpp index 8f513f3..662eef8 100644 --- a/SteamworksExample/StatsAndAchievements.cpp +++ b/SteamworksExample/StatsAndAchievements.cpp @@ -82,12 +82,6 @@ void CStatsAndAchievements::RunFrame() return; } - // If yes, are we connected to the back end? if no, wait - if ( !m_pSteamUser->BLoggedOn() ) - { - return; - } - // If yes, request our stats bool bSuccess = m_pSteamUserStats->RequestCurrentStats( ); diff --git a/SteamworksExample/SteamworksExample.vcproj b/SteamworksExample/SteamworksExample.vcproj index e0446d5..9285fbc 100644 --- a/SteamworksExample/SteamworksExample.vcproj +++ b/SteamworksExample/SteamworksExample.vcproj @@ -97,7 +97,7 @@ /> @@ -385,6 +385,10 @@ RelativePath=".\BaseMenu.cpp" > + + @@ -445,6 +449,10 @@ RelativePath=".\Sun.cpp" > + + + + @@ -551,6 +563,10 @@ RelativePath=".\Sun.h" > + + diff --git a/SteamworksExample/p2pauth.cpp b/SteamworksExample/p2pauth.cpp index 376f7e4..246633a 100644 --- a/SteamworksExample/p2pauth.cpp +++ b/SteamworksExample/p2pauth.cpp @@ -63,7 +63,7 @@ void CNetworkTransport::OnP2PSessionRequest( P2PSessionRequest_t *pP2PSessionReq //----------------------------------------------------------------------------- -// Purpose: another user has sent us a packet - do we accept? +// Purpose: we send a packet to another user but it failed //----------------------------------------------------------------------------- void CNetworkTransport::OnP2PSessionConnectFail( P2PSessionConnectFail_t *pP2PSessionConnectFail ) { @@ -380,7 +380,7 @@ bool CP2PAuthedGame::HandleMessage( EMessage eMsg, void *pMessage ) { if ( m_rgpP2PAuthPlayer[i]->HandleMessage( eMsg, pMessage ) ) { - return true; + return true; // message handled } } } @@ -391,18 +391,18 @@ bool CP2PAuthedGame::HandleMessage( EMessage eMsg, void *pMessage ) if ( m_rgpQueuedMessage[i] == NULL ) { m_rgpQueuedMessage[i] = pMsg; - return false; + return true; // message handled } } break; } default: - { - OutputDebugString( "Received unknown message on our listen socket\n" ); - break; - } + // this is not a P2P auth message + break; } - return true; + + // message didn't got handled here + return false; } diff --git a/SteamworksExample/stdafx.h b/SteamworksExample/stdafx.h index 9022b8b..26394b5 100644 --- a/SteamworksExample/stdafx.h +++ b/SteamworksExample/stdafx.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ // // Purpose: // @@ -55,6 +55,9 @@ #include "d3d9.h" #include "d3dx9.h" +// XAudio2 header files +#include + typedef __int16 int16; typedef unsigned __int16 uint16; typedef __int32 int32; @@ -68,39 +71,24 @@ typedef unsigned __int64 uint64; #include "steam/isteammatchmaking.h" #include "steam/steam_gameserver.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 - -#elif _PS3 +#elif defined(POSIX) #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include + + #if defined(_PS3) + + #include "stdafx_ps3.h" + + #elif defined(OSX) + + #include + + #endif // Need to define some types on POSIX typedef short int16; @@ -111,6 +99,7 @@ typedef long long int64; typedef unsigned long long uint64; typedef uint32 DWORD; typedef DWORD HWND; +typedef DWORD HINSTANCE; typedef short SHORT; typedef long LONG; typedef unsigned char byte; @@ -128,7 +117,7 @@ typedef unsigned char uint8; #define FW_EXTRABOLD 800 #define FW_HEAVY 900 -/* Some VK_ defines from windows, we'll map these to PS3 controls */ +/* Some VK_ defines from windows, we'll map these for posix */ #define VK_BACK 0x08 #define VK_TAB 0x09 #define VK_RETURN 0x0D @@ -136,7 +125,9 @@ typedef unsigned char uint8; #define VK_CONTROL 0x11 #define VK_ESCAPE 0x1B #define VK_SPACE 0x20 +#define VK_LEFT 0x25 #define VK_UP 0x26 +#define VK_RIGHT 0x27 #define VK_DOWN 0x28 #define VK_SELECT 0x29 @@ -177,16 +168,24 @@ typedef struct tagRECT #include "steam/isteammatchmaking.h" #include "steam/steam_gameserver.h" -extern CellDbgFontConsoleId g_DbgFontConsoleID; -static void OutputDebugString( const char *pchMsg ) -{ - printf( "%s", pchMsg ); - cellDbgFontConsolePrintf( g_DbgFontConsoleID, "%s", pchMsg ); -} +extern void OutputDebugString( const char *pchMsg ); -// No _snprintf on PS3, use sprintf +// No _snprintf on POSIX, use snprintf +#ifndef _snprintf #define _snprintf snprintf +#endif #define ARRAYSIZE(a) sizeof(a)/sizeof(a[0]) -#endif // _PS3 \ No newline at end of file +#endif // POSIX + +#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 + diff --git a/client/steam.exe b/client/steam.exe index 75028c9..a598ed3 100644 Binary files a/client/steam.exe and b/client/steam.exe differ diff --git a/public/steam/isteamapps.h b/public/steam/isteamapps.h index fccbb61..c865814 100644 --- a/public/steam/isteamapps.h +++ b/public/steam/isteamapps.h @@ -28,9 +28,17 @@ public: // Takes AppID of DLC and checks if the user owns the DLC & if the DLC is installed virtual bool BIsDlcInstalled( AppId_t appID ) = 0; + + // returns the Unix time of the purchase of the app + virtual uint32 GetEarliestPurchaseUnixTime( AppId_t nAppID ) = 0; + + // Checks if the user is subscribed to the current app through a free weekend + // This function will return false for users who have a retail or other type of license + // Before using, please ask your Valve technical contact how to package and secure your free weekened + virtual bool BIsSubscribedFromFreeWeekend() = 0; }; -#define STEAMAPPS_INTERFACE_VERSION "STEAMAPPS_INTERFACE_VERSION003" +#define STEAMAPPS_INTERFACE_VERSION "STEAMAPPS_INTERFACE_VERSION004" // callbacks #pragma pack( push, 8 ) diff --git a/public/steam/isteamappticket.h b/public/steam/isteamappticket.h new file mode 100644 index 0000000..21fb9e1 --- /dev/null +++ b/public/steam/isteamappticket.h @@ -0,0 +1,28 @@ +//====== Copyright 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: a private, but well versioned, interface to get at critical bits +// of a steam3 appticket - consumed by the simple drm wrapper to let it +// ask about ownership with greater confidence. +// +//============================================================================= + +#ifndef ISTEAMAPPTICKET_H +#define ISTEAMAPPTICKET_H +#pragma once + +//----------------------------------------------------------------------------- +// Purpose: hand out a reasonable "future proof" view of an app ownership ticket +// the raw (signed) buffer, and indices into that buffer where the appid and +// steamid are located. the sizes of the appid and steamid are implicit in +// (each version of) the interface - currently uin32 appid and uint64 steamid +//----------------------------------------------------------------------------- +class ISteamAppTicket +{ +public: + virtual uint32 GetAppOwnershipTicketData( uint32 nAppID, void *pvBuffer, uint32 cbBufferLength, uint32 *piAppId, uint32 *piSteamId, uint32 *piSignature, uint32 *pcbSignature ) = 0; +}; + +#define STEAMAPPTICKET_INTERFACE_VERSION "STEAMAPPTICKET_INTERFACE_VERSION001" + + +#endif // ISTEAMAPPTICKET_H diff --git a/public/steam/isteamclient.h b/public/steam/isteamclient.h index 84a7b80..2e071dc 100644 --- a/public/steam/isteamclient.h +++ b/public/steam/isteamclient.h @@ -130,9 +130,12 @@ public: // callbacks will occur directly after the API function is called that generated the warning or message virtual void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction ) = 0; + // Trigger global shutdown for the DLL + virtual bool BShutdownIfAllPipesClosed() = 0; + }; -#define STEAMCLIENT_INTERFACE_VERSION "SteamClient009" +#define STEAMCLIENT_INTERFACE_VERSION "SteamClient010" //----------------------------------------------------------------------------- // Purpose: Base values for callback identifiers, each callback must diff --git a/public/steam/isteamfriends.h b/public/steam/isteamfriends.h index 1ff2671..0150ed8 100644 --- a/public/steam/isteamfriends.h +++ b/public/steam/isteamfriends.h @@ -86,6 +86,18 @@ enum k_cwchPersonaNameMax = 32, }; +//----------------------------------------------------------------------------- +// Purpose: user restriction flags +//----------------------------------------------------------------------------- +enum EUserRestriction +{ + k_nUserRestrictionNone = 0, // no known chat/content restriction + k_nUserRestrictionUnknown = 1, // we don't know yet (user offline) + k_nUserRestrictionChat = 2, // user is not allowed to send/recv text/voice chat +}; + + + // size limit on chat room or member metadata const uint32 k_cubChatMetadataMax = 8192; @@ -196,9 +208,37 @@ public: // gets the large (184x184) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set // returns -1 if this image has yet to be loaded, in this case wait for a AvatarImageLoaded_t callback and then call this again virtual int GetLargeFriendAvatar( CSteamID steamIDFriend ) = 0; + + // requests information about a user - persona name & avatar + // if bRequireNameOnly is set, then the avatar of a user isn't downloaded + // - it's a lot slower to download avatars and churns the local cache, so if you don't need avatars, don't request them + // if returns true, it means that data is being requested, and a PersonaStateChanged_t callback will be posted when it's retrieved + // if returns false, it means that we already have all the details about that user, and functions can be called immediately + virtual bool RequestUserInformation( CSteamID steamIDUser, bool bRequireNameOnly ) = 0; + + // requests information about a clan officer list + // when complete, data is returned in ClanOfficerListResponse_t call result + // this makes available the calls below + // you can only ask about clans that a user is a member of + // note that this won't download avatars automatically; if you get an officer, + // and no avatar image is available, call RequestUserInformation( steamID, false ) to download the avatar + virtual SteamAPICall_t RequestClanOfficerList( CSteamID steamIDClan ) = 0; + + // iteration of clan officers - can only be done when a RequestClanOfficerList() call has completed + + // returns the steamID of the clan owner + virtual CSteamID GetClanOwner( CSteamID steamIDClan ) = 0; + // returns the number of officers in a clan (including the owner) + virtual int GetClanOfficerCount( CSteamID steamIDClan ) = 0; + // returns the steamID of a clan officer, by index, of range [0,GetClanOfficerCount) + virtual CSteamID GetClanOfficerByIndex( CSteamID steamIDClan, int iOfficer ) = 0; + // if current user is chat restricted, he can't send or receive any text/voice chat messages. + // the user can't see custom avatars. But the user can be online and send/recv game invites. + // a chat restricted user can't add friends or join any groups. + virtual uint32 GetUserRestrictions() = 0; }; -#define STEAMFRIENDS_INTERFACE_VERSION "SteamFriends007" +#define STEAMFRIENDS_INTERFACE_VERSION "SteamFriends008" // callbacks #pragma pack( push, 8 ) @@ -281,6 +321,19 @@ struct AvatarImageLoaded_t int m_iTall; // height of the loaded image }; + +//----------------------------------------------------------------------------- +// Purpose: marks the return of a request officer list call +//----------------------------------------------------------------------------- +struct ClanOfficerListResponse_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 35 }; + CSteamID m_steamIDClan; + int m_cOfficers; + uint8 m_bSuccess; +}; + + #pragma pack( pop ) #endif // ISTEAMFRIENDS_H diff --git a/public/steam/isteamgameserver.h b/public/steam/isteamgameserver.h index 62e6c3e..fef56bb 100644 --- a/public/steam/isteamgameserver.h +++ b/public/steam/isteamgameserver.h @@ -113,6 +113,24 @@ public: // 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; + + // New auth system APIs - do not mix with the old auth system APIs. + // ---------------------------------------------------------------- + + // Retrieve ticket to be sent to the entity who wishes to authenticate you ( using BeginAuthSession API ). + // pcbTicket retrieves the length of the actual ticket. + virtual HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) = 0; + + // Authenticate ticket ( from GetAuthSessionTicket ) from entity steamID to be sure it is valid and isnt reused + // Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse ) + virtual EBeginAuthSessionResult BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID ) = 0; + + // Stop tracking started by BeginAuthSession - called when no longer playing game with this entity + virtual void EndAuthSession( CSteamID steamID ) = 0; + + // 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; + }; #define STEAMGAMESERVER_INTERFACE_VERSION "SteamGameServer010" diff --git a/public/steam/isteammatchmaking.h b/public/steam/isteammatchmaking.h index 33dcbbd..7b35007 100644 --- a/public/steam/isteammatchmaking.h +++ b/public/steam/isteammatchmaking.h @@ -19,6 +19,7 @@ // lobby type description enum ELobbyType { + k_ELobbyTypePrivate = 0, // only way to join the lobby is to invite to someone else 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 @@ -204,7 +205,8 @@ public: // this will send down all the metadata associated with a lobby // this is an asynchronous call // returns false if the local user is not connected to the Steam servers - // restart are returned by a LobbyDataUpdate_t callback + // results will be returned by a LobbyDataUpdate_t callback + // if the specified lobby doesn't exist, LobbyDataUpdate_t::m_bSuccess will be set to false virtual bool RequestLobbyData( CSteamID steamIDLobby ) = 0; // sets the game server associated with the lobby @@ -530,6 +532,8 @@ struct LobbyDataUpdate_t uint64 m_ulSteamIDLobby; // steamID of the Lobby uint64 m_ulSteamIDMember; // steamID of the member whose data changed, or the room itself + uint8 m_bSuccess; // true if we lobby data was successfully changed; + // will only be false if RequestLobbyData() was called on a lobby that no longer exists }; diff --git a/public/steam/isteamremotestorage.h b/public/steam/isteamremotestorage.h index 813a945..2fcb60d 100644 --- a/public/steam/isteamremotestorage.h +++ b/public/steam/isteamremotestorage.h @@ -12,6 +12,27 @@ #include "isteamclient.h" +// A handle to a piece of user generated content +typedef uint64 UGCHandle_t; +const UGCHandle_t k_UGCHandleInvalid = 0xffffffffffffffffull; + +// Ways to handle a synchronization conflict +enum EResolveConflict +{ + k_EResolveConflictKeepClient = 1, // The local version of each file will be used to overwrite the server version + k_EResolveConflictKeepServer = 2, // The server version of each file will be used to overwrite the local version +}; + +enum ERemoteStoragePlatform +{ + k_ERemoteStoragePlatformNone = 0, + k_ERemoteStoragePlatformWindows = (1 << 0), + k_ERemoteStoragePlatformOSX = (1 << 1 ), + k_ERemoteStoragePlatformPS3 = (1 << 2), + + k_ERemoteStoragePlatformAll = 0xffffffff +}; + //----------------------------------------------------------------------------- // Purpose: Functions for accessing, reading and writing files stored remotely // and cached locally @@ -28,18 +49,130 @@ class ISteamRemoteStorage // file operations virtual bool FileWrite( const char *pchFile, const void *pvData, int32 cubData ) = 0; - virtual int32 GetFileSize( const char *pchFile ) = 0; virtual int32 FileRead( const char *pchFile, void *pvData, int32 cubDataToRead ) = 0; + virtual bool FileForget( const char *pchFile ) = 0; + virtual bool FileDelete( const char *pchFile ) = 0; + virtual SteamAPICall_t FileShare( const char *pchFile ) = 0; + virtual bool SetSyncPlatforms( const char *pchFile, ERemoteStoragePlatform eRemoteStoragePlatform ) = 0; + + // file information virtual bool FileExists( const char *pchFile ) = 0; + virtual bool FilePersisted( const char *pchFile ) = 0; + virtual int32 GetFileSize( const char *pchFile ) = 0; + virtual int64 GetFileTimestamp( const char *pchFile ) = 0; + virtual ERemoteStoragePlatform GetSyncPlatforms( const char *pchFile ) = 0; // iteration virtual int32 GetFileCount() = 0; virtual const char *GetFileNameAndSize( int iFile, int32 *pnFileSizeInBytes ) = 0; - // quota management + // configuration management virtual bool GetQuota( int32 *pnTotalBytes, int32 *puAvailableBytes ) = 0; + virtual bool IsCloudEnabledForAccount() = 0; + virtual bool IsCloudEnabledForApp() = 0; + virtual void SetCloudEnabledForApp( bool bEnabled ) = 0; + + // user generated content + virtual SteamAPICall_t UGCDownload( UGCHandle_t hContent ) = 0; + virtual bool GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID, char **ppchName, int32 *pnFileSizeInBytes, CSteamID *pSteamIDOwner ) = 0; + virtual int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead ) = 0; + + // user generated content iteration + virtual int32 GetCachedUGCCount() = 0; + virtual UGCHandle_t GetCachedUGCHandle( int32 iCachedContent ) = 0; + + // The following functions are only necessary on the Playstation 3. On PC & Mac, the Steam client will handle these operations for you +#if _PS3 + // synchronization + virtual bool SynchronizeToClient() = 0; + virtual bool SynchronizeToServer() = 0; + virtual bool ResolveSyncConflict( EResolveConflict eResolveConflict ) = 0; +#endif }; -#define STEAMREMOTESTORAGE_INTERFACE_VERSION "STEAMREMOTESTORAGE_INTERFACE_VERSION002" +#define STEAMREMOTESTORAGE_INTERFACE_VERSION "STEAMREMOTESTORAGE_INTERFACE_VERSION004" + +// callbacks +#pragma pack( push, 8 ) + +//----------------------------------------------------------------------------- +// Purpose: sent when the local file cache is fully synced with the server for an app +// That means that an application can be started and has all latest files +//----------------------------------------------------------------------------- +struct RemoteStorageAppSyncedClient_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 1 }; + AppId_t m_nAppID; + EResult m_eResult; + int m_unNumDownloads; +}; + +//----------------------------------------------------------------------------- +// Purpose: sent when the server is fully synced with the local file cache for an app +// That means that we can shutdown Steam and our data is stored on the server +//----------------------------------------------------------------------------- +struct RemoteStorageAppSyncedServer_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 2 }; + AppId_t m_nAppID; + EResult m_eResult; + int m_unNumUploads; +}; + +//----------------------------------------------------------------------------- +// Purpose: Status of up and downloads during a sync session +// +//----------------------------------------------------------------------------- +struct RemoteStorageAppSyncProgress_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 3 }; + char m_rgchCurrentFile[260]; // Current file being transferred + AppId_t m_nAppID; // App this info relates to + uint32 m_uBytesTransferredThisChunk; // Bytes transferred this chunk + double m_dAppPercentComplete; // Percent complete that this app's transfers are + bool m_bUploading; // if false, downloading +}; + +// +// IMPORTANT! k_iClientRemoteStorageCallbacks + 4-5 are used, see iclientremotestorage.h +// + +//----------------------------------------------------------------------------- +// Purpose: Sent after a conflict resolution attempt. +//----------------------------------------------------------------------------- +struct RemoteStorageConflictResolution_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 6 }; + AppId_t m_nAppID; + EResult m_eResult; +}; + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to FileShare() +//----------------------------------------------------------------------------- +struct RemoteStorageFileShareResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 7 }; + EResult m_eResult; // The result of the operation + UGCHandle_t m_hFile; // The handle that can be shared with users and features +}; + +//----------------------------------------------------------------------------- +// Purpose: The result of a call to UGCDownload() +//----------------------------------------------------------------------------- +struct RemoteStorageDownloadUGCResult_t +{ + enum { k_iCallback = k_iClientRemoteStorageCallbacks + 8 }; + EResult m_eResult; // The result of the operation. + UGCHandle_t m_hFile; // The handle to the file that was attempted to be downloaded. + AppId_t m_nAppID; // ID of the app that created this file. + int32 m_nSizeInBytes; // The size of the file that was downloaded, in bytes. + char *m_pchFileName; // The name of the file that was downloaded. This pointer is + // not guaranteed to be valid indefinitely. + uint64 m_ulSteamIDOwner; // Steam ID of the user who created this content. +}; + +#pragma pack( pop ) + #endif // ISTEAMREMOTESTORAGE_H diff --git a/public/steam/isteamuser.h b/public/steam/isteamuser.h index 340b46d..8869b44 100644 --- a/public/steam/isteamuser.h +++ b/public/steam/isteamuser.h @@ -132,11 +132,6 @@ public: // (i.e a SteamServersConnected_t has been issued) and may not catch all forms of NAT. virtual bool BIsBehindNAT() = 0; -#ifdef _PS3 - // Logs a user into Steam by using his login name and password - virtual void LogOn( const char *pchUserName, const char *pchPassword ) = 0; -#endif - // set data to be replicated to friends so that they can join your game // CSteamID steamIDGameServer - the steamID of the game server, received from the game server by the client // uint32 unIPServer, uint16 usPortServer - the IP address of the game server @@ -150,6 +145,39 @@ public: // retrieve a finished ticket virtual bool GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) = 0; +#ifdef _PS3 + // Initiates PS3 Logon request using just PSN ticket. + // + // PARAMS: bInteractive - If set tells Steam to go ahead and show the PS3 NetStart dialog if needed to + // prompt the user for network setup/PSN logon before initiating the Steam side of the logon. + // + // Listen for SteamServersConnected_t or SteamServerConnectFailure_t for status. SteamServerConnectFailure_t + // may return with EResult k_EResultPSNAccountUnlinked if the PSN account is unknown to Steam. You should + // then call LogOnAndLinkSteamAccountToPSN() after prompting the user for credentials to establish a link. + // Future calls to LogOn() after the one time link call should succeed as long as the user is connected to PSN. + virtual void LogOn( bool bInteractive ) = 0; + + // Initiates a request to logon with a specific steam username/password and create a PSN account link at + // the same time. Should call this only if LogOn() has failed and indicated the PSN account is unlinked. + // + // PARAMS: bInteractive - If set tells Steam to go ahead and show the PS3 NetStart dialog if needed to + // prompt the user for network setup/PSN logon before initiating the Steam side of the logon. pchUserName + // should be the users Steam username, and pchPassword should be the users Steam password. + // + // Listen for SteamServersConnected_t or SteamServerConnectFailure_t for status. SteamServerConnectFailure_t + // may return with EResult k_EResultPSNAccountAlreadyLinked if already linked to another account. + virtual void LogOnAndLinkSteamAccountToPSN( bool bInteractive, const char *pchUserName, const char *pchPassword ) = 0; + + // Final logon option for PS3, this logs into an existing account if already linked, but if not already linked + // creates a new account using the info in the PSN ticket to generate a unique account name. The new account is + // then linked to the PSN ticket. This is the faster option for new users who don't have an existing Steam account + // to get into multiplayer. + // + // PARAMS: bInteractive - If set tells Steam to go ahead and show the PS3 NetStart dialog if needed to + // prompt the user for network setup/PSN logon before initiating the Steam side of the logon. + virtual void LogOnAndCreateNewSteamAccountIfNeeded( bool bInteractive ) = 0; +#endif + }; #define STEAMUSER_INTERFACE_VERSION "SteamUser014" diff --git a/public/steam/isteamuserstats.h b/public/steam/isteamuserstats.h index 971d197..13d0f9d 100644 --- a/public/steam/isteamuserstats.h +++ b/public/steam/isteamuserstats.h @@ -11,6 +11,7 @@ #endif #include "isteamclient.h" +#include "isteamremotestorage.h" // size limit on stat or achievement name (UTF-8 encoded) enum { k_cchStatNameMax = 128 }; @@ -33,6 +34,7 @@ enum ELeaderboardDataRequest k_ELeaderboardDataRequestGlobal = 0, k_ELeaderboardDataRequestGlobalAroundUser = 1, k_ELeaderboardDataRequestFriends = 2, + k_ELeaderboardDataRequestUsers = 3 }; // the sort order of a leaderboard @@ -68,6 +70,7 @@ struct LeaderboardEntry_t int32 m_nGlobalRank; // [1..N], where N is the number of users with an entry in the leaderboard int32 m_nScore; // score as set in the leaderboard int32 m_cDetails; // number of int32 details available for this entry + UGCHandle_t m_hUGC; // handle for UGC attached to the entry }; #pragma pack( pop ) @@ -173,6 +176,10 @@ public: // e.g. DownloadLeaderboardEntries( hLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -3, 3 ) will return 7 rows, 3 before the user, 3 after // k_ELeaderboardDataRequestFriends requests all the rows for friends of the current user virtual SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd ) = 0; + // as above, but downloads leaderboard entries for an arbitrary set of users - ELeaderboardDataRequest is k_ELeaderboardDataRequestUsers + // if a user doesn't have a leaderboard entry, they won't be included in the result + // a max of 100 users can be downloaded at a time, with only one outstanding call at a time + virtual SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLeaderboard, CSteamID *prgUsers, int cUsers ) = 0; // Returns data about a single leaderboard entry // use a for loop from 0 to LeaderboardScoresDownloaded_t::m_cEntryCount to get all the downloaded entries @@ -196,13 +203,17 @@ public: // 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, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount ) = 0; + // Attaches a piece of user generated content the user's entry on a leaderboard. + // hContent is a handle to a piece of user generated content that was shared using ISteamUserRemoteStorage::FileShare(). + // This call is asynchronous, with the result returned in LeaderboardUGCSet_t. + virtual SteamAPICall_t AttachLeaderboardUGC( SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC ) = 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_VERSION007" +#define STEAMUSERSTATS_INTERFACE_VERSION "STEAMUSERSTATS_INTERFACE_VERSION009" // callbacks #pragma pack( push, 8 ) @@ -296,6 +307,7 @@ struct NumberOfCurrentPlayers_t }; + //----------------------------------------------------------------------------- // Purpose: Callback indicating that a user's stats have been unloaded. // Call RequestUserStats again to access stats for this user @@ -325,6 +337,16 @@ struct UserAchievementIconFetched_t // IMPORTANT! k_iSteamUserStatsCallbacks + 10 is used, see iclientuserstats.h // +//----------------------------------------------------------------------------- +// Purpose: call result indicating UGC has been uploaded, returned as a result of SetLeaderboardUGC() +//----------------------------------------------------------------------------- +struct LeaderboardUGCSet_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 11 }; + EResult m_eResult; // The result of the operation + SteamLeaderboard_t m_hSteamLeaderboard; // the leaderboard handle that was +}; + #pragma pack( pop ) diff --git a/public/steam/isteamutils.h b/public/steam/isteamutils.h index b0d3652..ae928b5 100644 --- a/public/steam/isteamutils.h +++ b/public/steam/isteamutils.h @@ -109,6 +109,7 @@ public: // refresh the screen with Present or SwapBuffers to allow the overlay to do it's work. virtual bool BOverlayNeedsPresent() = 0; +#ifndef _PS3 // Asynchronous call to check if an executable file has been signed using the public key set on the signing tab // of the partner site, for example to refuse to load modified executable files. // The result is returned in CheckFileSignature_t. @@ -118,6 +119,12 @@ public: // k_ECheckFileSignatureInvalidSignature - The file exists, and the signing tab has been set for this file, but the file is either not signed or the signature does not match. // k_ECheckFileSignatureValidSignature - The file is signed and the signature is valid. virtual SteamAPICall_t CheckFileSignature( const char *szFileName ) = 0; +#endif + +#ifdef _PS3 + virtual void PostPS3SysutilCallback( uint64_t status, uint64_t param, void* userdata ) = 0; + virtual bool BIsReadyToShutdown() = 0; +#endif }; #define STEAMUTILS_INTERFACE_VERSION "SteamUtils005" @@ -184,6 +191,25 @@ struct CheckFileSignature_t ECheckFileSignature m_eCheckFileSignature; }; +#ifdef _PS3 +//----------------------------------------------------------------------------- +// callback for NetCtlNetStartDialog finishing on PS3 +//----------------------------------------------------------------------------- +struct NetStartDialogFinished_t +{ + enum { k_iCallback = k_iSteamUtilsCallbacks + 6 }; +}; + +//----------------------------------------------------------------------------- +// callback for NetCtlNetStartDialog unloaded on PS3 +//----------------------------------------------------------------------------- +struct NetStartDialogUnloaded_t +{ + enum { k_iCallback = k_iSteamUtilsCallbacks + 7 }; +}; + +#endif + #pragma pack( pop ) #endif // ISTEAMUTILS_H diff --git a/public/steam/lib/linux32/libsdkencryptedappticket.so b/public/steam/lib/linux32/libsdkencryptedappticket.so index 93a81c3..04f8f38 100644 Binary files a/public/steam/lib/linux32/libsdkencryptedappticket.so and b/public/steam/lib/linux32/libsdkencryptedappticket.so differ diff --git a/public/steam/lib/win32/sdkencryptedappticket.dll b/public/steam/lib/win32/sdkencryptedappticket.dll index f2a4898..b9a320e 100644 Binary files a/public/steam/lib/win32/sdkencryptedappticket.dll and b/public/steam/lib/win32/sdkencryptedappticket.dll differ diff --git a/public/steam/lib/win32/sdkencryptedappticket.lib b/public/steam/lib/win32/sdkencryptedappticket.lib index 3b0a8cd..de42a58 100644 Binary files a/public/steam/lib/win32/sdkencryptedappticket.lib and b/public/steam/lib/win32/sdkencryptedappticket.lib differ diff --git a/public/steam/lib/win64/sdkencryptedappticket64.dll b/public/steam/lib/win64/sdkencryptedappticket64.dll index 975bb75..831767f 100644 Binary files a/public/steam/lib/win64/sdkencryptedappticket64.dll and b/public/steam/lib/win64/sdkencryptedappticket64.dll differ diff --git a/public/steam/lib/win64/sdkencryptedappticket64.lib b/public/steam/lib/win64/sdkencryptedappticket64.lib index 0441f3e..6106b6a 100644 Binary files a/public/steam/lib/win64/sdkencryptedappticket64.lib and b/public/steam/lib/win64/sdkencryptedappticket64.lib differ diff --git a/public/steam/steam_api.h b/public/steam/steam_api.h index 88f598d..f46c6f5 100644 --- a/public/steam/steam_api.h +++ b/public/steam/steam_api.h @@ -80,6 +80,7 @@ S_API void SteamAPI_SetMiniDumpComment( const char *pchMsg ); // pvContext-- can be NULL, will be the void * context passed into m_pfnPreMinidumpCallback // PFNPreMinidumpCallback m_pfnPreMinidumpCallback -- optional callback which occurs just before a .dmp file is written during a crash. Applications can hook this to allow adding additional information into the .dmp comment stream. S_API void SteamAPI_UseBreakpadCrashHandler( char const *pchVersion, char const *pchDate, char const *pchTime, bool bFullMemoryDumps, void *pvContext, PFNPreMinidumpCallback m_pfnPreMinidumpCallback ); +S_API void SteamAPI_SetBreakpadAppID( uint32 unAppID ); // interface pointers, configured by SteamAPI_Init() S_API ISteamClient *SteamClient(); @@ -90,10 +91,35 @@ S_API ISteamClient *SteamClient(); // The following structure must be passed to when loading steam_api_ps3.prx //----------------------------------------------------------------------------------------------------------------------------------------------------------// #define STEAM_PS3_PATH_MAX 1055 +#define STEAM_PS3_SERVICE_ID_MAX 32 struct SteamPS3Params_t { + void *pReserved; + AppId_t m_nAppId; + char m_rgchInstallationPath[ STEAM_PS3_PATH_MAX ]; - char m_rgchSystemCache[ STEAM_PS3_PATH_MAX ]; + char m_rgchSystemCache[ STEAM_PS3_PATH_MAX ]; // temp working cache, not persistent + char m_rgchGameData[ STEAM_PS3_PATH_MAX ]; // persistent game data path for storing user data + char m_rgchNpServiceID[ STEAM_PS3_SERVICE_ID_MAX ]; + + // Should be SYS_TTYP3 through SYS_TTYP10, if it's 0 then Steam won't spawn a + // thread to read console input at all. Using this let's you use Steam console commands + // like: profile_on, profile_off, profile_dump, mem_stats, mem_validate. + unsigned int m_cSteamInputTTY; + + struct Ps3netInit_t + { + bool m_bNeedInit; + bool m_bNeedInitEx; + void *m_pMemory; + int m_nMemorySize; + int m_flags; + } m_sysNetInitInfo; + + struct Ps3jpgInit_t + { + bool m_bNeedInit; + } m_sysJpgInitInfo; }; diff --git a/public/steam/steamclientpublic.h b/public/steam/steamclientpublic.h index 5008304..d6e1444 100644 --- a/public/steam/steamclientpublic.h +++ b/public/steam/steamclientpublic.h @@ -81,6 +81,10 @@ enum EResult k_EResultDiskFull = 54, // Operation canceled - not enough disk space. k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side + k_EResultPSNAccountUnlinked = 57, // Attempt to logon from a PS3 failed because the PSN online id is not linked to a Steam account + k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid + k_EResultPSNAccountAlreadyLinked = 59, // PSN account is already linked to some other account, must explicitly request to replace/delete the link first + k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files }; // Error codes for use with the voice functions @@ -92,6 +96,8 @@ enum EVoiceResult k_EVoiceResultNoData = 3, k_EVoiceResultBufferTooSmall = 4, k_EVoiceResultDataCorrupted = 5, + k_EVoiceResultRestricted = 6, + }; // Result codes to GSHandleClientDeny/Kick @@ -198,6 +204,9 @@ enum ESteamUserStatType k_ESteamUserStatTypeAVGRATE = 3, k_ESteamUserStatTypeACHIEVEMENTS = 4, k_ESteamUserStatTypeGROUPACHIEVEMENTS = 5, + + // max, for sanity checks + k_ESteamUserStatTypeMAX }; @@ -229,6 +238,8 @@ enum EChatRoomEnterResponse k_EChatRoomEnterResponseError = 5, // Unexpected Error k_EChatRoomEnterResponseBanned = 6, // You are banned from this chat room and may not join k_EChatRoomEnterResponseLimited = 7, // Joining this chat is not allowed because you are a limited user (no value on account) + k_EChatRoomEnterResponseClanDisabled = 8, // Attempt to join a clan chat when the clan is locked or disabled + k_EChatRoomEnterResponseCommunityBan = 9, // Attempt to join a chat when the user has a community lock on their account }; @@ -645,21 +656,20 @@ inline bool CSteamID::IsValid() const return true; } - // generic invalid CSteamID -const CSteamID k_steamIDNil; +#define k_steamIDNil CSteamID() // This steamID comes from a user game connection to an out of date GS that hasnt implemented the protocol // to provide its steamID -const CSteamID k_steamIDOutofDateGS( 0, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ); +#define k_steamIDOutofDateGS CSteamID( 0, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ) // This steamID comes from a user game connection to an sv_lan GS -const CSteamID k_steamIDLanModeGS( 0, 0, k_EUniversePublic, k_EAccountTypeInvalid ); +#define k_steamIDLanModeGS CSteamID( 0, 0, k_EUniversePublic, k_EAccountTypeInvalid ) // This steamID can come from a user game connection to a GS that has just booted but hasnt yet even initialized // its steam3 component and started logging on. -const CSteamID k_steamIDNotInitYetGS( 1, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ); +#define k_steamIDNotInitYetGS CSteamID( 1, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ) // This steamID can come from a user game connection to a GS that isn't using the steam authentication system but still // wants to support the "Join Game" option in the friends list -const CSteamID k_steamIDNonSteamGS( 2, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ); +#define k_steamIDNonSteamGS CSteamID( 2, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ) #ifdef STEAM diff --git a/public/steam/steamps3params_internal.h b/public/steam/steamps3params_internal.h new file mode 100644 index 0000000..b4152c1 --- /dev/null +++ b/public/steam/steamps3params_internal.h @@ -0,0 +1,27 @@ +//====== Copyright 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef STEAM_PS3PARAMSINTERNAL_API_H +#define STEAM_PS3PARAMSINTERNAL_API_H +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Purpose: Params used only internally at Valve. This is the reserved pointer of SteamPS3Params_t +//----------------------------------------------------------------------------- +struct SteamPS3ParamsInternal_t +{ + int m_nVersion; + EUniverse m_eUniverse; + const char *m_pchCMForce; // list of CMs in form "IP:port;IP:port" or "IP;IP" +}; + +#define STEAM_PS3_PARAMS_INTERNAL_VERSION 1 + + +#endif // STEAM_PS3PARAMSINTERNAL_API_H diff --git a/redistributable_bin/Steam.dll b/redistributable_bin/Steam.dll index dae9753..28c0d10 100644 Binary files a/redistributable_bin/Steam.dll and b/redistributable_bin/Steam.dll differ diff --git a/redistributable_bin/linux32/libsteam_api.so b/redistributable_bin/linux32/libsteam_api.so index d63ce89..066085a 100644 Binary files a/redistributable_bin/linux32/libsteam_api.so and b/redistributable_bin/linux32/libsteam_api.so differ diff --git a/redistributable_bin/linux32/libtier0_s.so b/redistributable_bin/linux32/libtier0_s.so index a050957..f7e037c 100644 Binary files a/redistributable_bin/linux32/libtier0_s.so and b/redistributable_bin/linux32/libtier0_s.so differ diff --git a/redistributable_bin/linux32/libvstdlib_s.so b/redistributable_bin/linux32/libvstdlib_s.so index 0a00cb8..7395844 100644 Binary files a/redistributable_bin/linux32/libvstdlib_s.so and b/redistributable_bin/linux32/libvstdlib_s.so differ diff --git a/redistributable_bin/linux32/steamclient.so b/redistributable_bin/linux32/steamclient.so index 4db765d..8bd3a92 100644 Binary files a/redistributable_bin/linux32/steamclient.so and b/redistributable_bin/linux32/steamclient.so differ diff --git a/redistributable_bin/osx32/libSteam.dylib b/redistributable_bin/osx32/libSteam.dylib index f68339c..b81fea6 100644 Binary files a/redistributable_bin/osx32/libSteam.dylib and b/redistributable_bin/osx32/libSteam.dylib differ diff --git a/redistributable_bin/osx32/libsteam_api.dylib b/redistributable_bin/osx32/libsteam_api.dylib index ce4de89..fa60d7a 100644 Binary files a/redistributable_bin/osx32/libsteam_api.dylib and b/redistributable_bin/osx32/libsteam_api.dylib differ diff --git a/redistributable_bin/osx32/libtier0_s.dylib b/redistributable_bin/osx32/libtier0_s.dylib index 234f778..5291aa0 100644 Binary files a/redistributable_bin/osx32/libtier0_s.dylib and b/redistributable_bin/osx32/libtier0_s.dylib differ diff --git a/redistributable_bin/osx32/libvstdlib_s.dylib b/redistributable_bin/osx32/libvstdlib_s.dylib index c4c868b..a90f64d 100644 Binary files a/redistributable_bin/osx32/libvstdlib_s.dylib and b/redistributable_bin/osx32/libvstdlib_s.dylib differ diff --git a/redistributable_bin/osx32/steamclient.dylib b/redistributable_bin/osx32/steamclient.dylib index 078315a..1a7404c 100644 Binary files a/redistributable_bin/osx32/steamclient.dylib and b/redistributable_bin/osx32/steamclient.dylib differ diff --git a/redistributable_bin/steam_api.dll b/redistributable_bin/steam_api.dll index cd7bfe8..d82068f 100644 Binary files a/redistributable_bin/steam_api.dll and b/redistributable_bin/steam_api.dll differ diff --git a/redistributable_bin/steam_api.lib b/redistributable_bin/steam_api.lib index 5811af9..51a4cab 100644 Binary files a/redistributable_bin/steam_api.lib and b/redistributable_bin/steam_api.lib differ diff --git a/redistributable_bin/steamclient.dll b/redistributable_bin/steamclient.dll index 5d31b9c..2334d9d 100644 Binary files a/redistributable_bin/steamclient.dll and b/redistributable_bin/steamclient.dll differ diff --git a/redistributable_bin/tier0_s.dll b/redistributable_bin/tier0_s.dll index d6cbab6..5cf39b9 100644 Binary files a/redistributable_bin/tier0_s.dll and b/redistributable_bin/tier0_s.dll differ diff --git a/redistributable_bin/vstdlib_s.dll b/redistributable_bin/vstdlib_s.dll index 4941bfb..3d6e8c4 100644 Binary files a/redistributable_bin/vstdlib_s.dll and b/redistributable_bin/vstdlib_s.dll differ diff --git a/redistributable_bin/win64/steam_api64.dll b/redistributable_bin/win64/steam_api64.dll index 3217466..508d821 100644 Binary files a/redistributable_bin/win64/steam_api64.dll and b/redistributable_bin/win64/steam_api64.dll differ diff --git a/redistributable_bin/win64/steam_api64.lib b/redistributable_bin/win64/steam_api64.lib index 53aa656..d08f2b6 100644 Binary files a/redistributable_bin/win64/steam_api64.lib and b/redistributable_bin/win64/steam_api64.lib differ diff --git a/steamworksexample/connectingmenu.cpp b/steamworksexample/connectingmenu.cpp new file mode 100644 index 0000000..b2bf51f --- /dev/null +++ b/steamworksexample/connectingmenu.cpp @@ -0,0 +1,38 @@ +//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: Class to define the connecting game menu +// +// $NoKeywords: $ +//============================================================================= + + +#include "stdafx.h" +#include "connectingmenu.h" +#include "SpaceWar.h" + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CConnectingMenu::CConnectingMenu( IGameEngine *pGameEngine ) : CBaseMenu( pGameEngine ) +{ + m_bInitialLogonFailed = false; + AddMenuItem( MenuItem_t( "Connecting...", k_EClientConnectingToSteam ) ); +} + +void CConnectingMenu::Reset() +{ + m_bInitialLogonFailed = false; + ClearMenuItems(); + AddMenuItem( MenuItem_t( "Connecting...", k_EClientConnectingToSteam ) ); +} + +void CConnectingMenu::OnConnectFailure() +{ + m_bInitialLogonFailed = true; + + ClearMenuItems(); + + AddMenuItem( MenuItem_t( "Retry Connecting", k_EClientRetrySteamConnection ) ); + AddMenuItem( MenuItem_t( "Link Existing Account", k_EClientLinkSteamAccount ) ); + AddMenuItem( MenuItem_t( "Autocreate Account", k_EClientAutoCreateAccount ) ); +} \ No newline at end of file diff --git a/steamworksexample/connectingmenu.h b/steamworksexample/connectingmenu.h new file mode 100644 index 0000000..be58b4b --- /dev/null +++ b/steamworksexample/connectingmenu.h @@ -0,0 +1,31 @@ +//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: Class to define the main game menu +// +// $NoKeywords: $ +//============================================================================= + +#ifndef CONNECTINGMENU_H +#define CONNECTINGMENU_H + +#include +#include +#include "GameEngine.h" +#include "SpaceWar.h" +#include "BaseMenu.h" +#include "SpaceWarClient.h" + +class CConnectingMenu : public CBaseMenu +{ +public: + // Constructor + CConnectingMenu( IGameEngine *pGameEngine ); + + void Reset(); + void OnConnectFailure(); + +private: + bool m_bInitialLogonFailed; +}; + +#endif // CONNECTINGMENU_H \ No newline at end of file diff --git a/steamworksexample/gameengineosx.h b/steamworksexample/gameengineosx.h new file mode 100644 index 0000000..4b35d42 --- /dev/null +++ b/steamworksexample/gameengineosx.h @@ -0,0 +1,204 @@ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: Main class for the game engine -- osx implementation +// +// $NoKeywords: $ +//============================================================================= + +#ifndef GAMEENGINEOSX_H +#define GAMEENGINEOSX_H + +#include "GameEngine.h" +#include +#include +#include + +class CVoiceContext; + +class CGameEngineGL : public IGameEngine +{ +public: + + // Constructor + CGameEngineGL(); + + // Destructor + ~CGameEngineGL() { Shutdown(); } + + // Check if the game engine is initialized ok and ready for use + bool BReadyForUse() { return m_bEngineReadyForUse; } + + // Check if the engine is shutting down + bool BShuttingDown() { return m_bShuttingDown; } + + // Set the background color + void SetBackgroundColor( short a, short r, short g, short b ); + + // Start a frame, clear(), beginscene(), etc + bool StartFrame(); + + // Finish a frame, endscene(), present(), etc. + void EndFrame(); + + // Shutdown the game engine + void Shutdown(); + + // Pump messages from the OS + void MessagePump(); + + // Accessors for game screen size + int32 GetViewportWidth() { return m_nWindowWidth; } + int32 GetViewportHeight() { return m_nWindowHeight; } + + // Function for drawing text to the screen, dwFormat is a combination of flags like DT_LEFT, TEXTPOS_VCENTER etc... + bool BDrawString( HGAMEFONT hFont, RECT rect, DWORD dwColor, DWORD dwFormat, const char *pchText ); + + // Create a new font returning our internal handle value for it (0 means failure) + HGAMEFONT HCreateFont( int nHeight, int nFontWeight, bool bItalic, char * pchFont ); + + // Create a new texture returning our internal handle value for it (0 means failure) + HGAMETEXTURE HCreateTexture( byte *pRGBAData, uint32 uWidth, uint32 uHeight ); + + // Draw a line, the engine itself will manage batching these (although you can explicitly flush if you need to) + bool BDrawLine( float xPos0, float yPos0, DWORD dwColor0, float xPos1, float yPos1, DWORD dwColor1 ); + + // Flush the line buffer + bool BFlushLineBuffer(); + + // Draw a point, the engine itself will manage batching these (although you can explicitly flush if you need to) + bool BDrawPoint( float xPos, float yPos, DWORD dwColor ); + + // Flush the point buffer + bool BFlushPointBuffer(); + + // Draw a filled quad + bool BDrawFilledQuad( float xPos0, float yPos0, float xPos1, float yPos1, DWORD dwColor ); + + // Draw a textured rectangle + bool BDrawTexturedQuad( float xPos0, float yPos0, float xPos1, float yPos1, + float u0, float v0, float u1, float v1, DWORD dwColor, HGAMETEXTURE hTexture ); + + // Flush any still cached quad buffers + bool BFlushQuadBuffer(); + + // Get the current state of a key + bool BIsKeyDown( DWORD dwVK ); + + // Get the first (in some arbitrary order) key down, if any + bool BGetFirstKeyDown( DWORD *pdwVK ); + + // Get current tick count for the game engine + uint64 GetGameTickCount() { return m_ulGameTickCount; } + + // Get the tick count elapsed since the previous frame + // bugbug - We use this time to compute things like thrust and acceleration in the game, + // so it's important in doesn't jump ahead by large increments... Need a better + // way to handle that. + uint64 GetGameTicksFrameDelta() { return m_ulGameTickCount - m_ulPreviousGameTickCount; } + + // Tell the game engine to update current tick count + void UpdateGameTickCount(); + + // Tell the game engine to sleep for a bit if needed to limit frame rate + bool BSleepForFrameRateLimit( uint32 ulMaxFrameRate ); + + // Check if the game engine hwnd currently has focus (and a working d3d device) + bool BGameEngineHasFocus() { return true; } + + // Voice chat functions + virtual HGAMEVOICECHANNEL HCreateVoiceChannel(); + virtual void DestroyVoiceChannel( HGAMEVOICECHANNEL hChannel ); + virtual bool AddVoiceData( HGAMEVOICECHANNEL hChannel, const uint8 *pVoiceData, uint32 uLength ); + +private: + void AdjustViewport(); + + // Initialize the GL rendering interfaces and default state + bool BInitializeGL(); + + // Initialize the debug font library + bool BInitializeCellDbgFont(); + + bool BInitializeAudio(); + + void RunAudio(); + + void UpdateKey( uint32_t vkKey, int nDown ); + +private: + // Tracks whether the engine is ready for use + bool m_bEngineReadyForUse; + + // Tracks if we are shutting down + bool m_bShuttingDown; + + // Size of the window to display the game in + int32 m_nWindowWidth; + int32 m_nWindowHeight; + + // Current game time in milliseconds + uint64 m_ulGameTickCount; + + // Game time at the start of the previous frame + uint64 m_ulPreviousGameTickCount; + + // White texture used when drawing filled quads + HGAMETEXTURE m_hTextureWhite; + + NSOpenGLView *m_view; + NSWindow *m_window; + + // Pointer to actual data for points + GLfloat *m_rgflPointsData; + GLubyte *m_rgflPointsColorData; + + // How many points are outstanding needing flush + DWORD m_dwPointsToFlush; + + // Pointer to actual data for lines + GLfloat *m_rgflLinesData; + GLubyte *m_rgflLinesColorData; + + + // How many lines are outstanding needing flush + DWORD m_dwLinesToFlush; + + // Pointer to actual data for quads + GLfloat *m_rgflQuadsData; + GLubyte *m_rgflQuadsColorData; + GLfloat *m_rgflQuadsTextureData; + + // How many lines are outstanding needing flush + DWORD m_dwQuadsToFlush; + + // Map of font handles we have given out + HGAMEFONT m_nNextFontHandle; + std::map< HGAMEFONT, void* > m_MapGameFonts; + std::map< std::string, GLString * > m_MapStrings; + + // Map of handles to texture objects + struct TextureData_t + { + uint32 m_uWidth; + uint32 m_uHeight; + GLuint m_uTextureID; + }; + std::map m_MapTextures; + HGAMETEXTURE m_nNextTextureHandle; + + // Last bound texture, used to know when we must flush + HGAMETEXTURE m_hLastTexture; + + // Map of button state, translated to VK for win32. + std::set< DWORD > m_SetKeysDown; + + ALCcontext* m_palContext; + ALCdevice* m_palDevice; + + // Map of voice handles + std::map m_MapVoiceChannel; + uint32 m_unVoiceChannelCount; + +}; + +#endif // GAMEENGINEOSX_H \ No newline at end of file diff --git a/steamworksexample/gameengineosx.mm b/steamworksexample/gameengineosx.mm new file mode 100644 index 0000000..7ebca9a --- /dev/null +++ b/steamworksexample/gameengineosx.mm @@ -0,0 +1,1018 @@ +//========= Copyright 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: Main class for the game engine -- win32 implementation +// +// $NoKeywords: $ +//============================================================================= + +#include "stdafx.h" + +#include +#include +#include +#include +#include +#include + +#include "glstringosx.h" +#include "gameengineosx.h" + +IGameEngine *CreateGameEngineOSX() +{ + static CGameEngineGL* s_pGameEngine = NULL; + + if (!s_pGameEngine) + { + s_pGameEngine = new CGameEngineGL(); + } + + return s_pGameEngine; +} + +inline uint64_t GetTickCount() +{ + timeval time; + gettimeofday(&time, NULL); + return (time.tv_sec * 1000ULL) + (time.tv_usec / 1000ULL); +} + +void OutputDebugString( const char *pchMsg ) +{ + fprintf( stderr, "%s", pchMsg ); +} + +// How big is the vertex buffer for batching lines in total? +#define LINE_BUFFER_TOTAL_SIZE 1000 + +// How big is the vertex buffer for batching points in total? +#define POINT_BUFFER_TOTAL_SIZE 1800 + +// How big is the vertex buffer for batching quads in total? +#define QUAD_BUFFER_TOTAL_SIZE 1000 + +struct Packet_t +{ + uint32 unSize; + void *pData; +}; + +class CVoiceContext +{ +public: + CVoiceContext() + { + alGenBuffers( ARRAYSIZE(m_buffers), m_buffers ); + alGenSources( 1, &m_nSource ); + + alSourcei( m_nSource, AL_LOOPING, AL_FALSE ); + + for (int i = 0; i < ARRAYSIZE(m_buffers); i++ ) + alSourcei( m_nSource, AL_BUFFER, m_buffers[i] ); + + m_nNextFreeBuffer = 0; + } + virtual ~CVoiceContext() + { + // + } + + ALuint m_buffers[4]; + ALuint m_nSource; + size_t m_nNextFreeBuffer; + std::queue m_pending; +}; + +class AutoReleasePool +{ +public: + AutoReleasePool() + { + pool = [[NSAutoreleasePool alloc] init]; + } + ~AutoReleasePool() + { + [pool release]; + } + + NSAutoreleasePool *pool; +}; + + +@interface GLApplication : NSApplication +{ + +} +- (void)sendEvent:(NSEvent *)anEvent; +- (void)windowWillClose:(NSNotification *)notification; +@end + +@implementation GLApplication +- (void)sendEvent:(NSEvent *)anEvent +{ + //fprintf( stderr, "sendEvent: %s\n", [[anEvent description] UTF8String] ); + + [super sendEvent:anEvent]; +} + +- (void)windowWillClose:(NSNotification *)notification +{ + CreateGameEngineOSX()->Shutdown(); +} + +@end + +//----------------------------------------------------------------------------- +// Purpose: Constructor for game engine instance +//----------------------------------------------------------------------------- +CGameEngineGL::CGameEngineGL() +{ + m_bEngineReadyForUse = false; + m_bShuttingDown = false; + m_nWindowWidth = 0; + m_nWindowHeight = 0; + m_ulPreviousGameTickCount = 0; + m_ulGameTickCount = 0; + m_hTextureWhite = 0; + m_nNextFontHandle = 1; + m_nNextTextureHandle = 1; + m_hLastTexture = 0; + + m_rgflPointsData = new GLfloat[ 3*POINT_BUFFER_TOTAL_SIZE ]; + m_rgflPointsColorData = new GLubyte[ 4*POINT_BUFFER_TOTAL_SIZE ]; + m_dwPointsToFlush = 0; + + m_rgflLinesData = new GLfloat[ 6*LINE_BUFFER_TOTAL_SIZE ]; + m_rgflLinesColorData = new GLubyte[ 8*LINE_BUFFER_TOTAL_SIZE ]; + m_dwLinesToFlush = 0; + + m_rgflQuadsData = new GLfloat [ 12*QUAD_BUFFER_TOTAL_SIZE ]; + m_rgflQuadsColorData = new GLubyte[ 16*QUAD_BUFFER_TOTAL_SIZE ]; + m_rgflQuadsTextureData = new GLfloat[ 8*QUAD_BUFFER_TOTAL_SIZE ]; + m_dwQuadsToFlush = 0; + m_unVoiceChannelCount = 0; + + + if( !BInitializeGL() ) + { + OutputDebugString( "!! Initializing GL failed\n" ); + return; + } + + if ( !BInitializeAudio() ) + { + OutputDebugString( "!! Initializing audio failed\n" ); + return; + } + + m_bEngineReadyForUse = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Shutdown the game engine +//----------------------------------------------------------------------------- +void CGameEngineGL::Shutdown() +{ + // Flag that we are shutting down so the frame loop will stop running + m_bShuttingDown = true; + + if ( m_rgflPointsData ) + { + delete[] m_rgflPointsData; + m_rgflPointsData = NULL; + } + + if ( m_rgflPointsColorData ) + { + delete[] m_rgflPointsColorData; + m_rgflPointsColorData = NULL; + } + + if ( m_rgflLinesData ) + { + delete[] m_rgflLinesData; + m_rgflLinesData = NULL; + } + + if ( m_rgflLinesColorData ) + { + delete[] m_rgflLinesColorData; + m_rgflLinesColorData = NULL; + } + + if ( m_rgflQuadsData ) + { + delete[] m_rgflQuadsData; + m_rgflQuadsData = NULL; + } + + if ( m_rgflQuadsColorData ) + { + delete[] m_rgflQuadsColorData; + m_rgflQuadsColorData = NULL; + } + + if ( m_rgflQuadsTextureData ) + { + delete[] m_rgflQuadsTextureData; + m_rgflQuadsTextureData = NULL; + } + + std::map::const_iterator i; + for (i = m_MapStrings.begin(); i != m_MapStrings.end(); ++i) + { + [i->second release]; + } + + m_MapStrings.clear(); + + m_dwLinesToFlush = 0; + m_dwPointsToFlush = 0; + m_dwQuadsToFlush = 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Initialize voice/audio interfaces +//----------------------------------------------------------------------------- +bool CGameEngineGL::BInitializeAudio() +{ + m_palDevice = alcOpenDevice(NULL); + if ( m_palDevice ) + { + m_palContext = alcCreateContext( m_palDevice, NULL ); + alcMakeContextCurrent( m_palContext ); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Initialize the GL rendering interfaces and default state +//----------------------------------------------------------------------------- +bool CGameEngineGL::BInitializeGL() +{ + m_nWindowWidth = 0; + m_nWindowHeight = 0; + + AutoReleasePool pool; + + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType( &psn, kProcessTransformToForegroundApplication ); + SetFrontProcess( &psn ); + + uint32_t mask = NSTitledWindowMask | NSClosableWindowMask| NSResizableWindowMask; + + GLuint attribs[] = + { + NSOpenGLPFAAccelerated, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFANoRecovery, + NSOpenGLPFABackingStore, + NSOpenGLPFAColorSize, 32, + NSOpenGLPFADepthSize, 16, + 0 + }; + + NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) attribs]; + + NSApplication *app = [GLApplication sharedApplication]; + + NSApplicationLoad(); + + m_view = [[NSOpenGLView alloc] initWithFrame:NSMakeRect( 0, 0, 800, 600 ) + pixelFormat:pixelFormat]; + + + m_window = [[NSWindow alloc] initWithContentRect:NSMakeRect( 50, 50, 800, 600 ) + styleMask:mask + backing:NSBackingStoreBuffered + defer:NO]; + + [app setDelegate: app]; + [m_window setDelegate: app]; + + GLint swapInt = 1; + [[m_view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + + [m_window setContentView:m_view]; + [m_view release]; + + [m_window makeKeyAndOrderFront:nil]; + // [m_view setPostsFrameChangedNotifications:YES]; + + // Clear any errors + glGetError(); + + glClearDepth( 1.0f ); + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + glDisable( GL_CULL_FACE ); + glDisable( GL_ALPHA_TEST ); + glDisable( GL_STENCIL_TEST ); + glDisable( GL_SCISSOR_TEST ); + glDisable( GL_LIGHTING ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_FOG ); + + glDepthMask( GL_FALSE ); + + // We always need these two + glEnableClientState( GL_COLOR_ARRAY ); + glEnableClientState( GL_VERTEX_ARRAY ); + + // This we'll enable as needed + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef( 0, 0, 0 ); + + glMatrixMode( GL_TEXTURE ); + glLoadIdentity(); + glTranslatef( 0, 0, 0 ); + + glDepthRange( 0.0f, 1.0f ); + + AdjustViewport(); + + return true; +} + +void CGameEngineGL::AdjustViewport() +{ + NSRect viewBounds = { { 0, 0 }, { m_nWindowWidth, m_nWindowHeight} }; + + if( !NSEqualRects( [m_view bounds], viewBounds ) ) + { + m_nWindowWidth = [m_view bounds].size.width; + m_nWindowHeight = [m_view bounds].size.height; + + // Perspective + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho( 0, m_nWindowWidth, m_nWindowHeight, 0, -1.0f, 1.0f ); + glTranslatef( 0, 0, 0 ); + + // View port has changed as well + glMatrixMode(GL_MODELVIEW); + + glViewport( 0, 0, m_nWindowWidth, m_nWindowHeight ); + glScissor( 0, 0, m_nWindowWidth, m_nWindowHeight ); + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + glFlush(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Updates current tick count for the game engine +//----------------------------------------------------------------------------- +void CGameEngineGL::UpdateGameTickCount() +{ + m_ulPreviousGameTickCount = m_ulGameTickCount; + m_ulGameTickCount = GetTickCount(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Tell the game engine to sleep for a bit if needed to limit frame rate. You must keep +// calling this repeatedly until it returns false. If it returns true it's slept a little, but more +// time may be needed. +//----------------------------------------------------------------------------- +bool CGameEngineGL::BSleepForFrameRateLimit( uint32 ulMaxFrameRate ) +{ + // Frame rate limiting + float flDesiredFrameMilliseconds = 1000.0f/ulMaxFrameRate; + + uint64 ulGameTickCount = GetTickCount(); + + float flMillisecondsElapsed = (float)(ulGameTickCount - 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 > 3.0f ) + { + usleep( 5000 ); + } + else + { + // Just return right away so we busy loop, don't want to sleep too long and go over + } + + return true; + } + else + { + return false; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Set the background color to clear to +//----------------------------------------------------------------------------- +void CGameEngineGL::SetBackgroundColor( short a, short r, short g, short b ) +{ + glClearColor( (float)r/255.0f, (float)g/255.0f, (float)b/255.0f, (float)a/255.0f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Start a new frame +//----------------------------------------------------------------------------- +bool CGameEngineGL::StartFrame() +{ + AdjustViewport(); + + // Pump system callbacks + MessagePump(); + + // We may now be shutting down, check and don't start a frame then + if ( BShuttingDown() ) + return false; + + // Clear the screen for the new frame + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: End the current frame +//----------------------------------------------------------------------------- +void CGameEngineGL::EndFrame() +{ + if ( BShuttingDown() ) + return; + + // Flush point buffer + BFlushPointBuffer(); + + // Flush line buffer + BFlushLineBuffer(); + + // Flush quad buffer + BFlushQuadBuffer(); + + // Swap buffers now that everything is flushed + [[m_view openGLContext] flushBuffer]; + + RunAudio(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Draw a line, the engine internally manages a vertex buffer for batching these +//----------------------------------------------------------------------------- +bool CGameEngineGL::BDrawLine( float xPos0, float yPos0, DWORD dwColor0, float xPos1, float yPos1, DWORD dwColor1 ) +{ + if ( m_bShuttingDown ) + return false; + + + // Check if we are out of room and need to flush the buffer + if ( m_dwLinesToFlush == LINE_BUFFER_TOTAL_SIZE ) + { + BFlushLineBuffer(); + } + + DWORD dwOffset = m_dwLinesToFlush*6; + m_rgflLinesData[dwOffset] = xPos0; + m_rgflLinesData[dwOffset+1] = yPos0; + m_rgflLinesData[dwOffset+2] = 1.0; + m_rgflLinesData[dwOffset+3] = xPos1; + m_rgflLinesData[dwOffset+4] = yPos1; + m_rgflLinesData[dwOffset+5] = 1.0; + + dwOffset = m_dwLinesToFlush*8; + m_rgflLinesColorData[dwOffset] = COLOR_RED( dwColor0 ); + m_rgflLinesColorData[dwOffset+1] = COLOR_GREEN( dwColor0 ); + m_rgflLinesColorData[dwOffset+2] = COLOR_BLUE( dwColor0 ); + m_rgflLinesColorData[dwOffset+3] = COLOR_ALPHA( dwColor0 ); + m_rgflLinesColorData[dwOffset+4] = COLOR_RED( dwColor1 ); + m_rgflLinesColorData[dwOffset+5] = COLOR_GREEN( dwColor1 ); + m_rgflLinesColorData[dwOffset+6] = COLOR_BLUE( dwColor1 ); + m_rgflLinesColorData[dwOffset+7] = COLOR_ALPHA( dwColor1 ); + + ++m_dwLinesToFlush; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Flush batched lines to the screen +//----------------------------------------------------------------------------- +bool CGameEngineGL::BFlushLineBuffer() +{ + if ( !m_rgflLinesColorData || !m_rgflLinesData || m_bShuttingDown ) + return false; + + if ( m_dwLinesToFlush ) + { + glColorPointer( 4, GL_UNSIGNED_BYTE, 0, m_rgflLinesColorData ); + glVertexPointer( 3, GL_FLOAT, 0, m_rgflLinesData ); + glDrawArrays( GL_LINES, 0, m_dwLinesToFlush*2 ); + + m_dwLinesToFlush = 0; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Draw a point, the engine internally manages a vertex buffer for batching these +//----------------------------------------------------------------------------- +bool CGameEngineGL::BDrawPoint( float xPos, float yPos, DWORD dwColor ) +{ + + if ( m_bShuttingDown ) + return false; + + // Check if we are out of room and need to flush the buffer + if ( m_dwPointsToFlush == POINT_BUFFER_TOTAL_SIZE ) + { + BFlushPointBuffer(); + } + + DWORD dwOffset = m_dwPointsToFlush*3; + m_rgflPointsData[dwOffset] = xPos; + m_rgflPointsData[dwOffset+1] = yPos; + m_rgflPointsData[dwOffset+2] = 1.0; + + dwOffset = m_dwPointsToFlush*4; + m_rgflPointsColorData[dwOffset] = COLOR_RED( dwColor ); + m_rgflPointsColorData[dwOffset+1] = COLOR_GREEN( dwColor ); + m_rgflPointsColorData[dwOffset+2] = COLOR_BLUE( dwColor ); + m_rgflPointsColorData[dwOffset+3] = COLOR_ALPHA( dwColor ); + + ++m_dwPointsToFlush; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Flush batched points to the screen +//----------------------------------------------------------------------------- +bool CGameEngineGL::BFlushPointBuffer() +{ + if ( !m_rgflPointsColorData || !m_rgflPointsData || m_bShuttingDown ) + return false; + + if ( m_dwPointsToFlush ) + { + glColorPointer( 4, GL_UNSIGNED_BYTE, 0, m_rgflPointsColorData ); + glVertexPointer( 3, GL_FLOAT, 0, m_rgflPointsData ); + glDrawArrays( GL_POINTS, 0, m_dwPointsToFlush ); + + m_dwPointsToFlush = 0; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Draw a filled quad +//----------------------------------------------------------------------------- +bool CGameEngineGL::BDrawFilledQuad( float xPos0, float yPos0, float xPos1, float yPos1, DWORD dwColor ) +{ + if ( !m_hTextureWhite ) + { + byte *pRGBAData = new byte[ 1 * 1 * 4 ]; + memset( pRGBAData, 255, 1*1*4 ); + m_hTextureWhite = HCreateTexture( pRGBAData, 1, 1 ); + delete[] pRGBAData; + } + + return BDrawTexturedQuad( xPos0, yPos0, xPos1, yPos1, 0.0f, 0.0f, 1.0f, 1.0f, dwColor, m_hTextureWhite ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Draw a textured quad +//----------------------------------------------------------------------------- +bool CGameEngineGL::BDrawTexturedQuad( float xPos0, float yPos0, float xPos1, float yPos1, float u0, float v0, float u1, float v1, DWORD dwColor, HGAMETEXTURE hTexture ) +{ + if ( m_bShuttingDown ) + return false; + + // Find the texture + std::map::iterator iter; + iter = m_MapTextures.find( hTexture ); + if ( iter == m_MapTextures.end() ) + { + OutputDebugString( "BDrawTexturedQuad called with invalid hTexture value\n" ); + return false; + } + + // Check if we are out of room and need to flush the buffer, or if our texture is changing + // then we also need to flush the buffer. + if ( m_dwQuadsToFlush == QUAD_BUFFER_TOTAL_SIZE || m_hLastTexture != hTexture ) + { + BFlushQuadBuffer(); + } + + // Bind the new texture + glBindTexture( GL_TEXTURE_2D, iter->second.m_uTextureID ); + + DWORD dwOffset = m_dwQuadsToFlush*12; + m_rgflQuadsData[dwOffset] = xPos0; + m_rgflQuadsData[dwOffset+1] = yPos0; + m_rgflQuadsData[dwOffset+2] = 1.0; + m_rgflQuadsData[dwOffset+3] = xPos1; + m_rgflQuadsData[dwOffset+4] = yPos0; + m_rgflQuadsData[dwOffset+5] = 1.0; + m_rgflQuadsData[dwOffset+6] = xPos1; + m_rgflQuadsData[dwOffset+7] = yPos1; + m_rgflQuadsData[dwOffset+8] = 1.0; + m_rgflQuadsData[dwOffset+9] = xPos0; + m_rgflQuadsData[dwOffset+10] = yPos1; + m_rgflQuadsData[dwOffset+11] = 1.0; + + dwOffset = m_dwQuadsToFlush*16; + m_rgflQuadsColorData[dwOffset] = COLOR_RED( dwColor ); + m_rgflQuadsColorData[dwOffset+1] = COLOR_GREEN( dwColor ); + m_rgflQuadsColorData[dwOffset+2] = COLOR_BLUE( dwColor ); + m_rgflQuadsColorData[dwOffset+3] = COLOR_ALPHA( dwColor ); + m_rgflQuadsColorData[dwOffset+4] = COLOR_RED( dwColor ); + m_rgflQuadsColorData[dwOffset+5] = COLOR_GREEN( dwColor ); + m_rgflQuadsColorData[dwOffset+6] = COLOR_BLUE( dwColor ); + m_rgflQuadsColorData[dwOffset+7] = COLOR_ALPHA( dwColor ); + m_rgflQuadsColorData[dwOffset+8] = COLOR_RED( dwColor ); + m_rgflQuadsColorData[dwOffset+9] = COLOR_GREEN( dwColor ); + m_rgflQuadsColorData[dwOffset+10] = COLOR_BLUE( dwColor ); + m_rgflQuadsColorData[dwOffset+11] = COLOR_ALPHA( dwColor ); + m_rgflQuadsColorData[dwOffset+12] = COLOR_RED( dwColor ); + m_rgflQuadsColorData[dwOffset+13] = COLOR_GREEN( dwColor ); + m_rgflQuadsColorData[dwOffset+14] = COLOR_BLUE( dwColor ); + m_rgflQuadsColorData[dwOffset+15] = COLOR_ALPHA( dwColor ); + + dwOffset = m_dwQuadsToFlush*8; + m_rgflQuadsTextureData[dwOffset] = u0; + m_rgflQuadsTextureData[dwOffset+1] = v0; + m_rgflQuadsTextureData[dwOffset+2] = u1; + m_rgflQuadsTextureData[dwOffset+3] = v0; + m_rgflQuadsTextureData[dwOffset+4] = u1; + m_rgflQuadsTextureData[dwOffset+5] = v1; + m_rgflQuadsTextureData[dwOffset+6] = u0; + m_rgflQuadsTextureData[dwOffset+7] = v1; + + + ++m_dwQuadsToFlush; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Flush buffered quads +//----------------------------------------------------------------------------- +bool CGameEngineGL::BFlushQuadBuffer() +{ + if ( !m_rgflPointsColorData || !m_rgflPointsData || m_bShuttingDown ) + return false; + + if ( m_dwQuadsToFlush ) + { + glEnable( GL_TEXTURE_2D ); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glColorPointer( 4, GL_UNSIGNED_BYTE, 0, m_rgflQuadsColorData ); + glVertexPointer( 3, GL_FLOAT, 0, m_rgflQuadsData ); + glTexCoordPointer( 2, GL_FLOAT, 0, m_rgflQuadsTextureData ); + glDrawArrays( GL_QUADS, 0, m_dwQuadsToFlush*4 ); + + glDisable( GL_TEXTURE_2D ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + + m_dwQuadsToFlush = 0; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Creates a new texture +//----------------------------------------------------------------------------- +HGAMETEXTURE CGameEngineGL::HCreateTexture( byte *pRGBAData, uint32 uWidth, uint32 uHeight ) +{ + if ( m_bShuttingDown ) + return 0; + + TextureData_t TexData; + TexData.m_uWidth = uWidth; + TexData.m_uHeight = uHeight; + TexData.m_uTextureID = 0; + + glEnable( GL_TEXTURE_2D ); + glGenTextures( 1, &TexData.m_uTextureID ); + glBindTexture( GL_TEXTURE_2D, TexData.m_uTextureID ); + + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0 ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0 ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + + // build our texture mipmaps + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, uWidth, uHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void *)pRGBAData ); + glDisable( GL_TEXTURE_2D ); + + int nHandle = m_nNextTextureHandle; + ++m_nNextTextureHandle; + m_MapTextures[nHandle] = TexData; + + return nHandle; +} + + +//----------------------------------------------------------------------------- +// Purpose: Creates a new font +//----------------------------------------------------------------------------- +HGAMEFONT CGameEngineGL::HCreateFont( int nHeight, int nFontWeight, bool bItalic, char * pchFont ) +{ + AutoReleasePool pool; + + HGAMEFONT hFont = m_nNextFontHandle; + ++m_nNextFontHandle; + + NSString *fontName = [NSString stringWithUTF8String:pchFont]; + + NSFont *font = [NSFont fontWithName: fontName size: nHeight]; + [font retain]; + + m_MapGameFonts[ hFont ] = font; + + return hFont; +} + + +//----------------------------------------------------------------------------- +// Purpose: Draws text to the screen inside the given rectangular region, using the given font +//----------------------------------------------------------------------------- +bool CGameEngineGL::BDrawString( HGAMEFONT hFont, RECT rect, DWORD dwColor, DWORD dwFormat, const char *pchText ) +{ + if ( !hFont ) + { + OutputDebugString( "Someone is calling BDrawString with a null font handle\n" ); + return false; + } + + AutoReleasePool pool; + + NSFont *pFont = (NSFont*) m_MapGameFonts[ hFont ]; + NSRect box = { { rect.left, rect.top }, { rect.right-rect.left, rect.bottom-rect.top } }; + NSColor *pColor = [NSColor colorWithCalibratedRed:COLOR_RED(dwColor)/255.0 + green:COLOR_GREEN(dwColor)/255.0 + blue:COLOR_BLUE(dwColor)/255.0 + alpha:COLOR_ALPHA(dwColor)/255.0 ]; + + GLString *&string = m_MapStrings[ std::string(pchText) ]; + + NSString *nsString = [NSString stringWithUTF8String:pchText]; + + if ( string == NULL) + { + string = [[GLString alloc] initWithString:nsString + withFont:pFont + withTextColor:pColor + inBox:&box + withFlags:dwFormat]; + } + else + { + [string setFont: pFont]; + [string setColor: pColor]; + [string setBox: &box]; + } + + [string drawWithBounds: box]; + + return true; +} + +void CGameEngineGL::UpdateKey( uint32_t vkKey, int nDown ) +{ + if ( nDown ) + m_SetKeysDown.insert( vkKey ); + else + m_SetKeysDown.erase( vkKey ); +} + +//----------------------------------------------------------------------------- +// Purpose: Message pump for OS messages +//----------------------------------------------------------------------------- +void CGameEngineGL::MessagePump() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSApplication *pApp = [NSApplication sharedApplication]; + do + { + NSEvent *event = [pApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]; + if ( event == nil ) + break; + + // fprintf( stderr, ": %s\n", [[event description] UTF8String] ); + + uint32_t c = 0; + switch ( [event type] ) + { + case NSKeyDown: + case NSKeyUp: + c = [[event charactersIgnoringModifiers] characterAtIndex:0]; + + switch ( c ) + { + case NSUpArrowFunctionKey: + c = VK_UP; + break; + case NSDownArrowFunctionKey: + c = VK_DOWN; + break; + case NSLeftArrowFunctionKey: + c = VK_LEFT; + break; + case NSRightArrowFunctionKey: + c = VK_RIGHT; + break; + case 127: // on mac 'del' is backspace + c = VK_BACK; + break; + } + + c = toupper(c); + + if ( [event type] == NSKeyDown ) + m_SetKeysDown.insert( c ); + else + m_SetKeysDown.erase( c ); + continue; + + case NSFlagsChanged: + c = [event modifierFlags]; + UpdateKey( VK_SHIFT, c & NSShiftKeyMask ); + UpdateKey( VK_CONTROL, c & NSControlKeyMask ); + UpdateKey( VK_SELECT, c & NSAlternateKeyMask ); + continue; + + } + + [pApp sendEvent:event]; + [pApp updateWindows]; + } while ( !BShuttingDown() ); + + [pool release]; +} + +//----------------------------------------------------------------------------- +// Purpose: Find out if a key is currently down +//----------------------------------------------------------------------------- +bool CGameEngineGL::BIsKeyDown( DWORD dwVK ) +{ + std::set::iterator iter; + iter = m_SetKeysDown.find( dwVK ); + if ( iter != m_SetKeysDown.end() ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Get a down key value +//----------------------------------------------------------------------------- +bool CGameEngineGL::BGetFirstKeyDown( DWORD *pdwVK ) +{ + std::set::iterator iter; + iter = m_SetKeysDown.begin(); + if ( iter != m_SetKeysDown.end() ) + { + *pdwVK = *iter; + m_SetKeysDown.erase( iter ); + return true; + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HGAMEVOICECHANNEL CGameEngineGL::HCreateVoiceChannel() +{ + m_unVoiceChannelCount++; + CVoiceContext* pVoiceContext = new CVoiceContext; + + m_MapVoiceChannel[m_unVoiceChannelCount] = pVoiceContext; + + return m_unVoiceChannelCount; +} + +void CGameEngineGL::RunAudio() +{ + std::map::iterator iter; + + for( iter = m_MapVoiceChannel.begin(); iter!=m_MapVoiceChannel.end(); ++iter) + { + CVoiceContext* pVoice = iter->second; + + const int nBufferCount = ARRAYSIZE( pVoice->m_buffers ); + ALint nQueued, nProcessed; + alGetSourcei( pVoice->m_nSource, AL_BUFFERS_QUEUED, &nQueued ); + alGetSourcei( pVoice->m_nSource, AL_BUFFERS_PROCESSED, &nProcessed ); + + if ( ( nQueued == nBufferCount ) && ( nProcessed == 0 ) ) + { // No room at the inn + continue; + } + + ALuint nBufferID; + for ( int i = 0; i < nProcessed; i++ ) + alSourceUnqueueBuffers( pVoice->m_nSource, 1, &nBufferID ); + + int nMaxToQueue = nBufferCount - nQueued + nProcessed; + bool bQueued = false; + + while ( nMaxToQueue && !pVoice->m_pending.empty() ) + { + Packet_t &packet = pVoice->m_pending.front(); + + nBufferID = pVoice->m_buffers[ pVoice->m_nNextFreeBuffer ]; + alBufferData( nBufferID, AL_FORMAT_MONO16, packet.pData, packet.unSize, VOICE_OUTPUT_SAMPLE_RATE_IDEAL ); + pVoice->m_nNextFreeBuffer = (pVoice->m_nNextFreeBuffer + 1 ) % nBufferCount; + + alSourceQueueBuffers( pVoice->m_nSource, 1, &nBufferID); + + nMaxToQueue--; + free( packet.pData ); + pVoice->m_pending.pop(); + bQueued = true; + } + + if ( bQueued && ( (nQueued-nProcessed) == 0 ) ) + { + alSourcePlay( pVoice->m_nSource ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameEngineGL::DestroyVoiceChannel( HGAMEVOICECHANNEL hChannel ) +{ + std::map::iterator iter; + iter = m_MapVoiceChannel.find( hChannel ); + if ( iter != m_MapVoiceChannel.end() ) + { + CVoiceContext* pVoiceContext = iter->second; + + // free outstanding voice packets + + while( !pVoiceContext->m_pending.empty() ) + { + free( pVoiceContext->m_pending.front().pData ); + pVoiceContext->m_pending.pop(); + } + + delete pVoiceContext; + m_MapVoiceChannel.erase( iter ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CGameEngineGL::AddVoiceData( HGAMEVOICECHANNEL hChannel, const uint8 *pVoiceData, uint32 uLength ) +{ + std::map::iterator iter; + iter = m_MapVoiceChannel.find( hChannel ); + if ( iter == m_MapVoiceChannel.end() ) + return false; // channel not found + + CVoiceContext* pVoiceContext = iter->second; + + Packet_t packet; + + packet.pData = malloc ( uLength ); + memcpy( packet.pData, pVoiceData, uLength ); + packet.unSize = uLength; + + pVoiceContext->m_pending.push( packet ); + + return true; +} diff --git a/steamworksexample/gameenginewin32.cpp b/steamworksexample/gameenginewin32.cpp index e00ecbb..fd4b326 100644 --- a/steamworksexample/gameenginewin32.cpp +++ b/steamworksexample/gameenginewin32.cpp @@ -9,6 +9,10 @@ #include "GameEngineWin32.h" #include +#ifndef SAFE_RELEASE +#define SAFE_RELEASE( x ) if ( 0 != ( x ) ) { ( x )->Release(); x = 0; } +#endif + // Allocate static member std::map CGameEngineWin32::m_MapEngineInstances; @@ -71,6 +75,46 @@ LRESULT CALLBACK GameWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam return DefWindowProc(hWnd, msg, wParam, lParam); } +class CVoiceContext : public IXAudio2VoiceCallback +{ +public: + CVoiceContext() : m_hBufferEndEvent( CreateEvent( NULL, FALSE, FALSE, NULL ) ) + { + m_pSourceVoice = NULL; + } + virtual ~CVoiceContext() + { + CloseHandle( m_hBufferEndEvent ); + } + + STDMETHOD_( void, OnVoiceProcessingPassStart )( UINT32 ) + { + } + STDMETHOD_( void, OnVoiceProcessingPassEnd )() + { + } + STDMETHOD_( void, OnStreamEnd )() + { + } + STDMETHOD_( void, OnBufferStart )( void* ) + { + } + STDMETHOD_( void, OnBufferEnd )( void* pContext ) + { + free( pContext ); // free the sound buffer + SetEvent( m_hBufferEndEvent ); + } + STDMETHOD_( void, OnLoopEnd )( void* ) + { + } + STDMETHOD_( void, OnVoiceError )( void*, HRESULT ) + { + } + + HANDLE m_hBufferEndEvent; + IXAudio2SourceVoice* m_pSourceVoice; +}; + //----------------------------------------------------------------------------- // Purpose: Constructor for game engine instance @@ -83,6 +127,9 @@ CGameEngineWin32::CGameEngineWin32( HINSTANCE hInstance, int nShowCommand, int32 m_hWnd = NULL; m_pD3D9Interface = NULL; m_pD3D9Device = NULL; + m_pXAudio2 = NULL; + m_pMasteringVoice = NULL; + m_unVoiceChannelCount = 0; // 0 == invalid handle m_nWindowWidth = nWindowWidth; m_nWindowHeight = nWindowHeight; m_nNextFontHandle = 1; @@ -107,12 +154,14 @@ CGameEngineWin32::CGameEngineWin32( HINSTANCE hInstance, int nShowCommand, int32 m_ulGameTickCount = 0; m_dwBackgroundColor = D3DCOLOR_ARGB(0, 255, 255, 255 ); + // for XAudio2 + CoInitializeEx( NULL, COINIT_MULTITHREADED ); + // restrict this main game thread to the first processor, so query performance counter won't jump on crappy AMD cpus DWORD dwThreadAffinityMask = 0x01; ::SetThreadAffinityMask( ::GetCurrentThread(), dwThreadAffinityMask ); // Setup timing data - LARGE_INTEGER l; ::QueryPerformanceFrequency( &l ); m_ulPerfCounterToMillisecondsDivisor = l.QuadPart/1000; @@ -139,8 +188,21 @@ CGameEngineWin32::CGameEngineWin32( HINSTANCE hInstance, int nShowCommand, int32 m_nViewportWidth = r.right-r.left; m_nViewportHeight = r.bottom-r.top; - m_bEngineReadyForUse = true; + // initialzie XAudio2 interface + if( FAILED( XAudio2Create( &m_pXAudio2, 0 ) ) ) + { + ::MessageBoxA( NULL, "Failed to init XAudio2 engine", "SteamworksExample - Fatal error", MB_OK | MB_ICONERROR ); + return; + } + // Create a mastering voice + if( FAILED( m_pXAudio2->CreateMasteringVoice( &m_pMasteringVoice, XAUDIO2_DEFAULT_CHANNELS, VOICE_OUTPUT_SAMPLE_RATE ) ) ) + { + ::MessageBoxA( NULL, "Failed to create mastering voice", "SteamworksExample - Fatal error", MB_OK | MB_ICONERROR ); + return; + } + + m_bEngineReadyForUse = true; } @@ -157,8 +219,7 @@ void CGameEngineWin32::Shutdown() std::map::iterator iter; for( iter = m_MapFontInstances.begin(); iter != m_MapFontInstances.end(); ++iter ) { - iter->second->Release(); - iter->second = NULL; + SAFE_RELEASE( iter->second ); } m_MapFontInstances.clear(); } @@ -170,8 +231,7 @@ void CGameEngineWin32::Shutdown() { if ( iter->second.m_bIsLocked ) iter->second.m_pBuffer->Unlock(); - iter->second.m_pBuffer->Release(); - iter->second.m_pBuffer = NULL; + SAFE_RELEASE( iter->second.m_pBuffer ); } m_MapVertexBuffers.clear(); } @@ -188,26 +248,24 @@ void CGameEngineWin32::Shutdown() } if ( iter->second.m_pTexture ) { - iter->second.m_pTexture->Release(); - iter->second.m_pTexture = NULL; + SAFE_RELEASE( iter->second.m_pTexture ); } } m_MapTextures.clear(); } + // All XAudio2 interfaces are released when the engine is destroyed, but being tidy + if ( m_pMasteringVoice ) + { + m_pMasteringVoice->DestroyVoice(); + m_pMasteringVoice = NULL; + } + // Cleanup D3D - if ( m_pD3D9Device ) - { - m_pD3D9Device->Release(); - m_pD3D9Device = NULL; - } - - if ( m_pD3D9Interface ) - { - m_pD3D9Interface->Release(); - m_pD3D9Interface = NULL; - } - + SAFE_RELEASE( m_pD3D9Device ); + SAFE_RELEASE( m_pD3D9Interface ); + SAFE_RELEASE( m_pXAudio2 ); + // Destroy our window if ( m_hWnd ) { @@ -239,6 +297,8 @@ void CGameEngineWin32::Shutdown() } m_hInstance = NULL; } + + CoUninitialize(); } @@ -1643,3 +1703,104 @@ void CGameEngineWin32::RemoveInstanceFromHWNDMap( HWND hWnd ) m_MapEngineInstances.erase( iter ); } + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HGAMEVOICECHANNEL CGameEngineWin32::HCreateVoiceChannel() +{ + if ( !m_pXAudio2 ) + return 0; + + m_unVoiceChannelCount++; + CVoiceContext* pVoiceContext = new CVoiceContext; + + // The format we sample voice in. + WAVEFORMATEX voicesampleformat = + { + WAVE_FORMAT_PCM, // wFormatTag + 1, // nChannels + VOICE_OUTPUT_SAMPLE_RATE,// nSamplesPerSec + VOICE_OUTPUT_SAMPLE_RATE*BYTES_PER_SAMPLE, // nAvgBytesPerSec + 2, // nBlockAlign + 8*BYTES_PER_SAMPLE, // wBitsPerSample + sizeof(WAVEFORMATEX) // cbSize + }; + + if( FAILED( m_pXAudio2->CreateSourceVoice( &pVoiceContext->m_pSourceVoice, &voicesampleformat , 0, 1.0f, pVoiceContext ) ) ) + { + delete pVoiceContext; + return 0; // failed + } + + pVoiceContext->m_pSourceVoice->Start( 0, 0 ); + + m_MapVoiceChannel[m_unVoiceChannelCount] = pVoiceContext; + + return m_unVoiceChannelCount; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameEngineWin32::DestroyVoiceChannel( HGAMEVOICECHANNEL hChannel ) +{ + std::map::iterator iter; + iter = m_MapVoiceChannel.find( hChannel ); + if ( iter != m_MapVoiceChannel.end() ) + { + CVoiceContext* pVoiceContext = iter->second; + XAUDIO2_VOICE_STATE state; + + for(;;) + { + pVoiceContext->m_pSourceVoice->GetState( &state ); + if( !state.BuffersQueued ) + break; + + WaitForSingleObject( pVoiceContext->m_hBufferEndEvent, INFINITE ); + } + + pVoiceContext->m_pSourceVoice->Stop( 0 ); + pVoiceContext->m_pSourceVoice->DestroyVoice(); + + delete pVoiceContext; + + m_MapVoiceChannel.erase( iter ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CGameEngineWin32::AddVoiceData( HGAMEVOICECHANNEL hChannel, const uint8 *pVoiceData, uint32 uLength ) +{ + std::map::iterator iter; + iter = m_MapVoiceChannel.find( hChannel ); + if ( iter == m_MapVoiceChannel.end() ) + return false; // channel not found + + CVoiceContext* pVoiceContext = iter->second; + + // + // At this point we have a buffer full of audio and enough room to submit it, so + // let's submit it and get another read request going. + + uint8 *pBuffer = (uint8 *) malloc( uLength ); + memcpy( pBuffer, pVoiceData, uLength ); + + XAUDIO2_BUFFER buf = {0}; + buf.AudioBytes = uLength; + buf.pAudioData = pBuffer; + buf.pContext = pBuffer; // the buffer is freed again in CVoiceContext::OnBufferEnd + + if ( FAILED( pVoiceContext->m_pSourceVoice->SubmitSourceBuffer( &buf ) ) ) + { + free ( pBuffer ); + return false; + } + + return true; +} \ No newline at end of file diff --git a/steamworksexample/gameenginewin32.h b/steamworksexample/gameenginewin32.h index faa236f..d475015 100644 --- a/steamworksexample/gameenginewin32.h +++ b/steamworksexample/gameenginewin32.h @@ -67,6 +67,8 @@ struct TexturedQuadVertex_t float u, v; // texture coordinates }; +class CVoiceContext; + // WndProc declaration LRESULT CALLBACK GameWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); @@ -165,6 +167,10 @@ public: // Check if the game engine hwnd currently has focus (and a working d3d device) bool BGameEngineHasFocus() { return ::GetForegroundWindow() == m_hWnd && !m_bDeviceLost; } + // voice chat sound engine + virtual HGAMEVOICECHANNEL HCreateVoiceChannel(); + virtual void DestroyVoiceChannel( HGAMEVOICECHANNEL hChannel ); + virtual bool AddVoiceData( HGAMEVOICECHANNEL hChannel, const uint8 *pVoiceData, uint32 uLength ); // Track the state of keyboard input (these are public, but not part of the public interface) void RecordKeyDown( DWORD dwVK ); @@ -336,6 +342,12 @@ private: // Presentation parameters, saved in case of lost device needing reset D3DPRESENT_PARAMETERS m_d3dpp; + IXAudio2* m_pXAudio2; + IXAudio2MasteringVoice* m_pMasteringVoice; + + // Map of font handles to font objects + std::map m_MapVoiceChannel; + uint32 m_unVoiceChannelCount; }; #endif // GAMEENGINEWIN32_H \ No newline at end of file diff --git a/steamworksexample/glstringosx.h b/steamworksexample/glstringosx.h new file mode 100644 index 0000000..f69c22e --- /dev/null +++ b/steamworksexample/glstringosx.h @@ -0,0 +1,42 @@ +#import +#import +#import +#import +#import + +@interface GLString : NSObject { + CGLContextObj cgl_ctx; // current context at time of texture creation + GLuint texName; + NSSize texSize; + + NSAttributedString * string; + NSFont * font; + NSColor * textColor; // default is opaque white + NSRect border; + uint32_t flags; + + BOOL requiresUpdate; +} + +- (id) initWithString:(NSString *)aString withFont:(NSFont *)inFont withTextColor:(NSColor *)color inBox:(NSRect *)box withFlags:(uint32_t) inFlags; + +- (void) dealloc; + +- (GLuint) texName; // 0 if no texture allocated +- (NSSize) texSize; // actually size of texture generated in texels, (0, 0) if no texture allocated +- (NSColor *) textColor; // get the pre-multiplied default text color (includes alpha) string attributes could override this +- (NSRect) border; // bounds for rect +- (NSFont *)font; +- (uint32_t) flags; // get the pre-multiplied default text color (includes alpha) string attributes could override this + +- (void) setFont:(NSFont *)inFont; +- (void) setColor:(NSColor *)color; +- (void) setBox:(NSRect *)box; +- (void) setFlags:(uint32_t) inFlags; + +- (void) genTexture; // generates the texture without drawing texture to current context +- (void) drawWithBounds:(NSRect)bounds; // will update the texture if required due to change in settings (note context should be setup to be orthographic scaled to per pixel scale) +- (void) drawAtPoint:(NSPoint)point; + +@end + diff --git a/steamworksexample/glstringosx.mm b/steamworksexample/glstringosx.mm new file mode 100644 index 0000000..9b02cd2 --- /dev/null +++ b/steamworksexample/glstringosx.mm @@ -0,0 +1,246 @@ +#include "stdafx.h" +#include "GameEngine.h" + +#import "glstringosx.h" + +// GLString follows + +@implementation GLString + +- (void) deleteTexture +{ + if (texName && cgl_ctx) { + (*cgl_ctx->disp.delete_textures)(cgl_ctx->rend, 1, &texName); + texName = 0; // ensure it is zeroed for failure cases + cgl_ctx = 0; + } +} + +- (void) dealloc +{ + [self deleteTexture]; + [textColor release]; + [string release]; + [super dealloc]; +} + +// designated initializer +- (id) initWithString:(NSString *)aString withFont:(NSFont *) inFont withTextColor:(NSColor *)color inBox:(NSRect *)box withFlags:(uint32_t) inFlags +{ + [super init]; + cgl_ctx = NULL; + texName = 0; + texSize.width = 0.0f; + texSize.height = 0.0f; + + [color retain]; + textColor = color; + + [inFont retain]; + font = inFont; + + flags = inFlags; + + NSMutableDictionary *attribs = [NSMutableDictionary dictionary]; + [attribs setObject: font forKey: NSFontAttributeName]; + [attribs setObject: textColor forKey: NSForegroundColorAttributeName]; + + string = [[NSAttributedString alloc] initWithString:aString attributes:attribs]; + + border = *box; + + requiresUpdate = YES; + return self; +} + +- (void) setFont:(NSFont *)inFont +{ + if ( [font isEqual: inFont] ) + return; + + [string release]; + [font release]; + + [inFont retain]; + font = inFont; + + NSMutableDictionary *attribs = [NSMutableDictionary dictionary]; + [attribs setObject: font forKey: NSFontAttributeName]; + [attribs setObject: textColor forKey: NSForegroundColorAttributeName]; + + string = [[NSAttributedString alloc] initWithString:[string string] attributes:attribs]; + + requiresUpdate = YES; +} + +- (void) setColor:(NSColor *)color +{ + if ( [textColor isEqual:color] ) + return; + + [string release]; + [textColor release]; + + [color retain]; + textColor = color; + + NSMutableDictionary *attribs = [NSMutableDictionary dictionary]; + [attribs setObject: font forKey: NSFontAttributeName]; + [attribs setObject: textColor forKey: NSForegroundColorAttributeName]; + + string = [[NSAttributedString alloc] initWithString:[string string] attributes:attribs]; + + requiresUpdate = YES; +} + +- (void) setBox:(NSRect *)box +{ + if ( NSEqualRects(border, *box ) ) + return; + + border = *box; + requiresUpdate = YES; +} + +- (void) setFlags:(uint32_t) inFlags +{ + if ( inFlags == flags ) + return; + + flags = inFlags; + requiresUpdate = YES; +} + + +- (void) genTexture; // generates the texture without drawing texture to current context +{ + NSImage * image; + NSBitmapImageRep * bitmap; + + NSSize previousSize = texSize; + + image = [[NSImage alloc] initWithSize:border.size]; + + [image lockFocus]; + [[NSGraphicsContext currentContext] setShouldAntialias:YES]; + + [textColor set]; + + float x = 0.0f; + float y = (border.size.height - [string size].height)/2; + + if ( flags & TEXTPOS_CENTER ) + x = (border.size.width - [string size].width)/2; + else if ( flags & TEXTPOS_RIGHT ) + x = border.size.width - [string size].width; + + [string drawAtPoint:NSMakePoint(x, y)]; // draw at offset position + bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, border.size.width, border.size.height)]; + [image unlockFocus]; + texSize.width = [bitmap pixelsWide]; + texSize.height = [bitmap pixelsHigh]; + + if (cgl_ctx = CGLGetCurrentContext ()) + { // if we successfully retrieve a current context (required) + glPushAttrib(GL_TEXTURE_BIT); + if (0 == texName) glGenTextures (1, &texName); + glBindTexture (GL_TEXTURE_RECTANGLE_EXT, texName); + if (NSEqualSizes(previousSize, texSize)) { + glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT,0,0,0,texSize.width,texSize.height,[bitmap hasAlpha] ? GL_RGBA : GL_RGB,GL_UNSIGNED_BYTE,[bitmap bitmapData]); + } else { + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, texSize.width, texSize.height, 0, [bitmap hasAlpha] ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, [bitmap bitmapData]); + } + glPopAttrib(); + } + else + { + NSLog (@"-genTexture: Failure to get current OpenGL context\n"); + + } + [bitmap release]; + [image release]; + + requiresUpdate = NO; +} + +- (GLuint) texName +{ + return texName; +} + +- (NSSize) texSize +{ + return texSize; +} + +- (void) setTextColor:(NSColor *)color // set default text color +{ + [color retain]; + [textColor release]; + textColor = color; + requiresUpdate = YES; +} + +- (NSColor *) textColor +{ + return textColor; +} + +- (NSRect) border +{ + return border; +} + +- (NSFont *)font +{ + return font; +} + +- (uint32_t) flags +{ + return flags; +} + +- (void) drawWithBounds:(NSRect)bounds +{ + if (requiresUpdate) + [self genTexture]; + if (texName) + { + glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT); // GL_COLOR_BUFFER_BIT for glBlendFunc, GL_ENABLE_BIT for glEnable / glDisable + + glDisable (GL_DEPTH_TEST); // ensure text is not remove by depth buffer test. + glEnable (GL_BLEND); // for text fading + glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // ditto + glEnable (GL_TEXTURE_RECTANGLE_EXT); + + glBindTexture (GL_TEXTURE_RECTANGLE_EXT, texName); + glBegin (GL_QUADS); + glTexCoord2f (0.0f, 0.0f); // draw upper left in world coordinates + glVertex2f (bounds.origin.x, bounds.origin.y); + + glTexCoord2f (0.0f, texSize.height); // draw lower left in world coordinates + glVertex2f (bounds.origin.x, bounds.origin.y + bounds.size.height); + + glTexCoord2f (texSize.width, texSize.height); // draw upper right in world coordinates + glVertex2f (bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height); + + glTexCoord2f (texSize.width, 0.0f); // draw lower right in world coordinates + glVertex2f (bounds.origin.x + bounds.size.width, bounds.origin.y); + glEnd (); + + glPopAttrib(); + } +} + +- (void) drawAtPoint:(NSPoint)point +{ + if (requiresUpdate) + [self genTexture]; // ensure size is calculated for bounds + if (texName) // if successful + [self drawWithBounds:NSMakeRect (point.x, point.y, texSize.width, texSize.height)]; +} + +@end diff --git a/steamworksexample/osx/steam_appid.txt b/steamworksexample/osx/steam_appid.txt new file mode 100644 index 0000000..7ad8022 --- /dev/null +++ b/steamworksexample/osx/steam_appid.txt @@ -0,0 +1 @@ +480 \ No newline at end of file diff --git a/steamworksexample/osx/steamworksexample-info.plist b/steamworksexample/osx/steamworksexample-info.plist new file mode 100644 index 0000000..74060fa --- /dev/null +++ b/steamworksexample/osx/steamworksexample-info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDocumentTypes + + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.valvesoftware.steam.steamworksexample + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSMainNibFile + + NSPrincipalClass + + NSServices + + UTExportedTypeDeclarations + + UTImportedTypeDeclarations + + + diff --git a/steamworksexample/remotestoragesync.cpp b/steamworksexample/remotestoragesync.cpp new file mode 100644 index 0000000..637c6b9 --- /dev/null +++ b/steamworksexample/remotestoragesync.cpp @@ -0,0 +1,257 @@ +//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: Class for tracking stats and achievements +// +// $NoKeywords: $ +//============================================================================= + +#include "stdafx.h" +#include "remotestoragesync.h" +#include "BaseMenu.h" + +//----------------------------------------------------------------------------- +// NOTE +// +// The following code is for synchronizing files while the game is running +// This is only necessary when on the Playstation 3, as the Steam client handles +// these operations when on PC/Mac. +// +//----------------------------------------------------------------------------- + +#ifdef _PS3 +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CRemoteStorageSyncMenu::CRemoteStorageSyncMenu( IGameEngine *pGameEngine ) : CBaseMenu( pGameEngine ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Shows the synchronization progress menu +//----------------------------------------------------------------------------- +void CRemoteStorageSyncMenu::ShowSyncProgress( ESyncOperation eSyncOperation, double dPercentComplete ) +{ + if ( eSyncOperation == k_ESyncOperationIdle ) + return; + + PushSelectedItem(); + ClearMenuItems(); + + // Create progress text + char buffer[260]; + const char *pchFormat = (eSyncOperation == k_ESyncOperationToClient ) ? "Syncing to client (%d%%)" : "Syncing to server(%d%%)"; + sprintf( buffer, pchFormat, (int)dPercentComplete ); + AddMenuItem( CRemoteStorageSyncMenu::MenuItem_t( buffer, k_EMenuCommandProgress ) ); + + PopSelectedItem(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Shows the synchronization progress menu +//----------------------------------------------------------------------------- +void CRemoteStorageSyncMenu::ShowSyncComplete( ESyncOperation eSyncOperation, bool bSuccess ) +{ + if ( eSyncOperation == k_ESyncOperationIdle ) + return; + + PushSelectedItem(); + ClearMenuItems(); + + // Create progress text + char buffer[260]; + const char *pchResult = bSuccess ? "complete" : "failed"; + const char *pchFormat = (eSyncOperation == k_ESyncOperationToClient ) ? "Sync to client %s.. press X" : "Sync to server %s.. press X"; + sprintf( buffer, pchFormat, pchResult ); + AddMenuItem( CRemoteStorageSyncMenu::MenuItem_t( buffer, k_EMenuCommandSyncComplete ) ); + + PopSelectedItem(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Shows the conflict menu +//----------------------------------------------------------------------------- +void CRemoteStorageSyncMenu::ShowConflict() +{ + PushSelectedItem(); + ClearMenuItems(); + + // Description + AddMenuItem( CRemoteStorageSyncMenu::MenuItem_t( "There is a conflict between the local & server versions of your files:", k_EMenuCommandNone ) ); + + // options + AddMenuItem( CRemoteStorageSyncMenu::MenuItem_t( "- Keep the local client versions", k_EMenuCommandKeepClient ) ); + AddMenuItem( CRemoteStorageSyncMenu::MenuItem_t( "- Keep the remote server versions", k_EMenuCommandKeepServer ) ); + + PopSelectedItem(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CRemoteStorageSync::CRemoteStorageSync( IGameEngine *pGameEngine ) : + m_eSyncOperation( k_ESyncOperationIdle ), + m_menu( pGameEngine ), + m_callbackAppSyncClientComplete( this, &CRemoteStorageSync::OnAppSyncClientComplete ), + m_callbackAppSyncServerComplete( this, &CRemoteStorageSync::OnAppSyncServerComplete ), + m_callbackAppSyncProgress( this, &CRemoteStorageSync::OnAppSyncProgress ), + m_callbackConflictResolution( this, &CRemoteStorageSync::OnConflictResolution ) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Start synchronizing files to the client +//----------------------------------------------------------------------------- +void CRemoteStorageSync::SynchronizeToClient() +{ + if ( m_eSyncOperation != k_ESyncOperationIdle ) + return; + + if ( !SteamRemoteStorage()->SynchronizeToClient() ) + return; + + m_eSyncOperation = k_ESyncOperationToClient; + m_menu.ShowSyncProgress( m_eSyncOperation, 0.0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Start synchronizing files to the server +//----------------------------------------------------------------------------- +void CRemoteStorageSync::SynchronizeToServer() +{ + if ( m_eSyncOperation != k_ESyncOperationIdle ) + return; + + if ( !SteamRemoteStorage()->SynchronizeToServer() ) + return; + + m_eSyncOperation = k_ESyncOperationToServer; + m_menu.ShowSyncProgress( m_eSyncOperation, 0.0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called each frame. Draw's menu +//----------------------------------------------------------------------------- +void CRemoteStorageSync::Render() +{ + m_menu.RunFrame(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if we are done synchronizing +//----------------------------------------------------------------------------- +bool CRemoteStorageSync::BFinished() +{ + return m_eSyncOperation == k_ESyncOperationIdle; +} + + +//----------------------------------------------------------------------------- +// Purpose: Called periodically by Steam when a synchronization is in progress +//----------------------------------------------------------------------------- +void CRemoteStorageSync::OnAppSyncProgress( RemoteStorageAppSyncProgress_t *pParam ) +{ + // make sure the update is for us + if ( pParam->m_nAppID != SteamUtils()->GetAppID() ) + return; + + // make sure the update is in the direction we expect + if ( m_eSyncOperation == k_ESyncOperationIdle || (pParam->m_bUploading && m_eSyncOperation != k_ESyncOperationToServer) ) + return; + + m_menu.ShowSyncProgress( m_eSyncOperation, pParam->m_dAppPercentComplete ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called by Steam when a synchronization to the client is complete +//----------------------------------------------------------------------------- +void CRemoteStorageSync::OnAppSyncClientComplete( RemoteStorageAppSyncedClient_t *pParam ) +{ + // make sure the update is for us + if ( pParam->m_nAppID != SteamUtils()->GetAppID() ) + return; + + // make sure the update is in the direction we expect + if ( m_eSyncOperation != k_ESyncOperationToClient ) + return; + + // check for errors + if ( pParam->m_eResult == k_EResultRemoteFileConflict ) + { + m_menu.ShowConflict(); + return; + } + + m_menu.ShowSyncComplete( m_eSyncOperation, pParam->m_eResult == k_EResultOK ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called by Steam when a synchronization to the server is complete +//----------------------------------------------------------------------------- +void CRemoteStorageSync::OnAppSyncServerComplete( RemoteStorageAppSyncedServer_t *pParam ) +{ + // make sure the update is for us + if ( pParam->m_nAppID != SteamUtils()->GetAppID() ) + return; + + // make sure the update is in the direction we expect + if ( m_eSyncOperation != k_ESyncOperationToServer ) + return; + + // check for errors + if ( pParam->m_eResult == k_EResultRemoteFileConflict ) + { + m_menu.ShowConflict(); + return; + } + + m_menu.ShowSyncComplete( m_eSyncOperation, pParam->m_eResult == k_EResultOK ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called by Steam when a conflict resolution has completed +//----------------------------------------------------------------------------- +void CRemoteStorageSync::OnConflictResolution( RemoteStorageConflictResolution_t *pParam ) +{ + // make sure the update is for us + if ( pParam->m_nAppID != SteamUtils()->GetAppID() ) + return; + + // check for errors + m_menu.ShowSyncComplete( m_eSyncOperation, pParam->m_eResult == k_EResultOK ); +} + + +//----------------------------------------------------------------------------- +// Purpose: A sync menu item has been selected +//----------------------------------------------------------------------------- +void CRemoteStorageSync::OnMenuSelection( ERemoteStorageSyncMenuCommand selection ) +{ + switch( selection ) + { + case k_EMenuCommandSyncComplete: + m_eSyncOperation = k_ESyncOperationIdle; + break; + + case k_EMenuCommandKeepClient: + m_menu.ShowSyncProgress( m_eSyncOperation, 0.0 ); + SteamRemoteStorage()->ResolveSyncConflict( k_EResolveConflictKeepClient ); + break; + + case k_EMenuCommandKeepServer: + m_menu.ShowSyncProgress( m_eSyncOperation, 0.0 ); + SteamRemoteStorage()->ResolveSyncConflict( k_EResolveConflictKeepServer ); + break; + } +} + +#endif \ No newline at end of file diff --git a/steamworksexample/remotestoragesync.h b/steamworksexample/remotestoragesync.h new file mode 100644 index 0000000..634ae23 --- /dev/null +++ b/steamworksexample/remotestoragesync.h @@ -0,0 +1,87 @@ +//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: Class for tracking stats and achievements +// +// $NoKeywords: $ +//============================================================================= + +#ifndef REMOTE_STORAGE_SYNC_H +#define REMOTE_STORAGE_SYNC_H + +#include "SpaceWar.h" +#include "GameEngine.h" +#include "BaseMenu.h" + +//----------------------------------------------------------------------------- +// NOTE +// +// The following code is for synchronizing files while the game is running +// This is only necessary when on the Playstation 3, as the Steam client handles +// these operations when on PC/Mac. +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: Interface for sync screen +//----------------------------------------------------------------------------- +class IRemoteStorageSync +{ +public: + virtual void SynchronizeToClient() = 0; + virtual void SynchronizeToServer() = 0; + virtual void Render() = 0; + virtual bool BFinished() = 0; + virtual void OnMenuSelection( ERemoteStorageSyncMenuCommand selection ) = 0; +}; + +#ifdef _PS3 +enum ESyncOperation +{ + k_ESyncOperationIdle, + k_ESyncOperationToClient, + k_ESyncOperationToServer +}; + +//----------------------------------------------------------------------------- +// Purpose: Menu shown for synchronization progress and prompting user to resolve a conflict +//----------------------------------------------------------------------------- +class CRemoteStorageSyncMenu : public CBaseMenu< ERemoteStorageSyncMenuCommand > +{ +public: + CRemoteStorageSyncMenu( IGameEngine *pGameEngine ); + void ShowSyncProgress( ESyncOperation eSyncOperation, double dPercentComplete ); + void ShowSyncComplete( ESyncOperation eSyncOperation, bool bSuccess ); + void ShowConflict(); +}; + + +//----------------------------------------------------------------------------- +// Purpose: Synchronizes files with Steam +//----------------------------------------------------------------------------- +class CRemoteStorageSync : public IRemoteStorageSync +{ +public: + + // Constructor + CRemoteStorageSync( IGameEngine *pGameEngine ); + + // IRemoteStorageSync implementations + virtual void SynchronizeToClient(); + virtual void SynchronizeToServer(); + virtual void Render(); + virtual bool BFinished(); + virtual void OnMenuSelection( ERemoteStorageSyncMenuCommand selection ); + +private: + // synchronization updates + STEAM_CALLBACK( CRemoteStorageSync, OnAppSyncClientComplete, RemoteStorageAppSyncedClient_t, m_callbackAppSyncClientComplete ); + STEAM_CALLBACK( CRemoteStorageSync, OnAppSyncServerComplete, RemoteStorageAppSyncedServer_t, m_callbackAppSyncServerComplete ); + STEAM_CALLBACK( CRemoteStorageSync, OnAppSyncProgress, RemoteStorageAppSyncProgress_t, m_callbackAppSyncProgress ); + STEAM_CALLBACK( CRemoteStorageSync, OnConflictResolution, RemoteStorageConflictResolution_t, m_callbackConflictResolution ); + + CRemoteStorageSyncMenu m_menu; + ESyncOperation m_eSyncOperation; +}; +#endif // !_PS3 + +#endif diff --git a/steamworksexample/steamworksexample.xcodeproj/project.pbxproj b/steamworksexample/steamworksexample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..4cd5b8f --- /dev/null +++ b/steamworksexample/steamworksexample.xcodeproj/project.pbxproj @@ -0,0 +1,428 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 503C6CB61268F34200B66E3B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 503C6CB51268F34200B66E3B /* Cocoa.framework */; }; + 503C6D0F1268F49F00B66E3B /* BaseMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CD91268F49F00B66E3B /* BaseMenu.cpp */; }; + 503C6D101268F49F00B66E3B /* connectingmenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CDB1268F49F00B66E3B /* connectingmenu.cpp */; }; + 503C6D121268F49F00B66E3B /* gameengineosx.mm in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CE01268F49F00B66E3B /* gameengineosx.mm */; }; + 503C6D131268F49F00B66E3B /* glstringosx.mm in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CE21268F49F00B66E3B /* glstringosx.mm */; }; + 503C6D141268F49F00B66E3B /* Leaderboards.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CE31268F49F00B66E3B /* Leaderboards.cpp */; }; + 503C6D151268F49F00B66E3B /* Lobby.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CE51268F49F00B66E3B /* Lobby.cpp */; }; + 503C6D161268F49F00B66E3B /* Main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CE71268F49F00B66E3B /* Main.cpp */; }; + 503C6D171268F49F00B66E3B /* MainMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CE81268F49F00B66E3B /* MainMenu.cpp */; }; + 503C6D191268F49F00B66E3B /* p2pauth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CED1268F49F00B66E3B /* p2pauth.cpp */; }; + 503C6D1A1268F49F00B66E3B /* PhotonBeam.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CEF1268F49F00B66E3B /* PhotonBeam.cpp */; }; + 503C6D1B1268F49F00B66E3B /* QuitMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CF11268F49F00B66E3B /* QuitMenu.cpp */; }; + 503C6D1C1268F49F00B66E3B /* RemoteStorage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CF31268F49F00B66E3B /* RemoteStorage.cpp */; }; + 503C6D1D1268F49F00B66E3B /* ServerBrowser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CF51268F49F00B66E3B /* ServerBrowser.cpp */; }; + 503C6D1E1268F49F00B66E3B /* Ship.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CF81268F49F00B66E3B /* Ship.cpp */; }; + 503C6D1F1268F49F00B66E3B /* SpaceWarClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CFB1268F49F00B66E3B /* SpaceWarClient.cpp */; }; + 503C6D201268F49F00B66E3B /* SpaceWarEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6CFD1268F49F00B66E3B /* SpaceWarEntity.cpp */; }; + 503C6D221268F49F00B66E3B /* SpaceWarServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6D011268F49F00B66E3B /* SpaceWarServer.cpp */; }; + 503C6D231268F49F00B66E3B /* StarField.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6D031268F49F00B66E3B /* StarField.cpp */; }; + 503C6D241268F49F00B66E3B /* StatsAndAchievements.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6D051268F49F00B66E3B /* StatsAndAchievements.cpp */; }; + 503C6D251268F49F00B66E3B /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6D071268F49F00B66E3B /* stdafx.cpp */; }; + 503C6D261268F49F00B66E3B /* Sun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6D091268F49F00B66E3B /* Sun.cpp */; }; + 503C6D271268F49F00B66E3B /* VectorEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6D0B1268F49F00B66E3B /* VectorEntity.cpp */; }; + 503C6D281268F49F00B66E3B /* voicechat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503C6D0D1268F49F00B66E3B /* voicechat.cpp */; }; + 503C6DAC1268FE1000B66E3B /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 503C6DAA1268FE1000B66E3B /* OpenAL.framework */; }; + 503C6DAD1268FE1000B66E3B /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 503C6DAB1268FE1000B66E3B /* OpenGL.framework */; }; + 503C6DB41269002800B66E3B /* libsteam_api.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 503C6DB31269002800B66E3B /* libsteam_api.dylib */; }; + 504EDCBC126901EC00F96D63 /* libsteam_api.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 503C6DB31269002800B66E3B /* libsteam_api.dylib */; }; + 504EDCC21269026C00F96D63 /* steam_appid.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 504EDCC01269025A00F96D63 /* steam_appid.txt */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 504EDCBB126901D200F96D63 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 6; + files = ( + 504EDCC21269026C00F96D63 /* steam_appid.txt in CopyFiles */, + 504EDCBC126901EC00F96D63 /* libsteam_api.dylib in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 503C6CB21268F34200B66E3B /* steamworksexample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = steamworksexample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 503C6CB51268F34200B66E3B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 503C6CD91268F49F00B66E3B /* BaseMenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BaseMenu.cpp; sourceTree = ""; }; + 503C6CDA1268F49F00B66E3B /* BaseMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseMenu.h; sourceTree = ""; }; + 503C6CDB1268F49F00B66E3B /* connectingmenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = connectingmenu.cpp; sourceTree = ""; }; + 503C6CDC1268F49F00B66E3B /* connectingmenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = connectingmenu.h; sourceTree = ""; }; + 503C6CDD1268F49F00B66E3B /* GameEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameEngine.h; sourceTree = ""; }; + 503C6CDF1268F49F00B66E3B /* gameengineosx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gameengineosx.h; sourceTree = ""; }; + 503C6CE01268F49F00B66E3B /* gameengineosx.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = gameengineosx.mm; sourceTree = ""; }; + 503C6CE11268F49F00B66E3B /* glstringosx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = glstringosx.h; sourceTree = ""; }; + 503C6CE21268F49F00B66E3B /* glstringosx.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = glstringosx.mm; sourceTree = ""; }; + 503C6CE31268F49F00B66E3B /* Leaderboards.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Leaderboards.cpp; sourceTree = ""; }; + 503C6CE41268F49F00B66E3B /* Leaderboards.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Leaderboards.h; sourceTree = ""; }; + 503C6CE51268F49F00B66E3B /* Lobby.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Lobby.cpp; sourceTree = ""; }; + 503C6CE61268F49F00B66E3B /* Lobby.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Lobby.h; sourceTree = ""; }; + 503C6CE71268F49F00B66E3B /* Main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Main.cpp; sourceTree = ""; }; + 503C6CE81268F49F00B66E3B /* MainMenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MainMenu.cpp; sourceTree = ""; }; + 503C6CE91268F49F00B66E3B /* MainMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainMenu.h; sourceTree = ""; }; + 503C6CEA1268F49F00B66E3B /* Messages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Messages.h; sourceTree = ""; }; + 503C6CED1268F49F00B66E3B /* p2pauth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = p2pauth.cpp; sourceTree = ""; }; + 503C6CEE1268F49F00B66E3B /* p2pauth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = p2pauth.h; sourceTree = ""; }; + 503C6CEF1268F49F00B66E3B /* PhotonBeam.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PhotonBeam.cpp; sourceTree = ""; }; + 503C6CF01268F49F00B66E3B /* PhotonBeam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhotonBeam.h; sourceTree = ""; }; + 503C6CF11268F49F00B66E3B /* QuitMenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = QuitMenu.cpp; sourceTree = ""; }; + 503C6CF21268F49F00B66E3B /* QuitMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QuitMenu.h; sourceTree = ""; }; + 503C6CF31268F49F00B66E3B /* RemoteStorage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RemoteStorage.cpp; sourceTree = ""; }; + 503C6CF41268F49F00B66E3B /* RemoteStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteStorage.h; sourceTree = ""; }; + 503C6CF51268F49F00B66E3B /* ServerBrowser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ServerBrowser.cpp; sourceTree = ""; }; + 503C6CF61268F49F00B66E3B /* ServerBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServerBrowser.h; sourceTree = ""; }; + 503C6CF71268F49F00B66E3B /* ServerBrowserMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServerBrowserMenu.h; sourceTree = ""; }; + 503C6CF81268F49F00B66E3B /* Ship.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Ship.cpp; sourceTree = ""; }; + 503C6CF91268F49F00B66E3B /* Ship.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Ship.h; sourceTree = ""; }; + 503C6CFA1268F49F00B66E3B /* SpaceWar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpaceWar.h; sourceTree = ""; }; + 503C6CFB1268F49F00B66E3B /* SpaceWarClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpaceWarClient.cpp; sourceTree = ""; }; + 503C6CFC1268F49F00B66E3B /* SpaceWarClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpaceWarClient.h; sourceTree = ""; }; + 503C6CFD1268F49F00B66E3B /* SpaceWarEntity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpaceWarEntity.cpp; sourceTree = ""; }; + 503C6CFE1268F49F00B66E3B /* SpaceWarEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpaceWarEntity.h; sourceTree = ""; }; + 503C6CFF1268F49F00B66E3B /* SpaceWarRes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpaceWarRes.h; sourceTree = ""; }; + 503C6D011268F49F00B66E3B /* SpaceWarServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpaceWarServer.cpp; sourceTree = ""; }; + 503C6D021268F49F00B66E3B /* SpaceWarServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpaceWarServer.h; sourceTree = ""; }; + 503C6D031268F49F00B66E3B /* StarField.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StarField.cpp; sourceTree = ""; }; + 503C6D041268F49F00B66E3B /* StarField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StarField.h; sourceTree = ""; }; + 503C6D051268F49F00B66E3B /* StatsAndAchievements.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StatsAndAchievements.cpp; sourceTree = ""; }; + 503C6D061268F49F00B66E3B /* StatsAndAchievements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StatsAndAchievements.h; sourceTree = ""; }; + 503C6D071268F49F00B66E3B /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = ""; }; + 503C6D081268F49F00B66E3B /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = ""; }; + 503C6D091268F49F00B66E3B /* Sun.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sun.cpp; sourceTree = ""; }; + 503C6D0A1268F49F00B66E3B /* Sun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sun.h; sourceTree = ""; }; + 503C6D0B1268F49F00B66E3B /* VectorEntity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VectorEntity.cpp; sourceTree = ""; }; + 503C6D0C1268F49F00B66E3B /* VectorEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VectorEntity.h; sourceTree = ""; }; + 503C6D0D1268F49F00B66E3B /* voicechat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = voicechat.cpp; sourceTree = ""; }; + 503C6D0E1268F49F00B66E3B /* voicechat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = voicechat.h; sourceTree = ""; }; + 503C6DAA1268FE1000B66E3B /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; }; + 503C6DAB1268FE1000B66E3B /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; + 503C6DB31269002800B66E3B /* libsteam_api.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsteam_api.dylib; path = /Users/joer/Source/Steam3/main/src/lib/osx32/release/libsteam_api.dylib; sourceTree = ""; }; + 504EDCB4126900D600F96D63 /* steamworksexample-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "steamworksexample-Info.plist"; path = "osx/steamworksexample-Info.plist"; sourceTree = ""; }; + 504EDCC01269025A00F96D63 /* steam_appid.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = steam_appid.txt; path = osx/steam_appid.txt; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 503C6CAF1268F34200B66E3B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 503C6DB41269002800B66E3B /* libsteam_api.dylib in Frameworks */, + 503C6DAC1268FE1000B66E3B /* OpenAL.framework in Frameworks */, + 503C6DAD1268FE1000B66E3B /* OpenGL.framework in Frameworks */, + 503C6CB61268F34200B66E3B /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 503C6CA31268F34200B66E3B = { + isa = PBXGroup; + children = ( + 503C6DAA1268FE1000B66E3B /* OpenAL.framework */, + 503C6DAB1268FE1000B66E3B /* OpenGL.framework */, + 503C6D291268F4EE00B66E3B /* Headers */, + 503C6CAB1268F34200B66E3B /* Source */, + 503C6CAC1268F34200B66E3B /* Resources */, + 503C6CAD1268F34200B66E3B /* Frameworks */, + 503C6CB31268F34200B66E3B /* Products */, + ); + sourceTree = ""; + }; + 503C6CAB1268F34200B66E3B /* Source */ = { + isa = PBXGroup; + children = ( + 503C6CED1268F49F00B66E3B /* p2pauth.cpp */, + 503C6CEF1268F49F00B66E3B /* PhotonBeam.cpp */, + 503C6CF11268F49F00B66E3B /* QuitMenu.cpp */, + 503C6CF31268F49F00B66E3B /* RemoteStorage.cpp */, + 503C6CF51268F49F00B66E3B /* ServerBrowser.cpp */, + 503C6CF81268F49F00B66E3B /* Ship.cpp */, + 503C6CFB1268F49F00B66E3B /* SpaceWarClient.cpp */, + 503C6CFD1268F49F00B66E3B /* SpaceWarEntity.cpp */, + 503C6D011268F49F00B66E3B /* SpaceWarServer.cpp */, + 503C6D031268F49F00B66E3B /* StarField.cpp */, + 503C6D051268F49F00B66E3B /* StatsAndAchievements.cpp */, + 503C6D071268F49F00B66E3B /* stdafx.cpp */, + 503C6D091268F49F00B66E3B /* Sun.cpp */, + 503C6D0B1268F49F00B66E3B /* VectorEntity.cpp */, + 503C6D0D1268F49F00B66E3B /* voicechat.cpp */, + 503C6CD91268F49F00B66E3B /* BaseMenu.cpp */, + 503C6CDB1268F49F00B66E3B /* connectingmenu.cpp */, + 503C6CE01268F49F00B66E3B /* gameengineosx.mm */, + 503C6CE21268F49F00B66E3B /* glstringosx.mm */, + 503C6CE31268F49F00B66E3B /* Leaderboards.cpp */, + 503C6CE51268F49F00B66E3B /* Lobby.cpp */, + 503C6CE71268F49F00B66E3B /* Main.cpp */, + 503C6CE81268F49F00B66E3B /* MainMenu.cpp */, + ); + name = Source; + sourceTree = ""; + }; + 503C6CAC1268F34200B66E3B /* Resources */ = { + isa = PBXGroup; + children = ( + 504EDCC01269025A00F96D63 /* steam_appid.txt */, + 504EDCB4126900D600F96D63 /* steamworksexample-Info.plist */, + ); + name = Resources; + sourceTree = ""; + }; + 503C6CAD1268F34200B66E3B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 503C6DB31269002800B66E3B /* libsteam_api.dylib */, + 503C6CB51268F34200B66E3B /* Cocoa.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 503C6CB31268F34200B66E3B /* Products */ = { + isa = PBXGroup; + children = ( + 503C6CB21268F34200B66E3B /* steamworksexample.app */, + ); + name = Products; + sourceTree = ""; + }; + 503C6D291268F4EE00B66E3B /* Headers */ = { + isa = PBXGroup; + children = ( + 503C6CEE1268F49F00B66E3B /* p2pauth.h */, + 503C6CF01268F49F00B66E3B /* PhotonBeam.h */, + 503C6CF21268F49F00B66E3B /* QuitMenu.h */, + 503C6CF41268F49F00B66E3B /* RemoteStorage.h */, + 503C6CF61268F49F00B66E3B /* ServerBrowser.h */, + 503C6CF71268F49F00B66E3B /* ServerBrowserMenu.h */, + 503C6CF91268F49F00B66E3B /* Ship.h */, + 503C6CFA1268F49F00B66E3B /* SpaceWar.h */, + 503C6CFC1268F49F00B66E3B /* SpaceWarClient.h */, + 503C6CFE1268F49F00B66E3B /* SpaceWarEntity.h */, + 503C6CFF1268F49F00B66E3B /* SpaceWarRes.h */, + 503C6D021268F49F00B66E3B /* SpaceWarServer.h */, + 503C6D041268F49F00B66E3B /* StarField.h */, + 503C6D061268F49F00B66E3B /* StatsAndAchievements.h */, + 503C6D081268F49F00B66E3B /* stdafx.h */, + 503C6D0A1268F49F00B66E3B /* Sun.h */, + 503C6D0C1268F49F00B66E3B /* VectorEntity.h */, + 503C6D0E1268F49F00B66E3B /* voicechat.h */, + 503C6CDA1268F49F00B66E3B /* BaseMenu.h */, + 503C6CDC1268F49F00B66E3B /* connectingmenu.h */, + 503C6CDD1268F49F00B66E3B /* GameEngine.h */, + 503C6CDF1268F49F00B66E3B /* gameengineosx.h */, + 503C6CE11268F49F00B66E3B /* glstringosx.h */, + 503C6CE41268F49F00B66E3B /* Leaderboards.h */, + 503C6CE61268F49F00B66E3B /* Lobby.h */, + 503C6CE91268F49F00B66E3B /* MainMenu.h */, + 503C6CEA1268F49F00B66E3B /* Messages.h */, + ); + name = Headers; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 503C6CB11268F34200B66E3B /* steamworksexample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 503C6CC91268F34200B66E3B /* Build configuration list for PBXNativeTarget "steamworksexample" */; + buildPhases = ( + 503C6CAE1268F34200B66E3B /* Sources */, + 503C6CAF1268F34200B66E3B /* Frameworks */, + 503C6CB01268F34200B66E3B /* Resources */, + 504EDCBB126901D200F96D63 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = steamworksexample; + productName = steamworksexample; + productReference = 503C6CB21268F34200B66E3B /* steamworksexample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 503C6CA51268F34200B66E3B /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 503C6CA81268F34200B66E3B /* Build configuration list for PBXProject "steamworksexample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 503C6CA31268F34200B66E3B; + productRefGroup = 503C6CB31268F34200B66E3B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 503C6CB11268F34200B66E3B /* steamworksexample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 503C6CB01268F34200B66E3B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 503C6CAE1268F34200B66E3B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 503C6D0F1268F49F00B66E3B /* BaseMenu.cpp in Sources */, + 503C6D101268F49F00B66E3B /* connectingmenu.cpp in Sources */, + 503C6D121268F49F00B66E3B /* gameengineosx.mm in Sources */, + 503C6D131268F49F00B66E3B /* glstringosx.mm in Sources */, + 503C6D141268F49F00B66E3B /* Leaderboards.cpp in Sources */, + 503C6D151268F49F00B66E3B /* Lobby.cpp in Sources */, + 503C6D161268F49F00B66E3B /* Main.cpp in Sources */, + 503C6D171268F49F00B66E3B /* MainMenu.cpp in Sources */, + 503C6D191268F49F00B66E3B /* p2pauth.cpp in Sources */, + 503C6D1A1268F49F00B66E3B /* PhotonBeam.cpp in Sources */, + 503C6D1B1268F49F00B66E3B /* QuitMenu.cpp in Sources */, + 503C6D1C1268F49F00B66E3B /* RemoteStorage.cpp in Sources */, + 503C6D1D1268F49F00B66E3B /* ServerBrowser.cpp in Sources */, + 503C6D1E1268F49F00B66E3B /* Ship.cpp in Sources */, + 503C6D1F1268F49F00B66E3B /* SpaceWarClient.cpp in Sources */, + 503C6D201268F49F00B66E3B /* SpaceWarEntity.cpp in Sources */, + 503C6D221268F49F00B66E3B /* SpaceWarServer.cpp in Sources */, + 503C6D231268F49F00B66E3B /* StarField.cpp in Sources */, + 503C6D241268F49F00B66E3B /* StatsAndAchievements.cpp in Sources */, + 503C6D251268F49F00B66E3B /* stdafx.cpp in Sources */, + 503C6D261268F49F00B66E3B /* Sun.cpp in Sources */, + 503C6D271268F49F00B66E3B /* VectorEntity.cpp in Sources */, + 503C6D281268F49F00B66E3B /* voicechat.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 503C6CC71268F34200B66E3B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = ( + POSIX, + OSX, + ); + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.5; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = macosx10.5; + USER_HEADER_SEARCH_PATHS = ../public; + }; + name = Debug; + }; + 503C6CC81268F34200B66E3B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = ( + POSIX, + OSX, + ); + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.5; + PREBINDING = NO; + SDKROOT = macosx10.5; + USER_HEADER_SEARCH_PATHS = ../public; + }; + name = Release; + }; + 503C6CCA1268F34200B66E3B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + INFOPLIST_FILE = "osx/steamworksexample-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + LIBRARY_SEARCH_PATHS = ( + ../lib/osx32/release, + "\"$(SRCROOT)/../lib/osx32/release\"", + ../redistributable_bin/osx32/, + ); + PRODUCT_NAME = steamworksexample; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 503C6CCB1268F34200B66E3B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = "osx/steamworksexample-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + LIBRARY_SEARCH_PATHS = ( + ../lib/osx32/debug, + "\"$(SRCROOT)/../lib/osx32/release\"", + ../redistributable_bin/osx32/, + ); + PRODUCT_NAME = steamworksexample; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 503C6CA81268F34200B66E3B /* Build configuration list for PBXProject "steamworksexample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 503C6CC71268F34200B66E3B /* Debug */, + 503C6CC81268F34200B66E3B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 503C6CC91268F34200B66E3B /* Build configuration list for PBXNativeTarget "steamworksexample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 503C6CCA1268F34200B66E3B /* Debug */, + 503C6CCB1268F34200B66E3B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 503C6CA51268F34200B66E3B /* Project object */; +} diff --git a/steamworksexample/voicechat.cpp b/steamworksexample/voicechat.cpp new file mode 100644 index 0000000..a666cdd --- /dev/null +++ b/steamworksexample/voicechat.cpp @@ -0,0 +1,355 @@ +//========= Copyright © 1996-2010, Valve LLC, All rights reserved. ============ +// +// Purpose:Class for P2P voice chat +// +// $NoKeywords: $ +//============================================================================= + +#include "stdafx.h" +#include "voicechat.h" + + +CVoiceChat::CVoiceChat( IGameEngine *pGameEngine ) : + m_CallbackP2PSessionRequest( this, &CVoiceChat::OnP2PSessionRequest ), + m_CallbackP2PSessionConnectFail( this, &CVoiceChat::OnP2PSessionConnectFail ) +{ + m_pGameEngine = pGameEngine; + m_bIsActive = false; + m_ulLastTimeTalked = 0; + m_hVoiceLoopback = 0; +} + + +CVoiceChat::~CVoiceChat() +{ + m_pGameEngine = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: another user has sent us a packet - do we accept? +//----------------------------------------------------------------------------- +void CVoiceChat::OnP2PSessionRequest( P2PSessionRequest_t *pP2PSessionRequest ) +{ + std::map< uint64, VoiceChatConnection_t >::iterator iter; + iter = m_MapConnections.find( pP2PSessionRequest->m_steamIDRemote.ConvertToUint64() ); + if ( iter != m_MapConnections.end() ) + { + // we play with this user + iter->second.eLastError = k_EP2PSessionErrorNone; // our connection is now working + + // the packet itself will come through when you call SteamNetworking()->ReadP2PPacket() + SteamNetworking()->AcceptP2PSessionWithUser( pP2PSessionRequest->m_steamIDRemote ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: we send a packet to another user but it failed +//----------------------------------------------------------------------------- +void CVoiceChat::OnP2PSessionConnectFail( P2PSessionConnectFail_t *pP2PSessionConnectFail ) +{ + std::map< uint64, VoiceChatConnection_t >::iterator iter; + iter = m_MapConnections.find( pP2PSessionConnectFail->m_steamIDRemote.ConvertToUint64() ); + if ( iter != m_MapConnections.end() ) + { + // connection to that user failed + iter->second.eLastError = (EP2PSessionError) pP2PSessionConnectFail->m_eP2PSessionError; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVoiceChat::RunFrame() +{ + if ( m_bIsActive ) + { + // read local microphone input + uint32 nBytesAvalaible = 0; + EVoiceResult res = SteamUser()->GetAvailableVoice( &nBytesAvalaible, NULL ); + + if ( res == k_EVoiceResultOK && nBytesAvalaible > 0 ) + { + uint32 nBytesWritten = 0; + MsgVoiceChatData_t msg; + + // don't send more then 1 KB at a time + uint8 buffer[ 1024+sizeof(msg) ]; + + res = SteamUser()->GetVoice( true, buffer+sizeof(msg), 1024, &nBytesWritten, false, NULL, 0, NULL ); + + if ( res == k_EVoiceResultOK && nBytesWritten > 0 ) + { + // assemble message + msg.SetDataLength( nBytesWritten ); + memcpy( buffer, &msg, sizeof(msg) ); + + SendMessageToAll( buffer, sizeof(msg)+nBytesWritten ); + + m_ulLastTimeTalked = m_pGameEngine->GetGameTickCount(); + + // if local voice loopback is enabled, play it back now + if ( m_hVoiceLoopback != 0 ) + { + // Uncompress the voice data, buffer holds up to 1 second of data + uint32 numUncompressedBytes = 0; + const uint8* pVoiceData = (const uint8*) buffer; + pVoiceData += sizeof(MsgVoiceChatData_t); + + EVoiceResult res = SteamUser()->DecompressVoice( pVoiceData , nBytesWritten, + m_ubUncompressedVoice, sizeof( m_ubUncompressedVoice ), &numUncompressedBytes ); + + if ( res == k_EVoiceResultOK && numUncompressedBytes > 0 ) + { + m_pGameEngine->AddVoiceData( m_hVoiceLoopback, m_ubUncompressedVoice, numUncompressedBytes ); + } + } + } + } + + CheckConnections(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CVoiceChat::HandleMessage( CSteamID fromSteamID, EMessage eMsg, const void *pMessage ) +{ + if ( eMsg != k_EMsgVoiceChatPing && eMsg != k_EMsgVoiceChatData ) + return false; // we don't handle these messages + + std::map< uint64, VoiceChatConnection_t >::iterator iter; + iter = m_MapConnections.find( fromSteamID.ConvertToUint64() ); + if ( iter != m_MapConnections.end() ) + { + VoiceChatConnection_t &chatClient = iter->second; + chatClient.eLastError = k_EP2PSessionErrorNone; // our connection is working + + if ( eMsg == k_EMsgVoiceChatData ) + { + const MsgVoiceChatData_t *pMsgVoiceData = (const MsgVoiceChatData_t *) pMessage; + chatClient.ulLastReceiveVoiceTime = m_pGameEngine->GetGameTickCount(); + + // Uncompress the voice data, buffer holds up to 1 second of data + uint8 pbUncompressedVoice[ VOICE_OUTPUT_SAMPLE_RATE * BYTES_PER_SAMPLE ]; + uint32 numUncompressedBytes = 0; + const uint8* pVoiceData = (const uint8*) pMessage; + pVoiceData += sizeof(MsgVoiceChatData_t); + + EVoiceResult res = SteamUser()->DecompressVoice( pVoiceData , pMsgVoiceData->GetDataLength(), + pbUncompressedVoice, sizeof( pbUncompressedVoice ), &numUncompressedBytes ); + + if ( res == k_EVoiceResultOK && numUncompressedBytes > 0 ) + { + // play it again Sam + if ( chatClient.hVoiceChannel == 0 ) + { + chatClient.hVoiceChannel = m_pGameEngine->HCreateVoiceChannel(); + } + + m_pGameEngine->AddVoiceData( chatClient.hVoiceChannel, pbUncompressedVoice, numUncompressedBytes ); + } + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVoiceChat::MarkAllPlayersInactive() +{ + std::map< uint64, VoiceChatConnection_t >::iterator iter; + for( iter = m_MapConnections.begin(); iter != m_MapConnections.end(); ++iter ) + { + iter->second.bActive = false; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVoiceChat::MarkPlayerAsActive( CSteamID steamID ) +{ + if ( !m_bIsActive ) + return; + + if ( m_SteamIDLocalUser == steamID ) + return; + + std::map< uint64, VoiceChatConnection_t >::iterator iter; + iter = m_MapConnections.find( steamID.ConvertToUint64() ); + if ( iter != m_MapConnections.end() ) + { + // player already has a session object, no new object created + iter->second.bActive = true; + return; + } + + /*char szText[100]; + _snprintf(szText, 100, "CVoiceChat::AddPlayerToSession: %s.\n", SteamFriends()->GetFriendPersonaName( steamID ) ); + OutputDebugString( szText ); */ + + VoiceChatConnection_t session; + session.eLastError = k_EP2PSessionErrorTimeout; + session.ulLastReceiveVoiceTime = 0; + session.ulLastSentTime = 0; + session.hVoiceChannel = 0; + session.bActive = true; + + m_MapConnections[ steamID.ConvertToUint64() ] = session; + + return; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CVoiceChat::IsPlayerTalking( CSteamID steamID ) +{ + + if ( steamID == m_SteamIDLocalUser ) + { + // thats ourself + if ( m_ulLastTimeTalked + 250 > m_pGameEngine->GetGameTickCount() ) + return true; + } + else + { + std::map< uint64, VoiceChatConnection_t >::iterator iter; + iter = m_MapConnections.find( steamID.ConvertToUint64() ); + if ( iter != m_MapConnections.end() ) + { + if ( iter->second.eLastError == k_EP2PSessionErrorNone ) + { + if ( (iter->second.ulLastReceiveVoiceTime + 250) > m_pGameEngine->GetGameTickCount() ) + { + // user talked less then 250msec ago, assume still active + return true; + } + } + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CVoiceChat::StartVoiceChat() +{ + if ( !m_bIsActive ) + { + m_SteamIDLocalUser = SteamUser()->GetSteamID(); + + SteamUser()->StartVoiceRecording(); + + m_bIsActive = true; + + // here you can enable optional local voice loopback: + // m_hVoiceLoopback = m_pGameEngine->HCreateVoiceChannel(); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVoiceChat::StopVoiceChat() +{ + if ( m_bIsActive ) + { + std::map< uint64, VoiceChatConnection_t >::iterator iter; + for( iter = m_MapConnections.begin(); iter != m_MapConnections.end(); ++iter ) + { + CSteamID steamID( iter->first ); + m_pGameEngine->DestroyVoiceChannel( iter->second.hVoiceChannel ); + SteamNetworking()->CloseP2PSessionWithUser( steamID ); + } + + m_MapConnections.clear(); + + if ( m_hVoiceLoopback ) + { + m_pGameEngine->DestroyVoiceChannel( m_hVoiceLoopback ); + m_hVoiceLoopback = 0; + } + + SteamUser()->StopVoiceRecording(); + + m_bIsActive = false; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVoiceChat::CheckConnections() +{ + const uint64 ulTimeNow = m_pGameEngine->GetGameTickCount(); + const uint64 ulTimeoutLimit = 10 * 1000; // 10 seconds + // + std::map< uint64, VoiceChatConnection_t >::iterator iter; + for( iter = m_MapConnections.begin(); iter != m_MapConnections.end(); ++iter ) + { + if ( iter->second.bActive ) + { + const EP2PSessionError eLastError = iter->second.eLastError; + CSteamID steamID( iter->first ); + + if ( eLastError == k_EP2PSessionErrorNone || eLastError == k_EP2PSessionErrorTimeout ) + { + if ( iter->second.ulLastSentTime + ulTimeoutLimit < ulTimeNow ) + { + // time for a ping + MsgVoiceChatPing_t msg; + SteamNetworking()->SendP2PPacket( steamID, &msg, sizeof(msg), k_EP2PSendUnreliable ); + iter->second.ulLastSentTime = ulTimeNow; + } + } + } + else + { + // found inactive connection, destroy + CSteamID steamID( iter->first ); + SteamNetworking()->CloseP2PSessionWithUser( steamID ); + m_pGameEngine->DestroyVoiceChannel( iter->second.hVoiceChannel ); + + m_MapConnections.erase( iter ); + break; // break since the iterator became bad + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVoiceChat::SendMessageToAll( const void *pubData, uint32 cubData ) +{ + const uint64 ulTimeNow = m_pGameEngine->GetGameTickCount(); + // + std::map< uint64, VoiceChatConnection_t >::iterator iter; + for( iter = m_MapConnections.begin(); iter != m_MapConnections.end(); ++iter ) + { + const EP2PSessionError eLastError = iter->second.eLastError; + CSteamID steamID( iter->first ); + + if ( eLastError == k_EP2PSessionErrorNone ) + { + SteamNetworking()->SendP2PPacket( steamID, pubData, cubData, k_EP2PSendUnreliableNoDelay ); + iter->second.ulLastSentTime = ulTimeNow; + } + } +} \ No newline at end of file diff --git a/steamworksexample/voicechat.h b/steamworksexample/voicechat.h new file mode 100644 index 0000000..2c65b4c --- /dev/null +++ b/steamworksexample/voicechat.h @@ -0,0 +1,65 @@ +//========= Copyright © 1996-2010, Valve LLC, All rights reserved. ============ +// +// Purpose: Class for P2P voice chat +// +// $NoKeywords: $ +//============================================================================= + +#ifndef VOICE_CHAT_H +#define VOICE_CHAT_H + +#include "GameEngine.h" +#include "SpaceWar.h" +#include "Messages.h" + +typedef struct VoiceChatConnection_s +{ + EP2PSessionError eLastError; // ErrorNone means we have a session + uint64 ulLastReceiveVoiceTime; + uint64 ulLastSentTime; + HGAMEVOICECHANNEL hVoiceChannel; // engine voice channel for this player + bool bActive; +} VoiceChatConnection_t; + +class CVoiceChat +{ +public: + + CVoiceChat( IGameEngine *pGameEngine ); + ~CVoiceChat(); + + bool StartVoiceChat(); + void StopVoiceChat(); + + // chat control + void MarkAllPlayersInactive(); + void MarkPlayerAsActive( CSteamID steamID ); + + bool IsPlayerTalking( CSteamID steamID ); + + // chat engine + void RunFrame(); + bool HandleMessage( CSteamID fromSteamID, EMessage eMsg, const void *pMessage ); + void SendMessageToAll( const void *pubData, uint32 cubData ); + +private: + + STEAM_CALLBACK( CVoiceChat, OnP2PSessionRequest, P2PSessionRequest_t, m_CallbackP2PSessionRequest ); + STEAM_CALLBACK( CVoiceChat, OnP2PSessionConnectFail, P2PSessionConnectFail_t, m_CallbackP2PSessionConnectFail ); + + void CheckConnections(); + + // Pointer to engine instance (so we can play sound) + IGameEngine *m_pGameEngine; + + // map of voice chat sessions with other players + std::map< uint64, VoiceChatConnection_t > m_MapConnections; + + CSteamID m_SteamIDLocalUser; // ourself + bool m_bIsActive; // is voice chat system active + uint64 m_ulLastTimeTalked; // last time we've talked ourself + HGAMEVOICECHANNEL m_hVoiceLoopback; + uint8 m_ubUncompressedVoice[ VOICE_OUTPUT_SAMPLE_RATE * BYTES_PER_SAMPLE ]; // too big for the stack +}; + +#endif diff --git a/tools/ContentPrep.zip b/tools/ContentPrep.zip index f655e4f..5bdd43c 100644 Binary files a/tools/ContentPrep.zip and b/tools/ContentPrep.zip differ diff --git a/tools/goldmaster/disk_assets/Setup.exe b/tools/goldmaster/disk_assets/Setup.exe index 56d73b1..78169e2 100644 Binary files a/tools/goldmaster/disk_assets/Setup.exe and b/tools/goldmaster/disk_assets/Setup.exe differ diff --git a/tools/goldmaster/disk_assets/SteamRetailInstaller.dmg b/tools/goldmaster/disk_assets/SteamRetailInstaller.dmg new file mode 100644 index 0000000..a3e7a56 Binary files /dev/null and b/tools/goldmaster/disk_assets/SteamRetailInstaller.dmg differ diff --git a/tools/goldmaster/disk_assets/SteamService.exe b/tools/goldmaster/disk_assets/SteamService.exe index ce75da2..4850ff8 100644 Binary files a/tools/goldmaster/disk_assets/SteamService.exe and b/tools/goldmaster/disk_assets/SteamService.exe differ diff --git a/tools/goldmaster/disk_assets/resources/SteamInstall_English.msi b/tools/goldmaster/disk_assets/resources/SteamInstall_English.msi index 0dfc5eb..c9d18c4 100644 Binary files a/tools/goldmaster/disk_assets/resources/SteamInstall_English.msi and b/tools/goldmaster/disk_assets/resources/SteamInstall_English.msi differ diff --git a/tools/goldmaster/disk_assets/resources/installscript.vdf b/tools/goldmaster/disk_assets/resources/installscript.vdf index 2767d0b..67b94ba 100644 --- a/tools/goldmaster/disk_assets/resources/installscript.vdf +++ b/tools/goldmaster/disk_assets/resources/installscript.vdf @@ -1,5 +1,10 @@ "InstallScript" { + + // Install scripts on the Gold Master are only necessary for Games for Windows titles. + // Otherwise this file doesn't need to be shipped on the GM and the Steam client + // will run the install script shipped with your depots at a later point + "Run Process" { "VCRedist" diff --git a/tools/goldmaster/disk_assets/resources/setup_english.ini b/tools/goldmaster/disk_assets/resources/setup_english.ini index 44df570..a180cea 100644 Binary files a/tools/goldmaster/disk_assets/resources/setup_english.ini and b/tools/goldmaster/disk_assets/resources/setup_english.ini differ diff --git a/tools/goldmaster/disk_assets/setup.ini b/tools/goldmaster/disk_assets/setup.ini index 2336823..bfa7c9a 100644 Binary files a/tools/goldmaster/disk_assets/setup.ini and b/tools/goldmaster/disk_assets/setup.ini differ