In my Discord someone was asking about Steam parties so I thought that it was a good topic to write a guide on so here we are. Quite frankly this is something that someone should've made a guide on a long time ago.
Prerequisites
- Steam plugin (SteamBridge, UWorks, SteamCore, etc)
- Some C++ or BP knowledge
- A brain and patience 😀
In this guide, I'm using my SteamBridge plugin, but you're more than welcome to use any other Steam plugin that you already have. (The Steam API is the same across the board, but each plugin may implement it differently.)
The entirety of the party/lobby system is created using the ISteamMatchmaking interface. We're mainly going to use the following methods ISteamMatchmaking::CreateLobby
, ISteamMatchmaking::InviteUserToLobby
, ISteamMatchmaking::JoinLobby
, ISteamMatchmaking::LeaveLobby
, ISteamMatchmaking::SendLobbyChatMsg
, and most of the Lobby delegates listed here.
I'm doing most of this guide in C++, but you can do it in BP just as easily.
- Create a GameInstance class and open it.
#pragma once #include "CoreMinimal.h" #include "Engine/GameInstance.h" #include "SteamBridge/Public/SteamStructs.h" #include "CGameInstance.generated.h" /** * */ UCLASS() class STEAMBRIDGETEST_API UCGameInstance : public UGameInstance { GENERATED_BODY() public: UFUNCTION(BlueprintPure, Category = "SteamBridgeTest|UCGameInstance") FSteamID GetSteamLobbyID() const { return m_SteamLobbyID; } UFUNCTION(BlueprintCallable, Category = "SteamBridgeTest|UCGameInstance") void CreateSteamLobby(ESteamLobbyType LobbyType, uint8 MaxPlayers = 4); UFUNCTION(BlueprintCallable, Category = "SteamBridgeTest|UCGameInstance") void JoinSteamLobby(FSteamID SteamLobbyID); UFUNCTION(BlueprintCallable, Category = "SteamBridgeTest|UCGameInstance") void KickUserSteamLobby(FSteamID SteamIDUser); UFUNCTION(BlueprintPure, Category = "SteamBridgeTest|UCGameInstance") TArray<FString> GetLobbyMembers() const; protected: virtual void Init() override; private: // Our local copy of the lobby ID that we're in FSteamID m_SteamLobbyID; UFUNCTION() void OnLobbyChatMessage(FSteamID LobbyID, FSteamID SenderID, ESteamChatEntryType ChatEntryType, int32 ChatID); UFUNCTION() void OnLobbyCreated(ESteamResult Result, FSteamID SteamIDLobby); UFUNCTION() void OnLobbyDataUpdate(FSteamID SteamIDLobby, FSteamID SteamIDMember, bool bSuccess); UFUNCTION() void OnLobbyEnter(FSteamID SteamIDLobby, bool bLocked, ESteamChatRoomEnterResponse ChatRoomEnterResponse); UFUNCTION() void OnLobbyInvite(FSteamID SteamIDUser, FSteamID SteamIDLobby, int32 GameID); }; // CPP // Fill out your copyright notice in the Description page of Project Settings. #include "CGameInstance.h" #include "SteamBridge/Public/Core/SteamFriends.h" #include "SteamBridge/Public/Core/SteamMatchmaking.h" #include "SteamBridge/Public/Core/SteamUser.h" void UCGameInstance::Init() { Super::Init(); if (USteamMatchmaking* Matchmaking = USteamMatchmaking::GetSteamMatchmaking()) { // Matchmaking callbacks Matchmaking->m_OnLobbyChatMsg.AddDynamic(this, &UCGameInstance::OnLobbyChatMessage); Matchmaking->m_OnLobbyCreated.AddDynamic(this, &UCGameInstance::OnLobbyCreated); Matchmaking->m_OnLobbyDataUpdate.AddDynamic(this, &UCGameInstance::OnLobbyDataUpdate); Matchmaking->m_OnLobbyEnter.AddDynamic(this, &UCGameInstance::OnLobbyEnter); Matchmaking->m_OnLobbyInvite.AddDynamic(this, &UCGameInstance::OnLobbyInvite); } } void UCGameInstance::CreateSteamLobby(ESteamLobbyType LobbyType, uint8 MaxPlayers) { if (m_SteamLobbyID == 0) { if (USteamMatchmaking* Matchmaking = USteamMatchmaking::GetSteamMatchmaking()) { Matchmaking->CreateLobby(LobbyType, MaxPlayers); } } } void UCGameInstance::JoinSteamLobby(FSteamID SteamLobbyID) { if (USteamMatchmaking* Matchmaking = USteamMatchmaking::GetSteamMatchmaking()) { if (m_SteamLobbyID != 0) { Matchmaking->LeaveLobby(m_SteamLobbyID); m_SteamLobbyID = 0; } Matchmaking->JoinLobby(m_SteamLobbyID); } } void UCGameInstance::KickUserSteamLobby(FSteamID SteamIDUser) { if (USteamMatchmaking* Matchmaking = USteamMatchmaking::GetSteamMatchmaking()) { if (Matchmaking->GetLobbyOwner(m_SteamLobbyID) == USteamUser::GetSteamUser()->GetSteamID()) { Matchmaking->SendLobbyChatMsg(m_SteamLobbyID, FString::Printf(TEXT("Kick_%llu"), SteamIDUser.Value)); } } } TArray<FString> UCGameInstance::GetLobbyMembers() const { TArray<FString> Tmp; if (USteamMatchmaking* Matchmaking = USteamMatchmaking::GetSteamMatchmaking()) { for (uint8 i = 0; i < Matchmaking->GetNumLobbyMembers(m_SteamLobbyID); i++) { Tmp.Add(USteamFriends::GetSteamFriends()->GetFriendPersonaName(Matchmaking->GetLobbyMemberByIndex(m_SteamLobbyID, i))); } } return Tmp; } void UCGameInstance::OnLobbyChatMessage(FSteamID LobbyID, FSteamID SenderID, ESteamChatEntryType ChatEntryType, int32 ChatID) { if (USteamMatchmaking* Matchmaking = USteamMatchmaking::GetSteamMatchmaking()) { FString Msg; Matchmaking->GetLobbyChatEntry(LobbyID, ChatID, SenderID, Msg, ChatEntryType); FString Cmd, Arg; if (!Msg.Split(TEXT("_"), &Cmd, &Arg, ESearchCase::CaseSensitive)) { return; } if (Cmd == "Kick") { if (Arg == LexToString(USteamUser::GetSteamUser()->GetSteamID().Value)) { Matchmaking->LeaveLobby(m_SteamLobbyID); m_SteamLobbyID = 0; } } } } void UCGameInstance::OnLobbyCreated(ESteamResult Result, FSteamID SteamIDLobby) { m_SteamLobbyID = SteamIDLobby; } void UCGameInstance::OnLobbyDataUpdate(FSteamID SteamIDLobby, FSteamID SteamIDMember, bool bSuccess) { m_SteamLobbyID = SteamIDLobby; } void UCGameInstance::OnLobbyEnter(FSteamID SteamIDLobby, bool bLocked, ESteamChatRoomEnterResponse ChatRoomEnterResponse) { m_SteamLobbyID = SteamIDLobby; // maybe do a welcome message to the player that joined? } void UCGameInstance::OnLobbyInvite(FSteamID SteamIDUser, FSteamID SteamIDLobby, int32 GameID) { // Here you would probably would want to make a BP implementable event or even bind this delegate in BP and display an invite notification }
- Really that's all that's to it. You should create a UI for the party and then call things like
ISteamMatchmaking:InviteUserToLobby
in a widget.
until next time