Announcement

Collapse
No announcement yet.

[WIKI] [Finished] C++ Session Create/Find/Join/Destroy

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    [WIKI] [Finished] C++ Session Create/Find/Join/Destroy

    Hey there,

    if you had trouble getting Sessions to work in C++ and the ShooterGame only made it worse, i have a solution for you!
    I took my time and learned how the ShooterGame and the Session BP Nodes work and build the most basic and working
    Session System for you.

    There are some issue with the Editor (standalone works fine), but they are due to the initial connection of client and server!
    Read the first part of Wiki Entry to get help or look further down in this post.

    Here is the link to the Wiki Entry:

    Wiki Post: How to use Sessions in C++

    I will change things and add additions later. For now i need a break. Wrote 2 hours straight.


    Tell me if there is something wrong, or easier to make. Or if i'm missing something!

    If you have questions: PLEASE POST THEM!


    Known issues and workarounds (can be found in the Wiki Post too!):




    Kind regards

    Cedric 'eXi'
    Last edited by eXi; 08-01-2015, 04:03 AM.
    Open for contracted work | C++/BP (incl. Multiplayer) | Tutoring | VR

    My UE4 Blog/Page with Tutorials and more: Hit me for ALL the things!
    (Including 100+ Pages Multiplayer Network Compendium to get you started.)

    #2
    Good start. I feel that it would be easier if we talked about it in a more bare bones way so that it's clearly understood what exactly is required vs optional.

    Dependencies (in [Project].build.cs):
    Public: "OnlineSubsystem", "OnlineSubsystemUtils", "OnlineSubsystemNull".
    Dynamic: "OnlineSubsystemNull".
    Can substitute null with Steam if that's what you want.

    In DefaultEngine.ini:
    Code:
    [OnlineSubsystem]
    DefaultPlatformService=Null
    Can substitute null with Steam if that's what you want.

    Header include:
    #include "OnlineSubsystemUtils.h"
    Put this wherever you deal with the Online Subsystem directly.

    For creating a game this is what I did for creating sessions:

    .h
    Code:
    FOnCreateSessionCompleteDelegate CreateSessionCompleteD;
    FDelegateHandle CreateSessionCompleteDH;
    void CreateSessionComplete(FName SessionName, bool bWasSuccessful);
    FOnlineSessionSettings SessionSettings;
    .cpp
    Code:
    void ANetworkTest_GameSession::BeginPlay()
    {
    	IOnlineSubsystem* OSInst = IOnlineSubsystem::Get();
    	IOnlineSessionPtr SessionInst = OSInst->GetSessionInterface();
    
    	CreateSessionCompleteD = FOnCreateSessionCompleteDelegate::CreateUObject(this, &ANetworkTest_GameSession::CreateSessionComplete);
    	CreateSessionCompleteDH = SessionInst->AddOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteD);
    }
    
    void ANetworkTest_GameSession::Host()
    {
    	SessionSettings.bIsLANMatch = true;
    	SessionSettings.bAllowJoinInProgress = true;
    	SessionSettings.bUsesPresence = true;   // What is presence?
    	SessionSettings.bShouldAdvertise = true;
    
    	IOnlineSubsystem* OSInst = IOnlineSubsystem::Get();
    	IOnlineSessionPtr SessionInst = OSInst->GetSessionInterface();
    
    	// If session isn't created then one might already exist so try to destroy it.	
    	if (SessionInst->CreateSession(0, "cheese", SessionSettings))
    		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, "Yes: Create");
    	else
    	{
    		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, "No: Create");
    
    		if (SessionInst->DestroySession("cheese"))
    			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, "Yes: Delete");
    		else
    			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, "No: Delete");
    	}
    }
    
    void ANetworkTest_GameSession::CreateSessionComplete(FName SessionName, bool bWasSuccessful)
    {
    	if (bWasSuccessful)
    		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, "Pass: Create");
    	else
    		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, "Fail: Create");
    
    	UGameplayStatics::OpenLevel(GetWorld(), "Sandbox", true, "listen");
    }
    I've tried to keep it as simple as possible so I know what's required. My Host function for example takes no parameters and this makes it clear that it's not directly an important function.

    Edit:

    I've asked before, but hopefully someone who knows will see it here:

    I'm hoping someone could tell me why setting a delegate wasn't made as simple as MySessionPtr->CreateSession(0, "SessionName", this, &MyClass::CallThisWhenCreated, &MyClass::CallThisWhenFailed). The advantage here is that I don't need to remember 4 or so extra steps for Unreal's custom delegate system.

    Also the way we set the game session is quite odd. Why isn't it as simple as setting a variable for like DefaultPawnClass?
    Last edited by MaxL; 07-29-2015, 10:51 AM.

    Comment


      #3
      That's pretty similar, if not the same to how i've done it. I only added the "Start Session" part.
      If someone knows for what we need the "StartSession" thing, then i would be happy (:

      @MaxL: Would be nice if you could keep general delegate questions away from here. I want to focus on the session system (:
      Open for contracted work | C++/BP (incl. Multiplayer) | Tutoring | VR

      My UE4 Blog/Page with Tutorials and more: Hit me for ALL the things!
      (Including 100+ Pages Multiplayer Network Compendium to get you started.)

      Comment


        #4
        Added "Finding Sessions" and also added comments to "creating a session". And i changed some code of the "Creating a Sessions" Parts.
        This is all not final, just gathering. The final thing will be in the wiki and will contain way more information and help. Please keep this in mind!
        Open for contracted work | C++/BP (incl. Multiplayer) | Tutoring | VR

        My UE4 Blog/Page with Tutorials and more: Hit me for ALL the things!
        (Including 100+ Pages Multiplayer Network Compendium to get you started.)

        Comment


          #5
          Question:
          Code:
          SessionSearch = MakeShareable(new FOnlineSessionSearch());
          this line was giving me a lot of trouble yesterday, looks like Unreal did not like the way that I declared it.
          I used:
          Code:
          TSharedPtr<class FOnlineSessionSearch> SessionSearch;
          I don't remember exactly what the error was, but looks like Unreal wasn't a big fan of it.

          Comment


            #6
            Hey there (:

            i took this out of the ShooterGame. TSharedPtr have some extra things to them. You are better of reading about it here:

            https://docs.unrealengine.com/latest...ary/index.html

            https://docs.unrealengine.com/latest...Ptr/index.html

            https://docs.unrealengine.com/latest...ter/index.html

            I'm not that good in these extra pointers from epic, but the "MakeShareable" is the way to initialize the a new "object" for that TSharedPtr i guess (:
            Open for contracted work | C++/BP (incl. Multiplayer) | Tutoring | VR

            My UE4 Blog/Page with Tutorials and more: Hit me for ALL the things!
            (Including 100+ Pages Multiplayer Network Compendium to get you started.)

            Comment


              #7
              Thanks, I'll look deeper into it. But thank you a ton for coming up with this amazing tutorial! Feels like my life will be much easier now

              Comment


                #8
                Its not ready yet though. There is missing a ton and it is also not yet a tutorial xD
                Just writing down what i learn. Will either continue this night or tomorrow.
                Open for contracted work | C++/BP (incl. Multiplayer) | Tutoring | VR

                My UE4 Blog/Page with Tutorials and more: Hit me for ALL the things!
                (Including 100+ Pages Multiplayer Network Compendium to get you started.)

                Comment


                  #9
                  I've looked trough it, looks like you guys might have forgotten to include few things:

                  First:
                  In MyGame.Build.cs you have to include:
                  Code:
                   PublicDependencyModuleNames.AddRange(
                  			new string[] {
                  				"Core",
                  				"CoreUObject",
                  				"Engine",
                  				"OnlineSubsystem",
                  				"OnlineSubsystemUtils",
                  			}
                  		);
                  *Make sure to regenerate project files after adding this*

                  To essentially include OnlineSubststem to project.

                  Second
                  Thanks to MaxL, I know that.

                  Overload GetGameSessionClass() function in AGameMode class for game mode to actually use your game session (If you're extending AGameSession class):
                  Code:
                  TSubclassOf<AGameSession> MyGameGameMode::GetGameSessionClass() const
                  {
                  	return MyGameGameSession::StaticClass();
                  }
                  Please correct me if I'm wrong.
                  Last edited by Vadim Osipov; 07-29-2015, 09:00 PM.

                  Comment


                    #10
                    No you are correct, but you need to read my text correctly:

                    Originally posted by eXi
                    Where do we start?

                    We will do this step by step, starting with Creating a session. [...] Once we finished the session thing, i will create a Wiki page with all information, the whole
                    code project and small code parts that i explain. I will also add the part that you need to do before even using sessions (Adding OnlineSubsystem = NULL etc).
                    As i stated, the includes etc are not listed and i will add them with the wiki entry. They are not important for me at this point, since they are pretty straight forward.
                    This thread is only for the sessions "nodes".

                    The second is pretty cool to know. Thanks for sharing that. This will fit into the page where we gather information about extending the system. Thanks!
                    Open for contracted work | C++/BP (incl. Multiplayer) | Tutoring | VR

                    My UE4 Blog/Page with Tutorials and more: Hit me for ALL the things!
                    (Including 100+ Pages Multiplayer Network Compendium to get you started.)

                    Comment


                      #11
                      eXi, my bad I should have read it properly.

                      Comment


                        #12
                        So, let me say it in a picture, combined with 2 "words":

                        WUB WUB!



                        Will update the main post with the code to join a session.

                        Then only destroying is left!

                        EDIT: Updated it. Will make a break and do the destroying later.
                        Last edited by eXi; 07-30-2015, 07:16 AM.
                        Open for contracted work | C++/BP (incl. Multiplayer) | Tutoring | VR

                        My UE4 Blog/Page with Tutorials and more: Hit me for ALL the things!
                        (Including 100+ Pages Multiplayer Network Compendium to get you started.)

                        Comment


                          #13
                          Hi guys,

                          I'm having trouble getting both creating and finding sessions to work in C++ at the same time.
                          Whenever I'm doing one of that via blueprint and the other one via C++, it work's just fine, so for example creating session via blueprint and finding via c++ works perfect, as well as creating via c++ and finding via blueprint.

                          This is a mystery for me, because this tells me that my c++ functions are actually working just fine.

                          This is what I did:

                          1. Creating a UMyGameInstance and setting that as default game instance in the project settings.
                          2. UMyGameInstance.h as follows:


                          Code:
                          public:
                          	UMyGameInstance(const FObjectInitializer& ObjectInitializer);
                          
                          	// Test button click
                          	UFUNCTION(BlueprintCallable, Category = "C++ Functions")
                          	void TestButton();
                          
                          	// Test button click
                          	UFUNCTION(BlueprintCallable, Category = "C++ Functions")
                          	void TestButtonHost();
                          
                          	// Test button delegates
                          	void TestButtonDelegate1(bool bWasSuccessful);
                          	void TestButtonHostDelegate1(FName SessionName, bool bWasSuccessful);
                          
                          	FOnFindSessionsCompleteDelegate OnFindSessionsCompleteDelegate;
                          	FDelegateHandle OnFindSessionsCompleteDelegateHandle;
                          
                          	FOnCreateSessionCompleteDelegate OnCreateSessionCompleteDelegate;
                          	FDelegateHandle OnCreateSessionCompleteDelegateHandle;
                          
                          	TSharedPtr<class FOnlineSessionSearch> SearchSettings;
                          	TSharedPtr<class FOnlineSessionSettings> HostSettings;
                          3. UMyGameInstance.cpp as follows:

                          Code:
                          UMyGameInstance::UMyGameInstance(const FObjectInitializer& ObjectInitializer)
                          	: Super(ObjectInitializer)
                          {
                          	// create Online session delegates:
                          	OnFindSessionsCompleteDelegate = FOnFindSessionsCompleteDelegate::CreateUObject(this, &UMyGameInstance::TestButtonDelegate1);
                          	OnCreateSessionCompleteDelegate = FOnCreateSessionCompleteDelegate::CreateUObject(this, &UMyGameInstance::TestButtonHostDelegate1);
                          }
                          
                          
                          void UMyGameInstance::TestButton()
                          {
                          	IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
                          	if (OnlineSub)
                          	{
                          		IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
                          		if (Sessions.IsValid())
                          		{
                          			SearchSettings = MakeShareable(new FOnlineSessionSearch());
                          
                          			SearchSettings->bIsLanQuery = true;
                          			SearchSettings->MaxSearchResults = 10;
                          
                          			SearchSettings->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
                          
                          			// Set search delegate:
                          			OnFindSessionsCompleteDelegateHandle = Sessions->AddOnFindSessionsCompleteDelegate_Handle(OnFindSessionsCompleteDelegate);
                          
                          			TSharedPtr<class FUniqueNetId> NetId = GetWorld()->GetFirstPlayerController()->PlayerState->UniqueId.GetUniqueNetId();
                          
                          			// start searching for sessions:
                          			Sessions->FindSessions(*NetId, SearchSettings.ToSharedRef());
                          		}
                          	}
                          }
                          
                          void UMyGameInstance::TestButtonHost()
                          {
                          	IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
                          	if (OnlineSub)
                          	{
                          		IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
                          		if (Sessions.IsValid())
                          		{
                          			HostSettings = MakeShareable(new FOnlineSessionSettings());
                          
                          			HostSettings->NumPublicConnections = 2;
                          			HostSettings->bShouldAdvertise = true;
                          			HostSettings->bAllowJoinInProgress = true;
                          			HostSettings->bIsLANMatch = true;
                          			HostSettings->bUsesPresence = true;
                          			HostSettings->bAllowJoinViaPresence = true;
                          
                          			// Set complete delegate:
                          			OnCreateSessionCompleteDelegateHandle = Sessions->AddOnCreateSessionCompleteDelegate_Handle(OnCreateSessionCompleteDelegate);
                          
                          			// start creating a session:
                          			FName SessionName = FName("Test");
                          			bool Status = Sessions->CreateSession(0, SessionName, *HostSettings);
                          
                          			if (Status) GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("UMyGameInstance::TestButtonHost Create OK"));
                          		}
                          	}
                          }
                          
                          void UMyGameInstance::TestButtonDelegate1(bool bWasSuccessful)
                          {
                          	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("UMyGameInstance::TestButtonDelegate1"));
                          
                          	IOnlineSubsystem* const OnlineSub = IOnlineSubsystem::Get();
                          	if (OnlineSub)
                          	{
                          		IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
                          		if (Sessions.IsValid())
                          		{
                          
                          			Sessions->ClearOnFindSessionsCompleteDelegate_Handle(OnFindSessionsCompleteDelegateHandle);
                          
                          			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Num Search Results: " + FString::FromInt(SearchSettings->SearchResults.Num())));
                          		}
                          	}
                          }
                          
                          void UMyGameInstance::TestButtonHostDelegate1(FName SessionName, bool bWasSuccessful)
                          {
                          	if (bWasSuccessful)
                          		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("UMyGameInstance::TestButtonHostDelegate1 Name: " + SessionName.ToString()));
                          	else
                          		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("UMyGameInstance::TestButtonHostDelegate1 ERROR"));
                          
                          	IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
                          	if (OnlineSub)
                          	{
                          		IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
                          		Sessions->ClearOnCreateSessionCompleteDelegate_Handle(OnCreateSessionCompleteDelegateHandle);
                          
                          		//StartCompleteDelegateHandle = Sessions->AddOnStartSessionCompleteDelegate_Handle(StartCompleteDelegate);
                          		Sessions->StartSession(SessionName); // for testing
                          	}
                          }
                          4. Added two button which just calls UMyGameInstance::TestButton() and UMyGameInstance::TestButtonHost()
                          5. in my project build cs:
                          Code:
                                  // Uncomment if you are using online features
                          	PrivateDependencyModuleNames.Add("OnlineSubsystem");
                          
                                  // Adding Subsystem utils:
                                  PublicDependencyModuleNames.AddRange(new string[] { "OnlineSubsystem", "OnlineSubsystemUtils" });
                          
                                  // Adding Subsystem service implementation:
                                  DynamicallyLoadedModuleNames.Add("OnlineSubsystemSteam");
                                  DynamicallyLoadedModuleNames.AddRange(new string[] { "OnlineSubsystemNull" });
                          6. In my DefaultEngine.ini:
                          Code:
                          [/Script/Engine.GameEngine]
                          +NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
                          
                          [OnlineSubsystem]
                          ;DefaultPlatformService=Null
                          DefaultPlatformService=Steam
                          PollingIntervalInMs=20
                          
                          [OnlineSubsystemSteam]
                          bEnabled=true
                          SteamDevAppId=480
                          bVACEnabled=0
                          
                          [/Script/OnlineSubsystemSteam.SteamNetDriver]
                          NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"
                          
                          [OnlineSubsystemNull]
                          And that's it - Both creating and finding sessions in C++ works, but not at the same time. But when i either create a session or search for sessions via blueprint, the respective C++ counterpart works perfect.
                          For testing i simply use two PIE windows: one for hosting, one for joining.


                          eXi, can you maybe send me your project so I can check that out? Hopefully I find my problem here and can contribute to the documentation.

                          Greetings!
                          Last edited by Xetatronics; 07-30-2015, 10:57 AM.

                          Comment


                            #14
                            Originally posted by Xetatronics View Post
                            eXi, can you maybe send me your project so I can check that out? Hopefully I find my problem here and can contribute to the documentation.

                            Greetings!
                            Yes sure, but i would like to finish this first and tidy it up, so i can directly upload it for everyone. Do you have some time to spent on other things
                            until i finish this here?
                            Open for contracted work | C++/BP (incl. Multiplayer) | Tutoring | VR

                            My UE4 Blog/Page with Tutorials and more: Hit me for ALL the things!
                            (Including 100+ Pages Multiplayer Network Compendium to get you started.)

                            Comment


                              #15
                              Questions for eXi so far:
                              First:
                              On OnStartOnlineGameComplete() delegate you use GetWorld()->ServerTravel("SomeMapURL") which sends Hosting Client to desired map and kicks off Game Session.
                              For OnJoinSessionComplete() you use PlayerController->ClientTravel(TravelURL, ETravelType::TRAVEL_Absolute); which sends client to connect to the Hosting Client.

                              Hosting Client is a Server at that time, or I'm getting that wrong?
                              I guess my main question is: Would that logic work with Dedicated Server?

                              Second
                              In ShooterGame example they use something like this
                              Code:
                              ABWGameSession* UBWGameInstance::GetGameSession() const
                              {
                              	UWorld* const World = GetWorld();
                              	if (World)
                              	{
                              		AGameMode* const Game = World->GetAuthGameMode();
                              		if (Game)
                              		{
                              			return Cast<ABWGameSession>(Game->GameSession);
                              		}
                              		else
                              		{
                              			UE_LOG(LogTemp, Warning, TEXT("Game Instance - GetGameSession - Game* is NULL"));
                              			return nullptr;
                              		}
                              	}
                              	else
                              	{
                              		UE_LOG(LogTemp, Warning, TEXT("Game Instance - GetGameSession - World* is NULL"));
                              		return nullptr;
                              	}
                              	return nullptr;
                              }
                              to get the reference of current game session(if i'm getting it right) before they host the game. And you do not use it.
                              My Question is: Do you know why they use it? What is the benefit?

                              Comment

                              Working...
                              X