TPS入门DAY01 服务器篇
1.实现代码
/* 头文件以及成员变量 */
#include "OnlineSubsystem.h"
#include "Interfaces/OnlineSessionInterface.h" // 包含委托定义
DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);
public:
/* 进入服务器的凭证,通过该凭证来加入同一个服务器 */
IOnlineSessionPtr OnlineSessionInterface;
protected:
/* 创建会话 */
UFUNCTION(BlueprintCallable)
void CreateGameSession();
/* 查找会话 */
UFUNCTION(BlueprintCallable)
void FindGameSession();
/* 创建完会话的回调函数 */
void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);
/* 查找到会话的回调函数 */
void OnFindSessionsComplete(bool bWasSuccessful);
/* 加入会话时对应的回调函数 */
void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);
private:
/* 创建完成会话的委托 */
FOnCreateSessionCompleteDelegate OnCreateSessionCompleteDelegate;
/* 查找完成会话的委托 */
FOnFindSessionsCompleteDelegate OnFindSessionsCompleteDelegate;
/* 加入会话时的委托 */
FOnJoinSessionCompleteDelegate OnJoinSessionCompleteDelegate;
/* 存储搜索会话条件的容器,当用户通过凭证进入服务器时,并借凭证去搜索会话,将搜索会话的结果存储到该容器中的 SearchResults 中*/
TSharedPtr<class FOnlineSessionSearch> SessionSearch;
头文件完整定义
.cpp实现文件
/*.cpp文件中的头文件*/
#include "OnlineSessionSettings.h"
#include "Online/OnlineSessionNames.h"
#include "Debug/MultiDebugHelper.h"
using namespace Debug;
/*构造函数*/
// 在线会话子系统
// Steam的桥梁作用,当使用Steam子系统是时,虽然每个程序实例都有自己的OnlinSessionInterface
// 但这些接口会连接到同一个Steam SDK
// 所有使用相同Steam配置的程序会看到相同的会话列表,创建的会话会注册到Steam服务器
// 所以IOnlineSessionInterface就是访问Steam在线服务的通行证,每个程序实例都会获取自己的通行证然后连接到同一个Steam服务器
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
if (OnlineSub)
{
// 获取会话管理接口
OnlineSessionInterface = OnlineSub->GetSessionInterface();
Print(FString::Printf(TEXT("OnlineSubsystem: %s"), *OnlineSub->GetSubsystemName().ToString()));
}
OnCreateSessionCompleteDelegate = FOnCreateSessionCompleteDelegate::CreateUObject(this, &AMenuSystemCharacter::OnCreateSessionComplete);
OnFindSessionsCompleteDelegate = FOnFindSessionsCompleteDelegate::CreateUObject(this, &AMenuSystemCharacter::OnFindSessionsComplete);
OnJoinSessionCompleteDelegate = FOnJoinSessionCompleteDelegate::CreateUObject(this, &AMenuSystemCharacter::OnJoinSessionComplete);
// 创建会话
void AMenuSystemCharacter::CreateGameSession()
{
if (!OnlineSessionInterface.IsValid())
{
return;
}
// 1.搜索名为GameSession的会话并销毁
auto ExistingSession = OnlineSessionInterface->GetNamedSession(NAME_GameSession);
if (ExistingSession)
{
OnlineSessionInterface->DestroySession(NAME_GameSession);
}
// 2.绑定创建新会话完成时的委托
OnlineSessionInterface->AddOnCreateSessionCompleteDelegate_Handle(OnCreateSessionCompleteDelegate);
// 3.配置会话参数
TSharedPtr<FOnlineSessionSettings> SessionSettings = MakeShareable(new FOnlineSessionSettings());
SessionSettings->bIsLANMatch = false; // 使用互联网而非局域网
SessionSettings->NumPublicConnections = 4; // 最大玩家数
SessionSettings->bAllowJoinInProgress = true; // 允许中途加入
SessionSettings->bAllowJoinViaPresence = true; // 允许通过平台好友功能加入
SessionSettings->bShouldAdvertise = true; // 广播此会话
SessionSettings->bUsesPresence = true; // 使用平台的在线状态服务(Steam好友系统)
SessionSettings->bUseLobbiesIfAvailable = true; // 使用平台的聊天系统(Steam聊天室)
SessionSettings->Set(FName("MatchType"), FString("FreeForAll"), EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
OnlineSessionInterface->CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, *SessionSettings);
}
// 创建完会话的回调
void AMenuSystemCharacter::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{
if (bWasSuccessful)
{
Print(FString::Printf(TEXT("Session '%s' created successfully"), *SessionName.ToString()),FColor::Green);
UWorld* World = GetWorld();
if (World)
{
World->ServerTravel("/Game/ThirdPerson/Maps/Lobby?listen");
}
}
else
{
Print(FString::Printf(TEXT("Failed to create session '%s'"), *SessionName.ToString()), FColor::Red);
}
}
// 查找游戏会话
void AMenuSystemCharacter::FindGameSession()
{
if (!OnlineSessionInterface.IsValid())
{
return;
}
OnlineSessionInterface->AddOnFindSessionsCompleteDelegate_Handle(OnFindSessionsCompleteDelegate);
SessionSearch = MakeShareable(new FOnlineSessionSearch());
SessionSearch->bIsLanQuery = false;
SessionSearch->MaxSearchResults = 10000;
SessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals); // 启用Presence搜索
const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
// 提交会话请求
OnlineSessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), SessionSearch.ToSharedRef());
}
// 找到会话的回调
void AMenuSystemCharacter::OnFindSessionsComplete(bool bWasSuccessful)
{
if (!OnlineSessionInterface.IsValid())
{
return;
}
for (auto Result : SessionSearch->SearchResults)
{
FString ID = Result.GetSessionIdStr();
FString User = Result.Session.OwningUserName;
FString MatchType;
Result.Session.SessionSettings.Get(FName("MatchType"), MatchType);
Print(FString::Printf(TEXT("Id: %s, User: %s!"), *ID, *User));
if (MatchType == FString("FreeForAll"))
{
Print(FString::Printf(TEXT("Joining Match Type '%s'"), *MatchType));
}
OnlineSessionInterface->AddOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegate); // 添加委托到会话接口的委托列表
const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); //获取本地玩家指针
// 第一个输入参数类型为 const FUniqueNetId &SearchingPlayerId
// 第二个输入参数类型为 Fname SessionName
// 第三个输入参数类型为 const FOnlineSessionSearchResult &DesiredSession,这里写 Result 即可
OnlineSessionInterface->JoinSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, Result); // 调用在线接口函数加入会话
}
}
// 会话加入成功的回调
void AMenuSystemCharacter::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
if (!OnlineSessionInterface.IsValid())
return;
Print(TEXT("Session join complete"));
FString Address;
if (OnlineSessionInterface->GetResolvedConnectString(NAME_GameSession, Address))
{
Print(FString::Printf(TEXT("Connect string %s"), *Address));
APlayerController* PlayerController = GetGameInstance()->GetFirstLocalPlayerController();
if (PlayerController)
{
PlayerController->ClientTravel(Address, ETravelType::TRAVEL_Absolute);
}
}
}
打印代码
#pragma once
#include "CoreMinimal.h"
/**
*
*/
namespace Debug
{
static void Print(const FString& Message, FColor Color = FColor::Blue)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, Color, Message);
UE_LOG(LogTemp, Warning, TEXT("%s"), *Message);
}
}
};
2.实现细节
/*DefaultEngine.ini*/
[/Script/Engine.GameEngine]
!NetDriverDefinitions=ClearArray
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
[OnlineSubsystem]
DefaultPlatformService=Steam
[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480
bInitServerOnClient=true
[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"
两台电脑,两个Steam账号,双方互相为好友,且下载地区一直,例如我是中国-上海,联机的账号也必须是中国-上海,此外好友双方必须为在线状态;
3.蓝图
4.具体逻辑
就以微信群聊打比方吧,把会话比作微信群聊,steam通行证OnlineSessionInterface就是微信;
然后在微信中创建了群聊(会话),创建群聊成功后,你会跳转到群聊界面,其中创建群聊会有一系列设置,比如群聊名称(gamesession),仅允许好友访问又比如需要回答问题才能加入群聊,并且设置了这个群聊的键值对组合。
搜索群聊的时候也是根据这些信息去匹配搜索到的群聊,如果群聊的Result.Session.SessionSettings.Get(FName("MatchType"), MatchType);
即群聊名字为gamesession的键(Key)对应的value就是FreeForAll说明该会话查找成功,下一步就是申请加入群聊了,通过获取群聊名称为gamesession的地址,来加入群聊
大致逻辑就是这些