gamecreator Posted May 12, 2014 Share Posted May 12, 2014 I've been trying to wrap my head around how Steam Lobby works by looking at the docs, forums and the Spacewar example that comes with Steam but it's beyond my skills. Was hoping someone could help. To start, the Steam Matchmaking & Lobbies page says the following: Creating a lobby If you can't find an existing lobby for a user to join, this is when you'd typically create a lobby. Just call: SteamAPICall_t CreateLobby( ELobbyType eLobbyType, int cMaxMembers ); and wait for it to complete. The call result whether or not it succeeded, and if it did returns the steamID of the lobby in a LobbyCreated_t struct, which can be used to set metadata on the lobby. The first thing you'll want to do after you create a lobby is set a bunch of data on the lobby, that other game clients can use to search for it (see below). I then looked at the Steam Spacewar example and this is how it uses it: // ask steam to create a lobby SteamAPICall_t hSteamAPICall = SteamMatchmaking()->CreateLobby( k_ELobbyTypePublic /* public lobby, anyone can find it */, 4 ); // set the function to call when this completes m_SteamCallResultLobbyCreated.Set( hSteamAPICall, this, &CSpaceWarClient::OnLobbyCreated ); I kind of understand that the function OnLobbyCreated gets called when the CreateLobby function eventually succeeds. But I had to look into what m_SteamCallResultLobbyCreated was. That resulted in these two lines in SpaceWarClient.h // callback for when we're creating a new lobby void OnLobbyCreated( LobbyCreated_t *pCallback, bool bIOFailure ); CCallResult<CSpaceWarClient, LobbyCreated_t> m_SteamCallResultLobbyCreated; That CCallResult and whatever's going on after it completely lost me. Pretty sure that's a template but that's over my head. And what it has to do with CSpaceWarClient, which itself is a giant class, is past me. But for kicks, here's CCallResult in the steam_api.h header. //----------------------------------------------------------------------------- // Purpose: maps a steam async call result to a class member function // template params: T = local class, P = parameter struct //----------------------------------------------------------------------------- template< class T, class P > class CCallResult : private CCallbackBase { public: typedef void (T::*func_t)( P*, bool ); CCallResult() { m_hAPICall = k_uAPICallInvalid; m_pObj = NULL; m_Func = NULL; m_iCallback = P::k_iCallback; } void Set( SteamAPICall_t hAPICall, T *p, func_t func ) { if ( m_hAPICall ) SteamAPI_UnregisterCallResult( this, m_hAPICall ); m_hAPICall = hAPICall; m_pObj = p; m_Func = func; if ( hAPICall ) SteamAPI_RegisterCallResult( this, hAPICall ); } bool IsActive() const { return ( m_hAPICall != k_uAPICallInvalid ); } void Cancel() { if ( m_hAPICall != k_uAPICallInvalid ) { SteamAPI_UnregisterCallResult( this, m_hAPICall ); m_hAPICall = k_uAPICallInvalid; } } ~CCallResult() { Cancel(); } void SetGameserverFlag() { m_nCallbackFlags |= k_ECallbackFlagsGameServer; } private: virtual void Run( void *pvParam ) { m_hAPICall = k_uAPICallInvalid; // caller unregisters for us (m_pObj->*m_Func)( (P *)pvParam, false ); } void Run( void *pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall ) { if ( hSteamAPICall == m_hAPICall ) { m_hAPICall = k_uAPICallInvalid; // caller unregisters for us (m_pObj->*m_Func)( (P *)pvParam, bIOFailure ); } } int GetCallbackSizeBytes() { return sizeof( P ); } SteamAPICall_t m_hAPICall; T *m_pObj; func_t m_Func; }; Not sure if that's enough info. I'm still searching the Steam forums for help too. Thanks for looking. Quote Link to comment Share on other sites More sharing options...
gamecreator Posted May 12, 2014 Author Share Posted May 12, 2014 And from here: https://partner.steamgames.com/documentation/getting_started Call Results When appropriate, some Steamworks methods use call results instead of a callback to asynchronously return results from a function call. The primary difference between a call result and a callback is that callbacks are broadcast to all listeners, where call results target one listener. Like callbacks, your game will need to call SteamAPI_RunCallbacks() to dispatch call results to their listener. The following example shows how to use a CCallResult to map the asynchronous result of finding a leaderboard to a member function of a class named CLeaderboard: // In your class definition class CLeaderboard { ... void OnFindLeaderboard( LeaderboardFindResult_t *pResult, bool bIOFailure ); CCallResult<CLeaderboards, LeaderboardFindResult_t> m_callResultFindLeaderboard; } // Make the request to create the leaderboard void CLeaderboard::FindLeaderboard() { SteamAPICall_t hSteamAPICall = SteamUserStats()->FindLeaderboard( "Best Score" ); m_callResultFindLeaderboard.Set( hSteamAPICall, this, &CLeaderboard::OnFindLeaderboard ); } // Called when SteamUserStats()->FindLeaderboard() returns asynchronously void CLeaderboard::OnFindLeaderboard( LeaderboardFindResult_t *pResult, bool bIOFailure ) { // see if we encountered an error during the call if ( !pResult->m_bLeaderboardFound || bIOFailure ) { // show error return; } SteamLeaderboard_t hLeaderboard = pResult->m_hSteamLeaderboard; // use handle } Quote Link to comment Share on other sites More sharing options...
gamecreator Posted May 12, 2014 Author Share Posted May 12, 2014 Having found that last snippet just now and reading it a few times, I think I understand what it does, even if I don't really understand how it works. I'll try to mimic it with lobbies to see if I can get it working next week. If I do, I'll add it to the Introduction to Steam thread. Quote Link to comment Share on other sites More sharing options...
Rick Posted May 12, 2014 Share Posted May 12, 2014 Purpose: maps a steam async call result to a class member function So with callbacks you can either have a pointer to a normal C function (which is easy but not object oriented) or you can make a pointer to a C++ class method. That's what this is doing. It allows you to define one of your C++ class methods to be a callback that the Steam API calls when something happens. I use this all the time but I call mine Event<>. When you are coding in an object oriented way you don't want to mess around with straight C functions for callbacks. It screws up the flow of an object oriented program. Leadwerks actually does this with it's collision methods in C++ and to allow to not screw up the flow it allows you to pass a void pointer as an extra parameter so we can pass around class objects (this is generally actually looked at as bad practice though). CCallResult<CLeaderboards, LeaderboardFindResult_t> m_callResultFindLeaderboard; Think of this as defining an event. The event will call a function inside the CLeaderboards class and that function will have 1 parameter of type LeaderboardFinalResult_t. SteamAPICall_t hSteamAPICall = SteamUserStats()->FindLeaderboard( "Best Score" ); m_callResultFindLeaderboard.Set( hSteamAPICall, this, &CLeaderboard::OnFindLeaderboard ); This is where you are linking your event to your class and what function will be called on Steam. So when the FindLeaderboard() function from Steam gets called it'll in turn call your event function in your class. You are giving 'this' which is a pointer to your class, and defining what function in your class it'll call. 1 Quote Link to comment Share on other sites More sharing options...
gamecreator Posted May 12, 2014 Author Share Posted May 12, 2014 That makes perfect sense. Really appreciated Rick! I'll see if I can get this up and running tonight. Quote Link to comment Share on other sites More sharing options...
Rick Posted May 12, 2014 Share Posted May 12, 2014 [edit] Had this typed out and sitting before you posted so just going to finish it up IMHO something like this would be great if LE would do it. Valve took it a step farther with also sending what function you want to call from their API which wouldn't be needed and confuses things I think. I don't know why they require the class name to be passed in the template. It's not needed and makes it more confusing if you ask me, but LE falling back to C callbacks in my eyes is just a lack of understanding how to do C++ method callbacks. Instead of having a C style Collision callback which breaks the flow of OOP, it would be better if the Entity class had an event named OnCollide that we can subscribe to with our C++ classes. All callbacks should work like this if you ask me. class Player { private: Entity* player; void player_OnCollide(CollisionArgs e) { } public: void Player() { player->OnCollide.Bind(this, &player_Collide); } }; That seems pretty easy to follow doesn't it? This is the same thing steam is doing but they put defining the event itself on your shoulders too and for some reason require you to pass in the classname. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.