Hello,
I have been experimenting with the OSS in c++. While following eXi’s Wiki allowed me to host and find sessions, I am trying to create a matchmaking system that automates the process of finding and hosting sessions. At the moment, looking for a session lets me join it (without travelling with APlayerController::ClientTravel()) when a session is found, or creates one (without starting it). This way I can keep both players in a lobby screen (or map) and when the server learns that the client connected it can start the session and go to a map. The client then tests the state of the session and if it is InProgress, calls APlayerController::ClientTravel() with the right URL and everything.
Problem is, I have tried using RegisterPlayer and UpdateSession. These functions end up causing crashes when right after their delegates are called, or before they are called, or they simply affect calls to GetNamedSession, making it fail and breaking when destroying the sessions.
My question: Is there a way to tell the server (listen or dedicated, although I am using a listen server setup) that a client joined without using these? If not, how am I supposed to use them?
Thanks!
ps: Some code.
My wrapper for the RegisterPlayer:
bool USteamGameInstance::RegisterPlayerInternal(FName SessionName, TSharedPtr<const FUniqueNetId>PlayerId, bool bWasInvited)
{
/// I am calling destroy for some reason
//WhoCalled = "RegisterPlayerInternal";
/// Return bool
bool bSuccessful = false;
/// Get SessionInterface from the OnlineSubsystem
IOnlineSessionPtr Sessions = GetSessionInterface();
if (Sessions.IsValid() && PlayerId.IsValid())
{
/// Set the Handle for registering all players since there is no OnRegisterPlayerComplete delegate
OnRegisterPlayersCompleteDelegateHandle = Sessions->AddOnRegisterPlayersCompleteDelegate_Handle(OnRegisterPlayersCompleteDelegate);
/// Here the call has to be to RegisterPlayer
bSuccessful = Sessions->RegisterPlayer(SessionName, *PlayerId, bWasInvited);
}
return bSuccessful;
}
My OnRegisterPlayersComplete delegate:
void USteamGameInstance::OnRegisterPlayersComplete(FName SessionName, const TArray<TSharedRef<const FUniqueNetId>>& Players, bool bWasSuccessful)
{
FString BoolValue = BoolToFString(bWasSuccessful);
UE_LOG(LogTemp, Warning, TEXT("OnRegisterPlayersComplete %s, %s, %d"), *SessionName.ToString(), *BoolValue, Players.Num())
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Yellow, FString::Printf(TEXT("OnRegisterPlayersComplete %s, %s, %d"), *SessionName.ToString(), *BoolValue, Players.Num()));
/// Who is calling
UE_LOG(LogTemp, Warning, TEXT("Calling function: %s"), *WhoCalled)
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Yellow, FString::Printf(TEXT("Calling function: %s"), *WhoCalled));
/// Get SessionInterface from the OnlineSubsystem
IOnlineSessionPtr Sessions = GetSessionInterface();
if (Sessions.IsValid())
{
/// Clear the Delegate again
Sessions->ClearOnRegisterPlayersCompleteDelegate_Handle(OnRegisterPlayersCompleteDelegateHandle);
/// When matchmaking let BP code be run to decide when to start the match
OnCreateSessionSuccessfulDelegate.Broadcast();
/// When in the client, update the settings and broadcast it
if (bWasSuccessful && !GIsServer)
{
/// Set the UpdateSession delegate handle and call UpdateSession
//OnUpdateSessionCompleteDelegateHandle = Sessions->AddOnUpdateSessionCompleteDelegate_Handle(OnUpdateSessionCompleteDelegate);
TSharedPtr<FOnlineSessionSettings> NewSettings = MakeShareable(&Sessions->GetNamedSession(SessionName)->SessionSettings);
//Sessions->UpdateSession(SessionName, *NewSettings, true);
}
}
}
My OnJoinSessionComplete (where I call UpdateSession now):
void USteamGameInstance::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
FString test = ToString(Result);
UE_LOG(LogTemp, Warning, TEXT("OnDestroySessionComplete %s, %s"), *SessionName.ToString(), *test)
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Yellow, FString::Printf(TEXT("OnJoinSessionComplete %s, %s"), *SessionName.ToString(), *test));
/// Get SessionInterface from the OnlineSubsystem
IOnlineSessionPtr Sessions = GetSessionInterface();
if (Sessions.IsValid())
{
/// Clear the Delegate again
Sessions->ClearOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegateHandle);
/// Register the joining player here
//RegisterPlayerInternal(SessionName, GetLocalPlayer()->GetPreferredUniqueNetId(), false);
/// Get the first local PlayerController, so we can call "ClientTravel" to get to the Server Map
/// This is something the Blueprint Node "Join Session" does automatically!
APlayerController * const PlayerController = GetFirstLocalPlayerController();
/// We need a FString to use ClientTravel and we can let the SessionInterface contruct such a
/// String for us by giving him the SessionName and an empty String. We want to do this, because
/// Every OnlineSubsystem uses different TravelURLs
FString TravelURL;
if (bIsMatchMaking)
{
/// When matchmaking, allow BP code to deal with joining and traveling
//OnJoinSessionSuccessfulDelegate.Broadcast(TravelURL, ETravelType::TRAVEL_Absolute);
/// Add a new setting telling the server we connected to it
TSharedPtr<FOnlineSessionSettings> NewSettings = MakeShareable(&Sessions->GetNamedSession(SessionName)->SessionSettings);
NewSettings->Set(FName("ClientConnected"), true, EOnlineDataAdvertisementType::ViaOnlineService);
/// When matchmaking, update the session to let the server now we are connected to this session
OnUpdateSessionCompleteDelegateHandle = Sessions->AddOnUpdateSessionCompleteDelegate_Handle(OnUpdateSessionCompleteDelegate);
Sessions->UpdateSession(SessionName, *NewSettings, true);
}
else
{
/// Finally call the ClienTravel. If you want, you could print the TravelURL to see
/// how it really looks like
PlayerController->ClientTravel(TravelURL, ETravelType::TRAVEL_Absolute);
}
}
}