Added MultiplayerSessions plugin

This commit is contained in:
Kingsmedia 2022-04-27 19:54:30 +02:00 committed by Bert
parent 1ae803ce06
commit 92f264a78f
11 changed files with 609 additions and 0 deletions

1
.gitignore vendored
View File

@ -51,6 +51,7 @@ SourceArt/**/*.tga
# Binary Files # Binary Files
Binaries/* Binaries/*
Plugins/*/Binaries/* Plugins/*/Binaries/*
Plugins/Developer/
# Builds # Builds
Build/* Build/*

Binary file not shown.

View File

@ -0,0 +1,34 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "MultiplayerSessions",
"Description": "A plugin for handling online multiplayer sessions",
"Category": "Other",
"CreatedBy": "Bert",
"CreatedByURL": "",
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": true,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false,
"Modules": [
{
"Name": "MultiplayerSessions",
"Type": "Runtime",
"LoadingPhase": "Default"
}
],
"Plugins": [
{
"Name": "OnlineSubsystem",
"Enabled": true
},
{
"Name": "OnlineSubsystemSteam",
"Enabled": true
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,58 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class MultiplayerSessions : ModuleRules
{
public MultiplayerSessions(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"OnlineSubsystem",
"OnlineSubsystemSteam",
"UMG",
"Slate",
"SlateCore"
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore"
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}

View File

@ -0,0 +1,186 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Menu.h"
#include "Components/Button.h"
#include "MultiplayerSessionsSubsystem.h"
#include "OnlineSessionSettings.h"
#include "OnlineSubsystem.h"
void UMenu::MenuSetup(int32 NumberOfPublicConnections, FString TypeOfMatch)
{
NumPublicConnections = NumberOfPublicConnections;
MatchType = TypeOfMatch;
AddToViewport();
SetVisibility(ESlateVisibility::Visible);
bIsFocusable = true;
UWorld* World = GetWorld();
if (World)
{
APlayerController* PlayerController = World->GetFirstPlayerController();
if (PlayerController)
{
FInputModeUIOnly InputModeData;
InputModeData.SetWidgetToFocus(TakeWidget());
InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
PlayerController->SetInputMode(InputModeData);
PlayerController->SetShowMouseCursor(true);
}
}
UGameInstance* GameInstance = GetGameInstance();
if (GetGameInstance())
{
MultiplayerSessionsSubsystem = GameInstance->GetSubsystem<UMultiplayerSessionsSubsystem>();
}
if (MultiplayerSessionsSubsystem != nullptr)
{
MultiplayerSessionsSubsystem->MultiplayerOnCreateSessionComplete.AddDynamic(this, &ThisClass::OnCreateSession);
MultiplayerSessionsSubsystem->MultiplayerOnFindSessionsComplete.AddUObject(this, &ThisClass::OnFindSessions);
MultiplayerSessionsSubsystem->MultiplayerOnJoinSessionComplete.AddUObject(this, &ThisClass::OnJoinSession);
MultiplayerSessionsSubsystem->MultiplayerOnDestroySessionComplete.AddDynamic(this, &ThisClass::OnDestroySession);
MultiplayerSessionsSubsystem->MultiplayerOnStartSessionComplete.AddDynamic(this, &ThisClass::OnStartSession);
}
}
bool UMenu::Initialize()
{
if (!Super::Initialize())
{
return false;
}
if (HostButton)
{
HostButton->OnClicked.AddDynamic(this, &UMenu::HostButtonClicked);
}
if (JoinButton)
{
JoinButton->OnClicked.AddDynamic(this, &UMenu::JoinButtonClicked);
}
return true;
}
void UMenu::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld)
{
MenuTearDown();
Super::OnLevelRemovedFromWorld(InLevel, InWorld);
}
void UMenu::OnCreateSession(bool bWasSuccessful)
{
if (bWasSuccessful)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(
-1,
15.f,
FColor::Green,
FString(TEXT("Session created successfully!")));
}
UWorld* World = GetWorld();
if (World)
{
World->ServerTravel(FString("/Game/ThirdPerson/Maps/Lobby?listen"));
}
}
else
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(
-1,
15.f,
FColor::Red,
FString(TEXT("Failed to create session!")));
}
}
}
void UMenu::OnFindSessions(const TArray<FOnlineSessionSearchResult>& SessionResults, bool bWasSuccessful)
{
if (MultiplayerSessionsSubsystem == nullptr)
{
return;
}
for (auto Result : SessionResults)
{
FString SettingsValue;
Result.Session.SessionSettings.Get(FName("MatchType"), SettingsValue);
if (SettingsValue == MatchType)
{
MultiplayerSessionsSubsystem->JoinSession(Result);
return;
}
}
}
void UMenu::OnJoinSession(EOnJoinSessionCompleteResult::Type Result)
{
IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get();
if (Subsystem)
{
IOnlineSessionPtr SessionInterface = Subsystem->GetSessionInterface();
if (SessionInterface.IsValid())
{
FString Address;
SessionInterface->GetResolvedConnectString(NAME_GameSession, Address);
APlayerController* PlayerController = GetGameInstance()->GetFirstLocalPlayerController();
if (PlayerController)
{
PlayerController->ClientTravel(Address, ETravelType::TRAVEL_Absolute);
}
}
}
}
void UMenu::OnDestroySession(bool bWasSuccessful)
{
}
void UMenu::OnStartSession(bool bWasSuccessful)
{
}
void UMenu::HostButtonClicked()
{
if (MultiplayerSessionsSubsystem)
{
MultiplayerSessionsSubsystem->CreateSession(NumPublicConnections, MatchType);
}
}
void UMenu::JoinButtonClicked()
{
if (MultiplayerSessionsSubsystem)
{
MultiplayerSessionsSubsystem->FindSessions(10000);
}
}
void UMenu::MenuTearDown()
{
RemoveFromParent();
UWorld* World = GetWorld();
if (World)
{
APlayerController* PlayerController = World->GetFirstPlayerController();
if (PlayerController)
{
FInputModeGameOnly InputModeData;
PlayerController->SetInputMode(InputModeData);
PlayerController->SetShowMouseCursor(false);
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "MultiplayerSessions.h"
#define LOCTEXT_NAMESPACE "FMultiplayerSessionsModule"
void FMultiplayerSessionsModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FMultiplayerSessionsModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FMultiplayerSessionsModule, MultiplayerSessions)

View File

@ -0,0 +1,153 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MultiplayerSessionsSubsystem.h"
#include "OnlineSubsystem.h"
#include "OnlineSessionSettings.h"
UMultiplayerSessionsSubsystem::UMultiplayerSessionsSubsystem():
CreateSessionCompleteDelegate(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete)),
FindSessionsCompleteDelegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnFindSessionsComplete)),
JoinSessionCompleteDelegate(FOnJoinSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnJoinSessionComplete)),
DestroySessionCompleteDelegate(FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnDestroySessionComplete)),
StartSessionCompleteDelegate(FOnStartSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnStartSessionComplete))
{
IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get();
if (Subsystem)
{
SessionInterface = Subsystem->GetSessionInterface();
}
}
void UMultiplayerSessionsSubsystem::CreateSession(int32 NumPublicConnections, FString MatchType)
{
if (!SessionInterface.IsValid())
{
return;
}
auto ExistingSession = SessionInterface->GetNamedSession(NAME_GameSession);
if (ExistingSession != nullptr)
{
SessionInterface->DestroySession(NAME_GameSession);
}
// Store the delegate in a FDelegateHandle so we can later remove it from the delegate list
CreateSessionCompleteDelegateHandle = SessionInterface->AddOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegate);
LastSessionSettings = MakeShareable(new FOnlineSessionSettings());
LastSessionSettings->bIsLANMatch = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false;
LastSessionSettings->NumPublicConnections = NumPublicConnections;
LastSessionSettings->bAllowJoinInProgress = true;
LastSessionSettings->bAllowJoinViaPresence = true;
LastSessionSettings->bShouldAdvertise = true;
LastSessionSettings->bUsesPresence = true;
LastSessionSettings->bUseLobbiesIfAvailable = true; // For UE5 when not finding sessions
LastSessionSettings->Set(FName("MatchType"), FString(MatchType), EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
LastSessionSettings->BuildUniqueId = 1;
const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
if (!SessionInterface->CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, *LastSessionSettings))
{
SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegateHandle);
// Broadcast our own custom delegate
MultiplayerOnCreateSessionComplete.Broadcast(false);
}
}
void UMultiplayerSessionsSubsystem::FindSessions(int32 MaxSearchResults)
{
if (!SessionInterface.IsValid())
{
return;
}
FindSessionCompleteDelegateHandle = SessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate);
LastSessionSearch = MakeShareable(new FOnlineSessionSearch);
LastSessionSearch->MaxSearchResults = MaxSearchResults;
LastSessionSearch->bIsLanQuery = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false;
LastSessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
if (!SessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), LastSessionSearch.ToSharedRef()))
{
SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionCompleteDelegateHandle);
MultiplayerOnFindSessionsComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);
}
}
void UMultiplayerSessionsSubsystem::JoinSession(const FOnlineSessionSearchResult& SessionResult)
{
if (!SessionInterface.IsValid())
{
MultiplayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
return;
}
JoinSessionCompleteDelegateHandle = SessionInterface->AddOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegate);
const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
if (!SessionInterface->JoinSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, SessionResult))
{
SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegateHandle);
MultiplayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
}
}
void UMultiplayerSessionsSubsystem::DestroySession()
{
}
void UMultiplayerSessionsSubsystem::StartSession()
{
}
void UMultiplayerSessionsSubsystem::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{
if (SessionInterface)
{
SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegateHandle);
}
MultiplayerOnCreateSessionComplete.Broadcast(bWasSuccessful);
}
void UMultiplayerSessionsSubsystem::OnFindSessionsComplete(bool bWasSuccessful)
{
if (SessionInterface)
{
SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionCompleteDelegateHandle);
}
if (LastSessionSearch->SearchResults.Num() <= 0)
{
MultiplayerOnFindSessionsComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);
return;
}
MultiplayerOnFindSessionsComplete.Broadcast(LastSessionSearch->SearchResults, bWasSuccessful);
}
void UMultiplayerSessionsSubsystem::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
if (SessionInterface)
{
SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegateHandle);
}
MultiplayerOnJoinSessionComplete.Broadcast(Result);
}
void UMultiplayerSessionsSubsystem::OnDestroySessionComplete(FName SessionName, bool bWasSuccessful)
{
}
void UMultiplayerSessionsSubsystem::OnStartSessionComplete(FName SessionNAme, bool bWasSuccessful)
{
}

View File

@ -0,0 +1,61 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "Menu.generated.h"
/**
*
*/
UCLASS()
class MULTIPLAYERSESSIONS_API UMenu : public UUserWidget
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void MenuSetup(int32 NumberOfPublicConnections = 4, FString TypeOfMatch = FString(TEXT("FreeForAll")));
protected:
virtual bool Initialize() override;
virtual void OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld) override;
/*
* Callbacks fot the custom delegates on the MultiplayerSessionsSubsystem
*/
UFUNCTION()
void OnCreateSession(bool bWasSuccessful);
void OnFindSessions(const TArray<FOnlineSessionSearchResult>& SessionResults, bool bWasSuccessful);
void OnJoinSession(EOnJoinSessionCompleteResult::Type Result);
UFUNCTION()
void OnDestroySession(bool bWasSuccessful);
UFUNCTION()
void OnStartSession(bool bWasSuccessful);
private:
UPROPERTY(meta=(BindWidget))
class UButton* HostButton;
UPROPERTY(meta=(BindWidget))
UButton* JoinButton;
UFUNCTION()
void HostButtonClicked();
UFUNCTION()
void JoinButtonClicked();
void MenuTearDown();
// The subsystem designed to handle all online session functionality
class UMultiplayerSessionsSubsystem* MultiplayerSessionsSubsystem;
int32 NumPublicConnections {4};
FString MatchType {TEXT("FreeForAll")};
};

View File

@ -0,0 +1,15 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FMultiplayerSessionsModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View File

@ -0,0 +1,81 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "OnlineSessionSettings.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "MultiplayerSessionsSubsystem.generated.h"
// Declaring our own custom delegates for the Menu class to bind callbacks to
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiplayerOnCreateSessionComplete, bool, bWasSuccessful);
DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiplayerOnFindSessionsComplete, const TArray<FOnlineSessionSearchResult>& SessionResults, bool bWasSuccessful);
DECLARE_MULTICAST_DELEGATE_OneParam(FMultiplayerOnJoinSessionComplete, EOnJoinSessionCompleteResult::Type Result)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiplayerOnDestroySessionComplete, bool, bWasSuccessful);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiplayerOnStartSessionComplete, bool, bWasSuccessful);
/**
*
*/
UCLASS()
class MULTIPLAYERSESSIONS_API UMultiplayerSessionsSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
UMultiplayerSessionsSubsystem();
/*
* To handle session functionality. The Menu class will call these
*/
void CreateSession(int32 NumPublicConnections, FString MatchType);
void FindSessions(int32 MaxSearchResults);
void JoinSession(const FOnlineSessionSearchResult& SessionResult);
void DestroySession();
void StartSession();
/*
* Our own custom delegates for the Menu class to bind callbacks to
*/
FMultiplayerOnCreateSessionComplete MultiplayerOnCreateSessionComplete;
FMultiplayerOnFindSessionsComplete MultiplayerOnFindSessionsComplete;
FMultiplayerOnJoinSessionComplete MultiplayerOnJoinSessionComplete;
FMultiplayerOnDestroySessionComplete MultiplayerOnDestroySessionComplete;
FMultiplayerOnStartSessionComplete MultiplayerOnStartSessionComplete;
protected:
/*
* Internal callbacks for the delegates we'll add to the Online Session Interface delegate list.
* These don't need to be called outside this class
*/
void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);
void OnFindSessionsComplete(bool bWasSuccessful);
void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);
void OnDestroySessionComplete(FName SessionName, bool bWasSuccessful);
void OnStartSessionComplete(FName SessionNAme, bool bWasSuccessful);
private:
IOnlineSessionPtr SessionInterface;
TSharedPtr<FOnlineSessionSettings> LastSessionSettings;
TSharedPtr<FOnlineSessionSearch> LastSessionSearch;
/*
* To add to the Online Session Interface delegate list.
* We'll bind our MultiplayerSessionSubsystem internal callbacks to these.
*/
FOnCreateSessionCompleteDelegate CreateSessionCompleteDelegate;
FDelegateHandle CreateSessionCompleteDelegateHandle;
FOnFindSessionsCompleteDelegate FindSessionsCompleteDelegate;
FDelegateHandle FindSessionCompleteDelegateHandle;
FOnJoinSessionCompleteDelegate JoinSessionCompleteDelegate;
FDelegateHandle JoinSessionCompleteDelegateHandle;
FOnDestroySessionCompleteDelegate DestroySessionCompleteDelegate;
FDelegateHandle DestroySessionCompleteDelegateHandle;
FOnStartSessionCompleteDelegate StartSessionCompleteDelegate;
FDelegateHandle StartSessionCompleteDelegateHandle;
};