欢迎光临
我们一直在努力

UE5多人MOBA+GAS 53、测试专属服务器打包和连接,以及配置EOS

文章目录

  • 改一下代码,运行打开编辑器
  • 打包
  • 创建和配置 EOS产品
  • 项目中配置EOS
    • 编辑器中打开插件
      • Build文件中补充模块
    • 项目设置中设置
    • 创建会话读取命令行参数
    • 创建启动脚本
    • 实现服务器端会话创建
  • 实现游戏会话类并对玩家加入和离开做出反应
  • 启用和测试 EOS

改一下代码,运行打开编辑器

将项目改完源码引擎 在这里插入图片描述 选择你源码的位置 在这里插入图片描述 预防万一,再操作一手这个 在这里插入图片描述 点Crunch.sln进去 点击运行 在这里插入图片描述 在这里插入图片描述 命名为CrunchServer.Target,并拖进vs中 在这里插入图片描述 修改一下 在这里插入图片描述 顺便再修改一下AStormCore

#if WITH_EDITOR
// 编辑器属性变更回调
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
#endif

#if WITH_EDITOR
void AStormCore::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
// 获取属性名称
FName PropertyName = PropertyChangedEvent.GetPropertyName();

// 当影响力半径改变时,更新组件
if (PropertyName == GET_MEMBER_NAME_CHECKED(AStormCore, InfluenceRadius))
{
InfluenceRange->SetSphereRadius(InfluenceRadius); // 更新球体半径
FVector DecalSize = GroundDecalComponent->DecalSize;
GroundDecalComponent->DecalSize = FVector{DecalSize.X, InfluenceRadius, InfluenceRadius}; // 更新贴花大小
}
}
#endif

在这里插入图片描述 随后再次打开,选择编辑器服务器(这步在干嘛我看不懂思密达,运行完什么也没有) 在这里插入图片描述

在这里插入图片描述 生成后切换为编辑器,然后运行 在这里插入图片描述

打包

打包中点击高级 在这里插入图片描述 在这里插入图片描述

然后设置这个 在这里插入图片描述 然后打包项目 在这里插入图片描述 再打包客户端 在这里插入图片描述

服务器打包中输入cmd回车 在这里插入图片描述

CrunchServer.exe /Game/Maps/Lobby/L_Lobby?listen?port=7780 -log 在这里插入图片描述

客户端中 在这里插入图片描述 Crunch.exe -log -window 在这里插入图片描述 如果打开后全屏了,点一下~输入r.setres 1080x720w,修改一下大小 127.0.0.1是服务器地址,也就是本地机器 7780是刚刚指定的端口号 在这里插入图片描述 请添加图片描述

创建和配置 EOS产品

点我进链接 点击开发者门户 在这里插入图片描述 随便输名字创建 在这里插入图片描述 创建产品 在这里插入图片描述 在这里插入图片描述 点击游戏服务,接受并浏览 在这里插入图片描述 直滑底部然后点击接受 在这里插入图片描述 点击产品设置 在这里插入图片描述 把几个许可证都点了 在这里插入图片描述

创建客户端 在这里插入图片描述

在这里插入图片描述 添加新的客户端策略 在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 再添加新的客户端,以及新的策略 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 添加应用程序 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 然后就不管了

项目中配置EOS

编辑器中打开插件

在这里插入图片描述

Build文件中补充模块

到Build.cs中添加

"OnlineSubsystem","OnlineSubsystemEOS","OnlineSubsystemUtils","Networking","HTTP","Json"

在这里插入图片描述

项目设置中设置

在这里插入图片描述 在这里插入图片描述 最后一行的再此生成点我 在这里插入图片描述 再创建一个服务器 在这里插入图片描述 把默认修改为游戏客户端 在这里插入图片描述

创建会话读取命令行参数

UTNetStatics添加函数

/**
* 检查当前上下文是否为会话服务器
* @param WorldContextObject 世界上下文对象
* @return 是否为服务器实例
*/

static bool IsSessionServer(const UObject* WorldContextObject);

/**
* 获取会话名称字符串常量
* @return 会话名称字符串
*/

static FString GetSessionNameStr();
/**
* 获取会话名称键值
* @return 会话名称的FName键
*/

static FName GetSessionNameKey();

/**
* 获取会话搜索ID字符串
* @return 会话搜索ID字符串
*/

static FString GetSessionSearchIdStr();
/**
* 获取会话搜索ID键值
* @return 会话搜索ID的FName键
*/

static FName GetSessionSearchIdKey();

/**
* 获取会话端口配置
* @return 网络监听端口号
*/

static int GetSessionPort();
/**
* 获取端口配置键值
* @return 端口号配置的FName键
*/

static FName GetPortKey();

/**
* 获取命令行参数(字符串)
* @param ParamName 参数名称
* @return 命令行参数值
*/

static FString GetCommandlineArgAsString(const FName& ParamName);
/**
* 获取命令行参数(整数)
* @param ParamName 参数名称
* @return 参数转换为整数的值
*/

static int GetCommandlineArgAsInt(const FName& ParamName);

bool UTNetStatics::IsSessionServer(const UObject* WorldContextObject)
{
// 是否为服务器模式
return WorldContextObject->GetWorld()->GetNetMode() == ENetMode::NM_DedicatedServer;
}

FString UTNetStatics::GetSessionNameStr()
{
return GetCommandlineArgAsString(GetSessionNameKey());
}

FName UTNetStatics::GetSessionNameKey()
{
return FName("SESSION_NAME");
}

FString UTNetStatics::GetSessionSearchIdStr()
{
return GetCommandlineArgAsString(GetSessionSearchIdKey());
}

FName UTNetStatics::GetSessionSearchIdKey()
{
return FName("SESSION_SEARCH_ID");
}

int UTNetStatics::GetSessionPort()
{
return GetCommandlineArgAsInt(GetPortKey());
}

FName UTNetStatics::GetPortKey()
{
return FName("PORT");
}

FString UTNetStatics::GetCommandlineArgAsString(const FName& ParamName)
{
FString OutVal = "";
FString CommandLineArg = FString::Printf(TEXT("%s="), *(ParamName.ToString()));
FParse::Value(FCommandLine::Get(), *CommandLineArg, OutVal);
return OutVal;
}

int32 UTNetStatics::GetCommandlineArgAsInt(const FName& ParamName)
{
int32 OutVal = 0;
FString CommandLineArg = FString::Printf(TEXT("%s="), *(ParamName.ToString()));
FParse::Value(FCommandLine::Get(), *CommandLineArg, OutVal);
return OutVal;
}

UMGameInstance到游戏实例中

// 游戏实例初始化
virtual void Init() override;
/*************************************/
/* 会话服务器功能 */
/*************************************/
private:
// 创建会话
void CreateSession();
// 服务器会话名称
FString ServerSessionName;
// 会话服务器端口
int32 SessionServerPort;

void UMGameInstance::Init()
{
Super::Init();
// 编辑器环境下不执行
if (GetWorld()->IsEditorWorld()) return;

// 如果是会话服务器,则创建会话
if (UTNetStatics::IsSessionServer(this))
{
CreateSession();
}
}

void UMGameInstance::CreateSession()
{
// 获取会话名称、搜索ID、会话端口
ServerSessionName = UTNetStatics::GetSessionNameStr();
FString SessionSearchId = UTNetStatics::GetSessionSearchIdStr();
SessionServerPort = UTNetStatics::GetSessionPort();

UE_LOG(LogTemp, Warning, TEXT("#### 创建会话 With Name: %s, ID: %s, Port: %d"), *(ServerSessionName), *(SessionSearchId), SessionServerPort)
}

创建启动脚本

创建两个bat 在这里插入图片描述

打开你下载的引擎源码位置 在这里插入图片描述 到这个位置,复制目录,然后把\\改为/ 在这里插入图片描述

"D:/UnrealSource/UnrealEngine/Engine/Binaries/Win64/UnrealEditor.exe" ^
%~dp0../Crunch.uproject ^
-server ^
-log ^
-epicapp="ServerClient" ^
-SESSION_NAME="TestSession" ^
-SESSION_SEARCH_ID="dsfsdfsdfsdfsdfisdfsjdfsdfisdfjsdfjdsf" ^
-PORT=7779

也可以添加一个系统环境变量 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

然后就可以变成这样launchServer.bat

%UNREAL_EDITOR% ^
%~dp0../Crunch.uproject ^
-server ^
-log ^
-epicapp="ServerClient" ^
-SESSION_NAME="TestSession" ^
-SESSION_SEARCH_ID="dsfsdfsdfsdfsdfisdfsjdfsdfisdfjsdfjdsf" ^
-PORT=7779

launchGame.bat

%UNREAL_EDITOR% ^
%~dp0../Crunch.uproject ^
-game ^
-log ^
-epicapp="GameClient" ^

实现服务器端会话创建

UTNetStatics类中

#include "OnlineSubsystem.h"
#include "OnlineSessionSettings.h"

/**
* 生成在线会话配置
* @param SessionName 会话名称标识符
* @param SessionSearchId 会话搜索ID
* @param Port 监听端口号
* @return 配置好会话设置对象
*/

static FOnlineSessionSettings GenerateOnlineSessionSettings(const FName& SessionName, const FString& SessionSearchId, int Port);

/**
* 获取在线会话接口指针
* @return 会话接口智能指针
*/

static IOnlineSessionPtr GetSessionPtr();

/**
* 获取在线身份验证接口指针
* @return 身份接口智能指针
*/

static IOnlineIdentityPtr GetIdentityPtr();

FOnlineSessionSettings UTNetStatics::GenerateOnlineSessionSettings(const FName& SessionName,
const FString& SessionSearchId, int32 Port)
{
// 创建一个会话设置对象
FOnlineSessionSettings OnlineSessionSettings{};
// 是否是局域网对战(false 表示在线匹配)
OnlineSessionSettings.bIsLANMatch = false;
// 可加入的玩家数,这里取「每队人数 * 2」
OnlineSessionSettings.NumPublicConnections = GetPlayerCountPerTeam() * 2;
// 是否向在线服务公开这个会话(可被搜索到)
OnlineSessionSettings.bShouldAdvertise = true;
// 是否使用在线状态(Presence,如好友在线显示)功能,(这是服务器没有登录用户)
OnlineSessionSettings.bUsesPresence = false;
// 是否允许通过在线状态直接加入
OnlineSessionSettings.bAllowJoinViaPresence = false;
// 是否仅允许好友通过在线状态加入
OnlineSessionSettings.bAllowJoinViaPresenceFriendsOnly = false;
// 是否允许玩家通过邀请加入
OnlineSessionSettings.bAllowInvites = true;
// 是否允许游戏进行中途加入(false = 只能在开始前加入)
OnlineSessionSettings.bAllowJoinInProgress = false;
// 是否在可用时使用 Lobbies 功能
OnlineSessionSettings.bUseLobbiesIfAvailable = false;
// 是否在 Lobbies 中启用语音聊天
OnlineSessionSettings.bUseLobbiesVoiceChatIfAvailable = false;
// 是否启用统计系统(用于战绩、匹配等统计)
OnlineSessionSettings.bUsesStats = true;

// 设置自定义键值对(附加会话元数据,用于搜索和识别)
OnlineSessionSettings.Set(GetSessionNameKey(), SessionName.ToString(), EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
OnlineSessionSettings.Set(GetSessionSearchIdKey(), SessionSearchId, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
OnlineSessionSettings.Set(GetPortKey(), Port, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);

return OnlineSessionSettings;
}

IOnlineSessionPtr UTNetStatics::GetSessionPtr()
{
IOnlineSubsystem* OnlineSubSystem = IOnlineSubsystem::Get();
if (OnlineSubSystem)
{
// 返回会话接口(IOnlineSession)
return OnlineSubSystem->GetSessionInterface();
}
return nullptr;
}

IOnlineIdentityPtr UTNetStatics::GetIdentityPtr()
{
IOnlineSubsystem* OnlineSubSystem = IOnlineSubsystem::Get();
if (OnlineSubSystem)
{
// 返回身份接口(IOnlineIdentity)
return OnlineSubSystem->GetIdentityInterface();
}
return nullptr;
}

会话创建成功加载入关卡,失败则中止 UMGameInstance

// 当会话创建完成时触发
void OnSessionCreated(FName SessionName, bool bWasSuccessful);
// 当会话结束完成时回调
void EndSessionCompleted(FName SessionName, bool bWasSuccessful);

// 终止会话服务器
void TerminateSessionServer();

// 等待玩家加入的超时计时器句柄
FTimerHandle WaitPlayerJoinTimeoutHandle;

// 等待玩家加入的超时时间(默认60秒)
UPROPERTY(EditDefaultsOnly, Category = "Session")
float WaitPlayerJoinTimeOutDuration = 60.f;
// 玩家超时未加入时触发
void WaitPlayerJoinTimeoutReached();

void UMGameInstance::CreateSession()
{
IOnlineSessionPtr SessionPtr = UTNetStatics::GetSessionPtr();
if (SessionPtr)
{
// 获取会话名称、搜索ID、会话端口
ServerSessionName = UTNetStatics::GetSessionNameStr();
FString SessionSearchId = UTNetStatics::GetSessionSearchIdStr();
SessionServerPort = UTNetStatics::GetSessionPort();

UE_LOG(LogTemp, Warning, TEXT("#### 创建会话 With Name: %s, ID: %s, Port: %d"), *(ServerSessionName), *(SessionSearchId), SessionServerPort)

// 生成会话设置
FOnlineSessionSettings OnlineSessionSettings = UTNetStatics::GenerateOnlineSessionSettings(FName(ServerSessionName), SessionSearchId, SessionServerPort);
// 先移除旧的委托,避免重复绑定
SessionPtr->OnCreateSessionCompleteDelegates.RemoveAll(this);

// 绑定本实例的回调函数(异步完成时调用)
SessionPtr->OnCreateSessionCompleteDelegates.AddUObject(this, &UMGameInstance::OnSessionCreated);
// 发起会话创建(参数:本地用户索引=0,会话名,会话设置)
if (!SessionPtr->CreateSession(0, FName(ServerSessionName), OnlineSessionSettings))
{
UE_LOG(LogTemp, Warning, TEXT("错误:会话创建立即失败!!!!"))
SessionPtr->OnCreateSessionCompleteDelegates.RemoveAll(this);
TerminateSessionServer();
}
}
else
{
// 无法获取 Session 接口(通常是 OnlineSubsystem 没初始化)
UE_LOG(LogTemp, Warning, TEXT("错误:无法获取会话接口,正在终止服务"));
TerminateSessionServer();
}
}

void UMGameInstance::OnSessionCreated(FName SessionName, bool bWasSuccessful)
{
if (bWasSuccessful)
{
UE_LOG(LogTemp, Warning, TEXT("————- 会话创建成功! ————-"));

// 设置一个定时器:如果在指定时间内没有玩家加入,则自动关闭服务器
GetWorld()->GetTimerManager().SetTimer(
WaitPlayerJoinTimeoutHandle,
this,
&UMGameInstance::WaitPlayerJoinTimeoutReached,
WaitPlayerJoinTimeOutDuration
);

// 会话创建成功后,加载大厅关卡并开启监听(等待客户端连接)
LoadLevelAndListen(LobbyLevel);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("———— 会话创建失败 ————"));
// 如果会话创建失败,立刻关闭服务器
TerminateSessionServer();
}

// 无论结果如何,解绑 OnCreateSessionComplete 委托,避免重复绑定
if (IOnlineSessionPtr SessionPtr = UTNetStatics::GetSessionPtr())
{
SessionPtr->OnCreateSessionCompleteDelegates.RemoveAll(this);
}
}

void UMGameInstance::EndSessionCompleted(FName SessionName, bool bWasSuccessful)
{
// 直接请求退出游戏进程
FGenericPlatformMisc::RequestExit(false);
}

void UMGameInstance::TerminateSessionServer()
{
if (IOnlineSessionPtr SessionPtr = UTNetStatics::GetSessionPtr())
{
// 确保委托不重复绑定
SessionPtr->OnEndSessionCompleteDelegates.RemoveAll(this);
SessionPtr->OnEndSessionCompleteDelegates.AddUObject(this, &UMGameInstance::EndSessionCompleted);

// 尝试结束会话,如果失败则直接退出
if (!SessionPtr->EndSession(FName{ ServerSessionName }))
{
FGenericPlatformMisc::RequestExit(false);
}
}
else
{
// 如果拿不到 SessionPtr,说明 OnlineSubsystem 不可用,直接退出
FGenericPlatformMisc::RequestExit(false);
}
}

void UMGameInstance::WaitPlayerJoinTimeoutReached()
{
UE_LOG(LogTemp, Warning, TEXT("会话服务器在等待%f秒后后无玩家加入,现已关闭"), WaitPlayerJoinTimeOutDuration);
// 超时无人加入,关闭服务器
TerminateSessionServer();
}

void UMGameInstance::LoadLevelAndListen(TSoftObjectPtr<UWorld> Level)
{
// 从软引用的 UWorld 获取包路径(比如 /Game/Maps/MyMap.MyMap)
const FName LevelURL = FName(*FPackageName::ObjectPathToPackageName(Level.ToString()));
if (LevelURL != "")
{
// 构造带有监听和端口参数的 URL
FString TravelStr = FString::Printf(TEXT("%s?listen?port=%d"), *LevelURL.ToString(), SessionServerPort);
UE_LOG(LogTemp, Warning, TEXT("服务器地图切换至 %s"), *(TravelStr));
// 开启 ServerTravel(切换关卡并开启监听)
GetWorld()->ServerTravel(TravelStr);
}
}

实现游戏会话类并对玩家加入和离开做出反应

在这里插入图片描述

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameSession.h"
#include "TGameSession.generated.h"

/**
* 自定义的游戏会话类,继承自 AGameSession
* 主要用于管理玩家的注册、注销,以及自动登录等逻辑。
*/

UCLASS()
class ATGameSession : public AGameSession
{
GENERATED_BODY()

public:
// 处理自动登录逻辑,返回 true 表示成功
virtual bool ProcessAutoLogin() override;

// 当新玩家加入时调用
// 负责将玩家注册进 Session,并通知 GameInstance
virtual void RegisterPlayer(APlayerController* NewPlayer, const FUniqueNetIdRepl& UniqueId, bool bWasFromInvite) override;

// 当玩家离开时调用
// 负责将玩家从 Session 注销,并通知 GameInstance
virtual void UnregisterPlayer(FName FromSessionName, const FUniqueNetIdRepl& UniqueId) override;
};

#include "Network/TGameSession.h"

#include "Framework/MGameInstance.h"

bool ATGameSession::ProcessAutoLogin()
{
// return Super::ProcessAutoLogin();
// 服务器不需要登录直接返回true
return true;
}

void ATGameSession::RegisterPlayer(APlayerController* NewPlayer, const FUniqueNetIdRepl& UniqueId, bool bWasFromInvite)
{
Super::RegisterPlayer(NewPlayer, UniqueId, bWasFromInvite);
// 获取游戏实例
if (UMGameInstance* GameInstance = GetGameInstance<UMGameInstance>())
{
// 玩家加入(传入唯一ID)
GameInstance->PlayerJoined(UniqueId);
}
}

void ATGameSession::UnregisterPlayer(FName FromSessionName, const FUniqueNetIdRepl& UniqueId)
{
Super::UnregisterPlayer(FromSessionName, UniqueId);

if (UMGameInstance* GameInstance = GetGameInstance<UMGameInstance>())
{
// 玩家退出(传入唯一ID)
GameInstance->PlayerLeft(UniqueId);
}
}

游戏实例中添加玩家的加入以及离开函数,玩家记录集合

public:
// 玩家加入会话(服务器端调用)
void PlayerJoined(const FUniqueNetIdRepl& UniqueId);

// 玩家离开会话(服务器端调用)
void PlayerLeft(const FUniqueNetIdRepl& UniqueId);
private:
// 玩家记录集合
TSet<FUniqueNetIdRepl> PlayerRecord;

void UMGameInstance::PlayerJoined(const FUniqueNetIdRepl& UniqueId)
{
// 清除等待超时定时器
if (WaitPlayerJoinTimeoutHandle.IsValid())
{
GetWorld()->GetTimerManager().ClearTimer(WaitPlayerJoinTimeoutHandle);
}
// 记录玩家
PlayerRecord.Add(UniqueId);
}

void UMGameInstance::PlayerLeft(const FUniqueNetIdRepl& UniqueId)
{
// 移除玩家记录
PlayerRecord.Remove(UniqueId);

// 如果所有玩家都离开,终止会话服务器
if (PlayerRecord.Num() == 0)
{
UE_LOG(LogTemp, Warning, TEXT("所有玩家都离开了会话, 结束服务器"))
TerminateSessionServer();
}
}

给游戏模式基类添加构造函数,构造函数中添加默认的游戏会话类

ACGameMode::ACGameMode()
{
// 创建游戏会话类
GameSessionClass = ATGameSession::StaticClass();
}

大厅游戏模式也是如此(父类设置了,子类是不是应该不用管了呢)

ALobbyGameMode::ALobbyGameMode()
{
bUseSeamlessTravel = true;
GameSessionClass = ATGameSession::StaticClass();
}

确实不用管 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

启用和测试 EOS

点击进入EOS插件文档 需要在DefaultEngine.ini中配置一下文件 在这里插入图片描述

[OnlineSubsystemEOS]
bEnabled=true

[OnlineSubsystem]
DefaultPlatformService=EOS

[/Script/Engine.Engine]
!NetDriverDefinitions=ClearArray
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="SocketSubsystemEOS.NetDriverEOSBase",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
+NetDriverDefinitions=(DefName="DemoNetDriver",DriverClassName="/Script/Engine.DemoNetDriver",DriverClassNameFallback="/Script/Engine.DemoNetDriver")

[/Script/Engine.GameEngine]
!NetDriverDefinitions=ClearArray
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="SocketSubsystemEOS.NetDriverEOSBase",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
+NetDriverDefinitions=(DefName="DemoNetDriver",DriverClassName="/Script/Engine.DemoNetDriver",DriverClassNameFallback="/Script/Engine.DemoNetDriver")

随后双击一下脚本 在这里插入图片描述 在这里插入图片描述 通过属性可以找到会话,但现在还是不能让客户端连接到服务器 在这里插入图片描述

赞(0)
未经允许不得转载:171主机测评 » UE5多人MOBA+GAS 53、测试专属服务器打包和连接,以及配置EOS
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址