当前位置: 首页 > web >正文

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 != ""){// 构造带有监听和端口参数的 URLFString 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,并通知 GameInstancevirtual void RegisterPlayer(APlayerController* NewPlayer, const FUniqueNetIdRepl& UniqueId, bool bWasFromInvite) override;// 当玩家离开时调用// 负责将玩家从 Session 注销,并通知 GameInstancevirtual void UnregisterPlayer(FName FromSessionName, const FUniqueNetIdRepl& UniqueId) override;
};
#include "Network/TGameSession.h"#include "Framework/MGameInstance.h"bool ATGameSession::ProcessAutoLogin()
{// return Super::ProcessAutoLogin();// 服务器不需要登录直接返回truereturn 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")

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

http://www.xdnf.cn/news/18648.html

相关文章:

  • WiFi有网络但是电脑连不上网是怎么回事?该怎么解决?
  • 云原生高级——K8S总概
  • OpenHands:开源AI软件开发代理平台的革命性突破
  • 2025最新版mgg格式转MP3,mflac转mp3,mgg格式如何转mp3?
  • setup 语法糖核心要点
  • Windows应急响应一般思路(一)
  • MySQL 高级主题:索引优化、ORM 与数据库迁移
  • More Effective C++ 条款02:最好使用C++转型操作符
  • 【0基础PS】蒙版与剪贴蒙版详解
  • NoCode-bench:自然语言驱动功能添加的评估新基准
  • 3.4 缩略词抽取
  • 表格识别技术:通过图像处理与深度学习,将非结构化表格转化为可编辑结构化数据,推动智能化发展
  • Vue Teleport 原理解析与React Portal、 Fragment 组件
  • GEO优化专家孟庆涛发布:《GEO内容优化的四大黄金标准》
  • 普中烧录软件 PZISP,打不开,提示“应用程序无法启动,因为应用程序并行配置不正确.....”
  • 学习嵌入式第三十五天
  • Linux应用软件编程---网络编程1(目的、网络协议、网络配置、UDP编程流程)
  • APP Usage『安卓』:比系统自带强10倍!手机应用使用时长精确到秒
  • MySQL - 视图,事务和索引
  • java8 findAny()、findFirst()空指针NullPointerException问题
  • ​维基框架 (Wiki Framework) 1.1.0 版本发布​ 提供多模型AI辅助开发
  • 图像指针:高效处理像素数据的核心工具
  • Linux虚拟机安装FTP
  • AtCoder Beginner Contest 419(ABCDEF)
  • Python Flask快速实现163邮箱发送验证码
  • 防火墙双机热备
  • 数据结构之深入探索快速排序
  • docker 打包
  • syn和quote的简单使用——生成结构体
  • 网络编程8.22