-
Posts
4,937 -
Joined
-
Last visited
Content Type
Blogs
Forums
Store
Gallery
Videos
Downloads
Everything posted by gamecreator
-
And here's how to create a lobby with a key that anyone in the world can find and join automatically. I added the requeststeamlobbylist function and the related OnLobbyMatchListCallback callback. #include "App.h" using namespace Leadwerks; App::App() : window(NULL), context(NULL), world(NULL), camera(NULL) {} App::~App() { delete world; delete window; } bool createdlobby = false; bool joinedlobby = false; CSteamID steamIDLobby; CSteamID userjoiningorleavinglobby; class LobbyManagerClass { public: LobbyManagerClass(); void createlobby(); void requeststeamlobbylist(); // Called when the lobby is created (it's not instant, as it has to run it by Steam first) void OnLobbyCreated(LobbyCreated_t *pCallback, bool bIOFailure); CCallResult<LobbyManagerClass, LobbyCreated_t> m_SteamCallResultLobbyCreated; // Called when the lobby is created (it's not instant, as it has to run it by Steam first) void OnLobbyMatchListCallback(LobbyMatchList_t *pCallback, bool bIOFailure); CCallResult<LobbyManagerClass, LobbyMatchList_t> m_SteamCallResultLobbyMatchList; // Called when invitee enters a lobby void OnLobbyEntered(LobbyEnter_t *pCallback, bool bIOFailure); CCallResult<LobbyManagerClass, LobbyEnter_t> m_SteamCallResultLobbyEntered; // Called every time someone joins or leaves a lobby STEAM_CALLBACK(LobbyManagerClass, OnLobbyChatUpdate, LobbyChatUpdate_t, m_CallbackChatDataUpdate); } lobbymanager; LobbyManagerClass::LobbyManagerClass() : m_CallbackChatDataUpdate(this, &LobbyManagerClass::OnLobbyChatUpdate) { } void LobbyManagerClass::createlobby() { SteamAPICall_t hSteamAPICall = SteamMatchmaking()->CreateLobby(k_ELobbyTypePublic, 16); // Set the call result for when Steam returns lobby creation results m_SteamCallResultLobbyCreated.Set(hSteamAPICall, this, &LobbyManagerClass::OnLobbyCreated); } void LobbyManagerClass::requeststeamlobbylist() { // You can limit the number of results to a specified amount, if you want // SteamMatchmaking()->AddRequestLobbyListResultCountFilter(15); // Steam returns closest results first by geographic IP location so search worldwide SteamMatchmaking()->AddRequestLobbyListDistanceFilter(k_ELobbyDistanceFilterWorldwide); // Let's only find lobbies which have a name of Leadwerks lobby SteamMatchmaking()->AddRequestLobbyListStringFilter("name", "Leadwerkslobby", k_ELobbyComparisonEqual); SteamAPICall_t hSteamAPICall = SteamMatchmaking()->RequestLobbyList(); // Set the call result for when Steam returns a list of lobbies m_SteamCallResultLobbyMatchList.Set(hSteamAPICall, this, &LobbyManagerClass::OnLobbyMatchListCallback); } void LobbyManagerClass::OnLobbyCreated(LobbyCreated_t *pCallback, bool bIOFailure) { // If lobby creation was successful if(pCallback->m_eResult==k_EResultOK) { // Get lobby info steamIDLobby = pCallback->m_ulSteamIDLobby; SteamMatchmaking()->SetLobbyData(steamIDLobby, "name", "Leadwerkslobby"); createdlobby = true; } else MessageBoxA(0, "Could not create lobby.", "Error", 0); } void LobbyManagerClass::OnLobbyMatchListCallback(LobbyMatchList_t *pCallback, bool bIOFailure) { if(!bIOFailure && pCallback->m_nLobbiesMatching>0) { // Join the first lobby on the list steamIDLobby = SteamMatchmaking()->GetLobbyByIndex(0); SteamAPICall_t hSteamAPICall = SteamMatchmaking()->JoinLobby(steamIDLobby); // Set the call result for when player enters lobby m_SteamCallResultLobbyEntered.Set(hSteamAPICall, this, &LobbyManagerClass::OnLobbyEntered); } } void LobbyManagerClass::OnLobbyEntered(LobbyEnter_t *pCallback, bool bIOFailure) { // If we didn't get a success confirmation, let joiner know if(pCallback->m_EChatRoomEnterResponse!=k_EChatRoomEnterResponseSuccess) { MessageBoxA(0, "Failed to enter lobby", "Error", 0); return; } // We succesfully entered someone else's lobby steamIDLobby = pCallback->m_ulSteamIDLobby; joinedlobby = true; } // This gets called for both the invitor and invitees whenever someone joins or leaves a lobby void LobbyManagerClass::OnLobbyChatUpdate(LobbyChatUpdate_t *pCallback) { // Get the SteamID of the person joining or leaving the lobby userjoiningorleavinglobby = pCallback->m_ulSteamIDUserChanged; } bool App::Start() { window = Leadwerks::Window::Create("Steam Lobby Example"); context = Context::Create(window); world = World::Create(); camera = Camera::Create(); window->HideMouse(); Steamworks::Initialize(); return true; } bool App::Loop() { if(!SteamAPI_IsSteamRunning()) { MessageBoxA(0, "Steam not detected. Please run Steam before running this program.", "Error", 0); return false; } // Exit program if needed if(window->KeyHit(Key::Escape) || window->Closed()) return false; // This must be called regularly for callbacks and call results to work SteamAPI_RunCallbacks(); Leadwerks::Time::Update(); world->Update(); world->Render(); context->SetBlendMode(Blend::Alpha); context->SetColor(1, 1, 1); // If no lobby is created or joined, give player option to create one if(!createdlobby && !joinedlobby) { if(window->KeyHit(Key::C)) lobbymanager.createlobby(); if(window->KeyHit(Key::R)) lobbymanager.requeststeamlobbylist(); context->DrawText("Press C to create a lobby or R to refresh lobby list", 10, 30); context->DrawText("No lobby matches yet", 10, 50); } if(createdlobby) context->DrawText("You created a lobby. Lobby name:", 10, 30); if(joinedlobby) context->DrawText("You joined a lobby. Lobby name:", 10, 30); // Show lobby info if(createdlobby || joinedlobby) { context->DrawText(SteamMatchmaking()->GetLobbyData(steamIDLobby, "name"), 220, 30); context->DrawText("Members in lobby:", 10, 50); for(int i=0; i<SteamMatchmaking()->GetNumLobbyMembers(steamIDLobby); i++) { CSteamID steamIDFriendinlobby = SteamMatchmaking()->GetLobbyMemberByIndex(steamIDLobby, i); context->DrawText(SteamFriends()->GetFriendPersonaName(steamIDFriendinlobby), 10, 70+20*i); } } context->SetBlendMode(Blend::Solid); context->Sync(false); Sleep(10); // Sleep a bit to not spam connection return true; }
-
Here is a Steam lobby example. Sadly, it's not very beginner friendly as it requires callbacks and call results to be used and Steam requires that you do this all within a class. That said, once you wrap your head around it, it's not too bad. As is my preference, I've made this as easy and short as I could. Check out the Getting Started with Steam link to read more abotu callbacks and call results: https://partner.steamgames.com/documentation/getting_started Also, a lot of this was learned from here: https://partner.steamgames.com/documentation/matchmaking This program very simply creates a lobby, invites friends to join and shows you the results on either side (invitor or invitee). #include "App.h" using namespace Leadwerks; App::App() : window(NULL), context(NULL), world(NULL), camera(NULL) {} App::~App() { delete world; delete window; } bool createdlobby = false; bool joinedlobby = false; CSteamID m_steamIDLobby; CSteamID userjoiningorleavinglobby; class LobbyManagerClass { public: LobbyManagerClass(); void createlobby(); // Called when the lobby is created (it's not instant, as it has to run it by Steam first) void OnLobbyCreated(LobbyCreated_t *pCallback, bool bIOFailure); CCallResult<LobbyManagerClass, LobbyCreated_t> m_SteamCallResultLobbyCreated; // Called when invitee enters a lobby void OnLobbyEntered(LobbyEnter_t *pCallback, bool bIOFailure); CCallResult<LobbyManagerClass, LobbyEnter_t> m_SteamCallResultLobbyEntered; // Called every time someone joins or leaves a lobby STEAM_CALLBACK(LobbyManagerClass, OnLobbyChatUpdate, LobbyChatUpdate_t, m_CallbackChatDataUpdate); // Called when someone invites you to a lobby STEAM_CALLBACK(LobbyManagerClass, OnGameLobbyJoinRequested, GameLobbyJoinRequested_t, m_CallbackLobbyDataUpdate); } lobbymanager; LobbyManagerClass::LobbyManagerClass() : m_CallbackLobbyDataUpdate(this, &LobbyManagerClass::OnGameLobbyJoinRequested), m_CallbackChatDataUpdate(this, &LobbyManagerClass::OnLobbyChatUpdate) { } void LobbyManagerClass::createlobby() { SteamAPICall_t hSteamAPICall = SteamMatchmaking()->CreateLobby(k_ELobbyTypePublic, 16); // Set the call result for when Steam returns lobby creation results m_SteamCallResultLobbyCreated.Set(hSteamAPICall, this, &LobbyManagerClass::OnLobbyCreated); } void LobbyManagerClass::OnLobbyEntered(LobbyEnter_t *pCallback, bool bIOFailure) { // If we didn't get a success confirmation, let joiner know if(pCallback->m_EChatRoomEnterResponse!=k_EChatRoomEnterResponseSuccess) { MessageBoxA(0, "Failed to enter lobby", "Error", 0); return; } // We succesfully entered someone else's lobby m_steamIDLobby = pCallback->m_ulSteamIDLobby; joinedlobby = true; } void LobbyManagerClass::OnLobbyCreated(LobbyCreated_t *pCallback, bool bIOFailure) { // If lobby creation was successful if(pCallback->m_eResult==k_EResultOK) { // Get lobby info m_steamIDLobby = pCallback->m_ulSteamIDLobby; SteamMatchmaking()->SetLobbyData(m_steamIDLobby, "name", "Leadwerkslobby"); createdlobby = true; } else MessageBoxA(0, "Could not create lobby.", "Error", 0); } // This gets called for both the invitor and invitees whenever someone joins or leaves a lobby void LobbyManagerClass::OnLobbyChatUpdate(LobbyChatUpdate_t *pCallback) { // Get the SteamID of the person joining or leaving the lobby userjoiningorleavinglobby = pCallback->m_ulSteamIDUserChanged; } // Called when you accept an invite void LobbyManagerClass::OnGameLobbyJoinRequested(GameLobbyJoinRequested_t *pParam) { // Join lobby SteamAPICall_t hSteamAPICall = SteamMatchmaking()->JoinLobby(pParam->m_steamIDLobby); // Set the call result for when player enters lobby m_SteamCallResultLobbyEntered.Set(hSteamAPICall, this, &LobbyManagerClass::OnLobbyEntered); } bool App::Start() { window = Leadwerks::Window::Create("Steam Lobby Example"); context = Context::Create(window); world = World::Create(); camera = Camera::Create(); window->HideMouse(); Steamworks::Initialize(); return true; } bool App::Loop() { if(!SteamAPI_IsSteamRunning()) { MessageBoxA(0, "Steam not detected. Please run Steam before running this program.", "Error", 0); return false; } // Exit program if needed if(window->KeyHit(Key::Escape) || window->Closed()) return false; // This must be called regularly for callbacks and call results to work SteamAPI_RunCallbacks(); Leadwerks::Time::Update(); world->Update(); world->Render(); context->SetBlendMode(Blend::Alpha); context->SetColor(1, 1, 1); // If no lobby is created or joined, give player option to create one if(!createdlobby && !joinedlobby) { if(window->KeyHit(Key::C)) lobbymanager.createlobby(); context->DrawText("Press C to create a lobby or wait for a friend invite to join one", 10, 30); } // Once a lobby is created, give user option to invite others else if(createdlobby) { context->DrawText("Press I to invite a friend to your lobby.", 10, 10); if(window->KeyHit(Key::I)) SteamFriends()->ActivateGameOverlay("LobbyInvite"); // ... or you can invite friends through the program with the following syntax // InviteUserToLobby(CSteamID steamIDLobby, CSteamID steamIDInvitee); } if(createdlobby) context->DrawText("You created a lobby. Lobby name:", 10, 30); if(joinedlobby) context->DrawText("You joined a lobby. Lobby name:", 10, 30); // Show lobby info if(createdlobby || joinedlobby) { context->DrawText(SteamMatchmaking()->GetLobbyData(m_steamIDLobby, "name"), 220, 30); context->DrawText("Number of lobby members:", 10, 50); context->DrawText(String(SteamMatchmaking()->GetNumLobbyMembers(m_steamIDLobby)), 170, 50); context->DrawText("Members in lobby:", 10, 90); for(int i=0; i<SteamMatchmaking()->GetNumLobbyMembers(m_steamIDLobby); i++) { CSteamID steamIDFriendinlobby = SteamMatchmaking()->GetLobbyMemberByIndex(m_steamIDLobby, i); context->DrawText(SteamFriends()->GetFriendPersonaName(steamIDFriendinlobby), 10, 110+20*i); } } context->SetBlendMode(Blend::Solid); context->Sync(false); Sleep(10); // Sleep a bit to not spam connection return true; } Thanks to Rick for his help in understanding C++ I haven't used before.
-
Or if you're at http://www.leadwerks.com/werkspace/page/documentation/_/command-reference/ same thing: choose Documentation as the category. It really should be the default and maybe Josh chould change.
-
Great scene. You don't by any chance have a higher resolution picture of that?
-
http://www.leadwerks.com/werkspace/topic/9648-os-x-and-le-31/
-
The Documentation -> Tutorials link at the top links elsewhere. Consdering that Leadwerks isn't Steam dependent, the Steam tutorials page should just link to the one on Leadwerks.
-
Not an answer to your question but http://www.leadwerks.com/werkspace/topic/7921-bullet-holes-decals/#entry62725
-
Trying to Figure Out Steam Lobby / C++ Templates / Callbacks
gamecreator replied to gamecreator's topic in Programming
That makes perfect sense. Really appreciated Rick! I'll see if I can get this up and running tonight. -
Trying to Figure Out Steam Lobby / C++ Templates / Callbacks
gamecreator replied to gamecreator's topic in Programming
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. -
Trying to Figure Out Steam Lobby / C++ Templates / Callbacks
gamecreator replied to gamecreator's topic in Programming
And from here: https://partner.steamgames.com/documentation/getting_started -
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: 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.
-
That makes things a lot clearer. But I wonder: can we use the system we had in LE2 where we could just use our own numbers and assign collisions arbitrarily?
-
Nice. I wonder though why you click the destination and then the unit every time.
-
Ah, ok. I was just used to using the KEY_ prefixes but no problem switching. Thanks.
-
Sorry, thought it would be obvious. You can't do things like if(window->KeyDown(Key::KEY_0)) ... because you get Shouldn't we have access to all those keys in C? I get that I can uncomment them but I don't want to mess with things you commented for whatever reason. I figured you'd want to officially uncomment them in a patch.
-
Ha ha, I created a simple one for 2.5 but see here: http://www.leadwerks.com/werkspace/topic/9496-names-for-sequences-animations/page__st__20#entry72606
-
Maybe he edited his post afterwards but he's not talking about the Steam version.
-
Ah, I forgot about that. Thank you. It's an unofficial feature and so that's why I didn't find it in the documentation.
-
Or tex file. And of course alter height on the fly at any point.
-
And of course if you provide volume controls to turn off sound effects (which almost all games do), how hard is it to record music? You can do it with Fraps.
-
It's about a effort versus reward. If a few minutes of zipping up and password protecting files deters the majority of people from ripping off my stuff, it's worth it to me. It's certainly way better than providing models, textures and music in their original format openly. But I'm under no delusion that it works against everyone. In fact, I know some programs can take models and textures directly from memory as you're running them. Plus there are forums where people love writing programs that extract such data from game files, as a personal challenge.
-
Password protecting data is definitely a must for anyone who wants to release even small games or demos and it was nice that LE2 had this. It was one of the things I looked for when I was hunting for an engine. Considering that I own hundreds of dollars worth of models, it would be nice to be able to use them even in any competition. For now, I can't.
-
Lighting and physics speed optimizations. Remember this from LE2?
-
Josh mentioned some time back that there would never be a Leadwerks 4, for what that's worth. Upgrading from 3.0 to 3.1 cost existing users $100 so if you ever see 3.2 announced, you're better off waiting to buy it all together and save.
-
And in answer to your other question (though you may have already figured it out) try Sleep(3000);