18 KiB
title, date, excerpt, tags, rating
title | date | excerpt | tags | rating |
---|---|---|---|---|
GAS网络联机笔记 | 2022-12-09 14:57:04 | Online | ⭐ |
资料链接
https://docs.unrealengine.com/en-US/Resources/Showcases/BlueprintMultiplayer/index.html https://docs.unrealengine.com/en-US/ProgrammingAndScripting/Blueprints/UserGuide/OnlineNodes/index.html
视频
- 虚幻引擎多人联机网络基础 | Network Multiplayer Fundamentals(真实字幕组) https://www.bilibili.com/video/BV1rV41167Em
测试用启动参数
C:\UnrealEngine\UnrealEngine\Engine\Binaries\Win64\UE4Editor.exe "C:\UnrealEngine\Project\SDHGame\SDHGame.uproject" -game -WINDOWED -WinX=0 -WinY=270 -ResX=960 -ResY=600
C:\UnrealEngine\UnrealEngine\Engine\Binaries\Win64\UE4Editor.exe "C:\UnrealEngine\Project\SDHGame\SDHGame.uproject" /Game/SceneAssets/Maps/GameplayDevelopMap?game=MyGame -server -log
UE4原生部分
属性复制
- 对于不想序列化的临时属性,比如CurrentHealth可以勾选transient
RPC
网络模式(ENetMode)
- NM_DedicatedServer:纯服务器
- NM_ListenServer:客户端与服务器
- NM_Client:纯客户端
在函数中判断当前模式:
if(GetNetMode() == NM_DedicatedServer)
{}
- 局域网联机的FPS游戏
- 以NM_Standalone启动, 创建或加入房间
- 如果是房主创建房间, 则变为NM_ListenServer
- 如果是加入房间, 则变为NM_Client
- 广域网MMO等游戏
- 服务器以NM_DedicatedServer启动, 并只会是该模式
- 客户端以NM_Standalone启动, 连接服务器后变为NM_Client
- 命令行传递参数启动程序
- 客户端启动参数添加服务器地址, 直接连接, 以NM_Client启动
- 客户端启动参数中地图开启?Listen, 以NM_ListenServer启动
- 客户端启动参数中添加-Server, 以NM_DedicatedServer启动
流程
《Exploring in UE4》关于网络同步的理解与思考[概念理解]:https://zhuanlan.zhihu.com/p/34721113
主要步骤如下:
- 客户端发送连接请求
- 服务器将在本地调用 AGameMode::PreLogin。这样可以使 GameMode 有机会拒绝连接。
- 如果服务器接受连接,则发送当前地图
- 服务器等待客户端加载此地图,客户端如果加载成功,会发送Join信息到服务器
- 如果接受连接,服务器将调用 AGameMode::Login该函数的作用是创建一个PlayerController,可用于在今后复制到新连接的客户端。成功接收后,这个PlayerController 将替代客户端的临时PlayerController (之前被用作连接过程中的占位符)。 此时将调用 APlayerController::BeginPlay。应当注意的是,在此 actor 上调用RPC 函数尚存在安全风险。您应当等待 AGameMode::PostLogin 被调用完成。
- 如果一切顺利,AGameMode::PostLogin 将被调用。这时,可以放心的让服务器在此 PlayerController 上开始调用RPC 函数。
需要知道的概念
- PlayerController一定是客户端第一次链接到服务器,服务器同步过来的这个PlayerController(也就是上面的第五点,后面称其为拥有连接的PlayerController)。进一步来说,这个Controller里面包含着相关的NetDriver,Connection以及Session信息。
- 对于任何一个Actor(客户端上),他可以有连接,也可以无连接。一旦Actor有连接,他的Role(控制权限)就是ROLE_AutonomousProxy,如果没有连接,他的Role(控制权限)就是ROLE_SimulatedProxy 。
问题
Actor的Role是ROLE_Authority就是服务端么?
并不是,有了前面的讲述,我们已经可以理解,如果我在客户端创建一个独有的Actor(不能勾选bReplicate)。那么这个Actor的Role就是ROLE_Authority,所以这时候你就不能通过判断他的Role来确定当前调试的是客户端还是服务器。这时候最准确的办法是获取到NetDiver,然后通过NetDiver找到Connection。(事实上,GetNetMode()函数就是通过这个方法来判断当前是否是服务器的)对于服务器来说,他只有N个ClientConnections,对于客户端来说只有一个serverConnection。
如何找到NetDriver呢?可以参考下面的图片,从Outer获取到当前的Level,然后通过Level找到World。World里面就有一个NetDiver。当然,方法
其他
- 编辑器设置-Multipplayer Options-玩家个数,是的播放时能开启多个窗口(代表多个玩家)进行调试。
- 对于角色类除了需要勾选Replicates还需要勾选Replicate Movement
Sessions与相关函数
- FindSessions寻找房间,并且生成一个Session数组。
CreateSession
- PublicConnections:连接数
- UseLan:是否是局域网游戏
- OnSuccess=》OpenLevel(在Option中添加listen)这样就会开启监听服务器
JoinSession
GetGameInstance(Cast当前自定义游戏实例)=》JoinSession
蓝图多人设计游戏笔记
-
关卡蓝图中Beginplay中首先通过GameInstance调用TransitionToState(),状态为Playing
-
用一个MainMenu关卡作为菜单界面
-
PlayerState:记录玩家的分数与id(原生实现)。Replicates=true,NetDormancy=Awake
-
GameState:空
-
GameMode:实现玩家重生、登录以后的逻辑(将玩家Pawn引用加到对应数组中)以及指定若干类(Pawn、Controller、GameState、PlayerState)
-
PlayerController:控制角色相关UI以及在登录后执行一次角色重生函数。
GameInstance
自带状态枚举:
- StartUp
- MainMenu
- ServerList
- LoadingScreen
- ErrorDialog
- Playing
- Unknown
实现逻辑:
- 实现Transition():根据枚举执行不同的操作,比如隐藏UI、销毁Session。
- 实现IsCurrentState():判断是否枚举是否相同,返回bool。
- 显示主菜单逻辑:根据当前游戏状态(Playing或者MainMenu)显示游戏菜单或者退回主菜单(打开主界面关卡)。
- 显示载入界面逻辑:切换成载入界面。(如果没有创建UMG就创建并赋值)
- HostGameEvent:显示载入界面=》创建Session=》打开游戏地图,
- ShowServerListEvent:显示服务器列表UI。
- JoinFromServerListEvent:显示载入界面=》JoinSession
- 错误处理:打印错误信息。c++代码会触发这些事件,NetworkError与TravelError。
ShooterGame c++ 笔记
RPC UFUNCTION Meta
- server:服务端执行
- client:客户端执行
- NetMulticast:多播
- reliable:可靠RPC
- unreliable:不可靠RPC
- WithValidation:需要验证
Class
- AShooterTeamStart 出生点
- AShooterCheatManager 作弊管理器
AShooterCharacter
//////////////////////////////////////////////////////////////////////////
// Replication
void AShooterCharacter::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
//只有在这个属性发生变化后,才会在短时间内复制这个属性,这样加入进度中的玩家才不会在后期加入时被喷fx。
DOREPLIFETIME_ACTIVE_OVERRIDE(AShooterCharacter, LastTakeHitInfo, GetWorld() && GetWorld()->GetTimeSeconds() < LastTakeHitTimeTimeout);
}
void AShooterCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//只对本地所有者:武器更换请求是本地发起的,其他客户不需要。
DOREPLIFETIME_CONDITION(AShooterCharacter, Inventory, COND_OwnerOnly);
//除了本地拥有者:改变的本地激发者
// everyone except local owner: flag change is locally instigated
DOREPLIFETIME_CONDITION(AShooterCharacter, bIsTargeting, COND_SkipOwner);
DOREPLIFETIME_CONDITION(AShooterCharacter, bWantsToRun, COND_SkipOwner);
DOREPLIFETIME_CONDITION(AShooterCharacter, LastTakeHitInfo, COND_Custom);
// everyone
DOREPLIFETIME(AShooterCharacter, CurrentWeapon);
DOREPLIFETIME(AShooterCharacter, Health);
}
AShooterPlayerController
- 一堆OnlineSubsystem东西,比如查询成绩、加载朋友信息……
- SimulateInputKey():用于自动测试。
- 控制InGame菜单
- ReceivedNetworkEncryptionToken()与ReceivedNetworkEncryptionAck()对传输进行加密。使用一个定义的密钥。
ClientStartOnlineGame_Implementation
使用OnlineSession的联机游戏
ShooterGameInstance
- HostGame:开房间函数,url参数为地图。(可在FShooterMainMenu::HostGame()查看格式)
- JoinSession:进房间,调用Session->JoinSession,切换地图。
- BeginHostingQuickMatch:快速游戏,直接切换地图。
- OnPostLoadMap:隐藏载入界面。
- FindSessions:寻找房间。
- HostQuickSession:开始游戏,同时开房间。
/** Main menu UI */
TSharedPtr<FShooterMainMenu> MainMenuUI;
/** Message menu (Shown in the even of errors - unable to connect etc) */
TSharedPtr<FShooterMessageMenu> MessageMenuUI;
/** Welcome menu UI (for consoles) */
TSharedPtr<FShooterWelcomeMenu> WelcomeMenuUI;
/** Dialog widget to show non-interactive waiting messages for network timeouts and such. */
TSharedPtr<SShooterWaitDialog> WaitMessageWidget;
Online文件夹中文件
GameMode管理游戏的游戏方式与规则:
- ShooterGameMode(基类)
- ShooterGame_FreeForAll
- ShooterGame_TermDeathMatch
AShooterGameSession
继承AGameSession。
匹配:StartMatchmaking()、ContinueMatchmaking()会调用JoinSession()。
- RegisterServer
- HostSession
- FindSessions
- JoinSession
委托
/** Delegate for creating a new session */
FOnCreateSessionCompleteDelegate OnCreateSessionCompleteDelegate;
/** Delegate after starting a session */
FOnStartSessionCompleteDelegate OnStartSessionCompleteDelegate;
/** Delegate for destroying a session */
FOnDestroySessionCompleteDelegate OnDestroySessionCompleteDelegate;
/** Delegate for searching for sessions */
FOnFindSessionsCompleteDelegate OnFindSessionsCompleteDelegate;
/** Delegate after joining a session */
FOnJoinSessionCompleteDelegate OnJoinSessionCompleteDelegate;
//OnlineSubSystem交互的
OnFindSessionsComplete(bool bWasSuccessful)
void AShooterGameSession::OnJoinSessionComplete(FName InSessionName, EOnJoinSessionCompleteResult::Type Result)
{
bool bWillTravel = false;
UE_LOG(LogOnlineGame, Verbose, TEXT("OnJoinSessionComplete %s bSuccess: %d"), *InSessionName.ToString(), static_cast<int32>(Result));
IOnlineSubsystem* OnlineSub = Online::GetSubsystem(GetWorld());
if (OnlineSub)
{
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
Sessions->ClearOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegateHandle);
}
}
OnJoinSessionComplete().Broadcast(Result);
}
专用服务器
- 打包选择专用服务器(需要使用源码版引擎)
- MyProjectServer.exe -log
复制优化
设置Replicate中属性,比如:
- 当不需要复制时,关闭复制。
- 适当降低Net Update Frequency
- NetCullDistanceSquared
- NetClientTicksPerSecond
- NetDormancy:可以一开始将Actor设置为只在初始化同步,之后根据事件调用ForceNetUpdate或者将Net Dormancy设置为Awake。
- Relevancy
命令行输入netprofile,运行一会后,输入netprofile disable来停止记录,之后就可以在Saved中找到网络性能报告了。
服务器切换
当前服务器掉线时切换为玩家作为服务器。 https://docs.unrealengine.com/zh-CN/InteractiveExperiences/Networking/Travelling/index.html
其他
- 蓝图节点IsLocallyController判断角色是本地模拟还是远程控制。在c++是GetRomoteRole
- Instigator,每个Actor拥有变量,用于判断是谁触发了XX效果,可以用来谁击杀了玩家,以及谁得分了。
- 尽量避免使用RPC,使用OnRep_XX是个好的选择。
- Movement属于不可靠复制。可靠复制会占用一个特殊的buff队列,控制每帧发送可靠复制的量,以节约资源。
- 主机迁移(当前服务器离线时会让其中一个玩家充当服务器):1、非无缝,会出现加载框
- OnValidData标签用于验证数据是否有效,防止作弊,需要实现一个返回值为bool的XXX_ValidData函数。
Online Beacon 基类
Beacon 类执行的常规操作是请求服务质量信息、在客户端需要加入的游戏中预留空位、接收游戏中玩家名列表、获取正在进行的游戏中的得分和运行时间,等等。 以下类由引擎提供,构成了 Online Beacon 系统的基础:
AOnlineBeacon
这是 AOnlineBeaconClient 和 AOnlineBeaconHost 的基类。 它直接派生自 AActor。
AOnlineBeaconHost
此类使用其自身的 UNetDriver 获得来自远程客户端电脑的传入 Online Beacon 连接。 接收到连接时,它将在注册 AOnlineBeaconHostObject 实例列表中进行查找,找到与传入客户端匹配的实例并转交连接。 此类通常不需要被派生,因其只管理客户端和注册 AOnlineBeaconHostObject 之间的初始连接。
AOnlineBeaconClient
此类的子项连接到主机并执行实际的 RPC。 它们其中一个将在客户端电脑上生成,一个由正确的 AOnlineBeaconHostObject(注册到服务器的 AOnlineBeaconHost)在服务器上生成。 GetBeaconType 函数的输出(即为类名称)将用于对比此类的实例和正确主机对象类的注册实例。 注意:这和普通的 Actor 生成方式(服务器生成 Actor 然后复制到客户端)不同。 然而,客户端和服务器对象副本之间的连接建立后,对象复制将正常进行,任意一方均可向对方执行 RPC,而对象的服务器版本可对属性复制发送命令。 该基类实现 OnConnected 和 OnFailure 函数。这两个函数可由子类覆盖,在连接时执行 RPC,或处理失败连接。 此类是 Online Beacon 系统的主力,将执行 Beacon 所需的客户端端的工作。 在成功连接事件中,服务器上将生成和源实例同步的另一个实例,此例也可执行服务器端的工作,通过客户端和服务器 RPC(或服务器到客户端的复制属性)进行协调和交流。
AOnlineBeaconHostObject
此类也应被覆盖,使其和覆盖的 AOnlineBeaconClient 类配对。 将客户端 GetBeaconType 的返回值和保存在 BeaconTypeName 成员变量中的值进行匹配即可完成配对。 服务器的 AOnlineBeaconHost 检测到传入 AOnlineBeaconClient 的配对 AOnlineBeaconHostObject 时,它将指示 AOnlineBeaconHostObject 通过虚拟 SpawnBeaconActor 函数生成 AOnlineBeaconClient 的本地副本。 此函数默认使用 ClientBeaconActorClass 成员变量确定要生成的 actor 类,此类应被设为配对的 AOnlineBeaconClient 类。 它还将在生成对象的服务器副本上调用 SetBeaconOwner,以便客户端对象的服务器端实例与主机对象进行交流。 此设置多数建立在基类中,无需被覆盖。
插件
下面是2个牛逼插件
- Advanced Steam Sessions
- Advanced Session
官方插件
- Steam Sockets https://docs.unrealengine.com/zh-CN/InteractiveExperiences/Networking/HowTo/SteamSockets/index.html
- Online Subsystem
- Replication Graph插件 Replication Graph插件是一个用于多人游戏的网络复制系统,它的设计可以很好地适应大量玩家和复制Actor。例如,Epic自己的Fortnite Battle Royale 从一开始就支持每场比赛100名玩家,包含大约50,000个复制的Actor。https://www.bilibili.com/medialist/play/watchlater/BV1Xb411h7hp
GAS部分
Replication
#include "UnrealNetwork.h"
- 给对应的变量添加Replicated标记
- 重写void GetLifetimeReplicatedProps(TArray& OutLifetimeProps),并添加对应变量的代码,例如: DOREPLIFETIME_CONDITION_NOTIFY( UMyAttributeSet, MyAttribute, COND_None, REPNOTIFY_Always);
- 属性钩子函数UPROPERTY( ReplicatedUsing = OnRep_MyAttribute)、void OnRep_MyAttribute()
需要在c++对应的构造函数中进行初始化
AGDPlayerState :: AGDPlayerState()
{
//创建能力系统组件,并将其设置为显式复制
AbilitySystemComponent = CreateDefaultSubobject <UGDAbilitySystemComponent>( TEXT( “ AbilitySystemComponent ”));;
AbilitySystemComponent-> SetIsReplicated(true);
// ...
}
void APACharacterBase :: PossessedBy(AController * NewController)
{
Super :: PossessedBy(NewController);
如果(AbilitySystemComponent)
{
AbilitySystemComponent-> InitAbilityActorInfo(this,this);
}
// ASC MixedMode复制要求ASC所有者的所有者为控制器。
SetOwner(NewController);
}
// Server only
void AGDHeroCharacter::PossessedBy(AController * NewController)
{
Super::PossessedBy(NewController);
AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
if (PS)
{
// Set the ASC on the Server. Clients do this in OnRep_PlayerState()
AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
// AI won't have PlayerControllers so we can init again here just to be sure. No harm in initing twice for heroes that have PlayerControllers.
PS->GetAbilitySystemComponent()->InitAbilityActorInfo(PS, this);
}
//...
}
// Client only
void AGDHeroCharacter::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
if (PS)
{
// Set the ASC for clients. Server does this in PossessedBy.
AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
// Init ASC Actor Info for clients. Server will init its ASC when it possesses a new Actor.
AbilitySystemComponent->InitAbilityActorInfo(PS, this);
}
// ...
}
Net Security Policy
A GameplayAbility's NetSecurityPolicy determines where should an ability execute on the network. It provides protection from clients attempting to execute restricted abilities.
- NetSecurityPolicy Description ClientOrServer No security requirements. Client or server can trigger execution and termination of this ability freely.
- ServerOnlyExecution A client requesting execution of this ability will be ignored by the server. Clients can still request that the server cancel or end this ability.
- ServerOnlyTermination A client requesting cancellation or ending of this ability will be ignored by the server. Clients can still request execution of the ability.
- ServerOnly Server controls both execution and termination of this ability. A client making any requests will be ignored.
专用服务器查找
Dedicated servers must have "Use Presence" and "Allow Join Via Presence" set to false. Maybe that could solve your problem :)