언리얼5

[UE5 Multiplayer Shooting-4] UE 멀티 플레이어 플러그인 생성 02

TIN9 2023. 7. 27.
반응형

이전 포스팅

지난번 포스팅에서 세션의 동작 순서를 아래와 같다고 설명하였다.

Server
ClICK HOST ->(Session Settings) CREATE SESSION -> OPEN LOBBY

Client
CLICK JOIN ->(Search Settings) FIND SESSIONS -> (Pick a Valid Session) JOIN SESSION ->(Get the address) CLIENT TRAVEL

 

여기서 지난번 포스팅에서 구현한 부분은 Server에서의 CREATE SESSION까지였고, 이번 포스팅에서는 Open Lobby부터 Client의 마지막부분까지 다뤄보겠습니다.

 

멤버 함수 및 변수 갱신

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "MenuSystemCharacter.generated.h"

UCLASS(config=Game)
class AMenuSystemCharacter : public ACharacter
{
	GENERATED_BODY()
    
	...
    
public:
	//  온라인 세션 인터페이스 포인터
	IOnlineSessionPtr m_OnlineSessionInterface;

protected:
	UFUNCTION(BlueprintCallable)
	void CreateGameSession();

	UFUNCTION(BlueprintCallable)
	void JoinGameSession();

	// callback함수
	void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);

	// callback함수 
	void OnFindSessionsComplete(bool bWasSuccessful);
	// Join이 되었는지에 관한 결과값이 Result에 저장
	void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);

private:
	FOnCreateSessionCompleteDelegate m_CreateSessionCompleteDelegate;
	FOnFindSessionsCompleteDelegate m_FindSessionsCompleteDelegate;

	TSharedPtr<FOnlineSessionSearch> m_SessionSearch;
	FOnJoinSessionCompleteDelegate m_JoinSessionCompleteDelegate;

};

CallbackFunc

AMenuSystemCharacter::AMenuSystemCharacter() :
	m_CreateSessionCompleteDelegate(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete)),
	m_FindSessionsCompleteDelegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnFindSessionsComplete)),
	m_JoinSessionCompleteDelegate(FOnJoinSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnJoinSessionComplete))

1. OPEN LOBBY

이전 포스팅에서 CreateSEssionComplete콜백함수를 m_CreateSessionCompleteDelegate에 등록해놨습니다.

해당 콜백함수는 세션을 생성했을때 동작되는 콜백함수로 세션 생성에 성공했다면 ServerTravel함수를 이용하여 Lobby Level을 리슨서버로 여는 코드를 추가해줍니다.

void AMenuSystemCharacter::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{
	if (bWasSuccessful)
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(
				-1,
				15.f,
				FColor::Blue,
				FString::Printf(TEXT("Created session : %s"), *SessionName.ToString())
			);
		}

		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!"))
			);
		}
	}
}

2. CLICK JOIN

1. 인터페이스의 Find델리게이트리스트에 m_FIndSessionsCompleteDelegate를 저장

2. 탐색할 세션의 정보를 설정

3. 탐색할 세션의 정보를 토대로 세션을 찾고 세션을 찾으면 콜백함수 호출

 

void AMenuSystemCharacter::JoinGameSession()
{
	// 게임 세션을 찾는다
	// 인터페이스의 데이터가 유효하지 않다면 리턴
	if (!m_OnlineSessionInterface.IsValid())
	{
		return;
	}

	if (GEngine)
	{
		GEngine->AddOnScreenDebugMessage(
			-1,
			15.f,
			FColor::Blue,
			FString(TEXT("JoinGameSession() Success"))
		);
	}

	// 델리게이트리스트에 델리게이트 저장
	m_OnlineSessionInterface->AddOnFindSessionsCompleteDelegate_Handle(m_FindSessionsCompleteDelegate);

	m_SessionSearch = MakeShareable(new FOnlineSessionSearch());
	// 세션을 찾을 개수?
	m_SessionSearch->MaxSearchResults = 10000;
	// LAN을 사용하지 않기떄문에 false
	m_SessionSearch->bIsLanQuery = false;
	// 매칭 서버를 찾기 위한 쿼리
	// Find 세션을 호출하고 세션 검색을 전달하면 쿼리 세팅을 설정해서 우리가 찾는 세션을 사용하도록 함?
	m_SessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);

	// 로컬 플레이어를 얻어와 넷ID를 얻을 수 있다.
	const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();

	// 세션을 호출하고 세션이 완료되면 응답으로 콜백함수를 호출 요청
	m_OnlineSessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), m_SessionSearch.ToSharedRef());
}

3. FIND SESSIONS

FindSessions를 하게되면 해당 세션에 대한 결과(정보)가 m_SessionSearch->SearchResults에 저장이된다.세션을 성공적으로 찾았다면 Join하기위해 JoinDelegateList에 m_JoinSessionCompleteDelegate 저장 이후 Join 진행JoinSession이 완료되면 Callback함수 호출

void AMenuSystemCharacter::OnFindSessionsComplete(bool bWasSuccessful)
{
	// 인터페이스가 유효한지
	if (!m_OnlineSessionInterface.IsValid())
		return;

	for (auto Result : m_SessionSearch->SearchResults)
	{
		// 세션 ID 문자열을 해당 함수로 얻을 수 있다.
		FString Id = Result.GetSessionIdStr();
		FString User = Result.Session.OwningUserName;
		FString MatchType;

		// 위에서 저장한 MatchType의 키값으로 저장된 Value값을 MatchType이란 FString변수에 저장
		Result.Session.SessionSettings.Get(FName("MatchType"), MatchType);

		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(
				-1,
				15.f,
				FColor::Cyan,
				FString::Printf(TEXT("Id : %s, User : %s"), *Id, *User)
			);
		}

		// IP주소를 얻어내려면 세션에 참여해야한다
		// 세션이 맞을경우 아래의 if문에 들어감
		if (MatchType == FString("FreeForAll"))
		{
			if (GEngine)
			{
				GEngine->AddOnScreenDebugMessage(
					-1,
					15.f,
					FColor::Cyan,
					FString::Printf(TEXT("Joining Match Type : %s"), *MatchType)
				);
			}

			m_OnlineSessionInterface->AddOnJoinSessionCompleteDelegate_Handle(m_JoinSessionCompleteDelegate);

			// JoinSession 호출
			const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
			// 세션을 호출하고 세션이 완료되면 응답으로 콜백함수를 호출 요청
			m_OnlineSessionInterface->JoinSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, Result);
		}
	}
}

4. JOIN SESSION / CLIENT TRAVEL

세션의 IP를 얻어와 서버에 입장하는 구간

1. InterFace의 GetResolvedConnectString 함수를 이용하여 세션의 IP주소를 얻어온다.

2. 플레이어 컨트롤러를 얻어와 ClientTravel를 이용해 서버에 입장.

void AMenuSystemCharacter::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
	if (!m_OnlineSessionInterface.IsValid())
		return;

	// 세션의 IP주소를 얻어온다.
	// 세션에 등록된 IP주소를 얻어오기 때문에 IP주소를 직접 알아내서 적을 필요가없다.
	FString Address;
	if (m_OnlineSessionInterface->GetResolvedConnectString(NAME_GameSession, Address))
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(
				-1,
				15.f,
				FColor::Yellow,
				FString::Printf(TEXT("Connect string : %s"), *Address)
			);
		}

		APlayerController* PlayerController = GetGameInstance()->GetFirstLocalPlayerController();
		if (PlayerController)
		{
			PlayerController->ClientTravel(Address, ETravelType::TRAVEL_Absolute);
		}
	}
}

 


결과

https://youtu.be/RaJs0bETSuU

 

반응형

댓글