BlueRoseNote/03-UnrealEngine/Gameplay/Online/GAS网络联机笔记.md
2023-06-29 11:55:02 +08:00

18 KiB
Raw Blame History

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

视频

测试用启动参数

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

主要步骤如下:

  1. 客户端发送连接请求
  2. 服务器将在本地调用 AGameMode::PreLogin。这样可以使 GameMode 有机会拒绝连接。
  3. 如果服务器接受连接,则发送当前地图
  4. 服务器等待客户端加载此地图客户端如果加载成功会发送Join信息到服务器
  5. 如果接受连接,服务器将调用 AGameMode::Login该函数的作用是创建一个PlayerController可用于在今后复制到新连接的客户端。成功接收后这个PlayerController 将替代客户端的临时PlayerController (之前被用作连接过程中的占位符)。 此时将调用 APlayerController::BeginPlay。应当注意的是在此 actor 上调用RPC 函数尚存在安全风险。您应当等待 AGameMode::PostLogin 被调用完成。
  6. 如果一切顺利AGameMode::PostLogin 将被调用。这时,可以放心的让服务器在此 PlayerController 上开始调用RPC 函数。

需要知道的概念

  • PlayerController一定是客户端第一次链接到服务器服务器同步过来的这个PlayerController也就是上面的第五点后面称其为拥有连接的PlayerController。进一步来说这个Controller里面包含着相关的NetDriverConnection以及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

GetGameInstanceCast当前自定义游戏实例=》JoinSession

蓝图多人设计游戏笔记

  • 关卡蓝图中Beginplay中首先通过GameInstance调用TransitionToState()状态为Playing

  • 用一个MainMenu关卡作为菜单界面

  • PlayerState记录玩家的分数与id原生实现。Replicates=trueNetDormancy=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);
}

专用服务器

https://docs.unrealengine.com/en-US/InteractiveExperiences/Networking/HowTo/DedicatedServers/index.html

  • 打包选择专用服务器(需要使用源码版引擎)
  • 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

常规用法: http://community.metahusk.com/topic/26/community-project-cardinal-menu-system-instructions-help-and-discussion

官方插件

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-> SetIsReplicatedtrue;
	// ... 
}

void APACharacterBase :: PossessedByAController * NewController
{
	Super :: PossessedByNewController;

	如果(AbilitySystemComponent
	{
		AbilitySystemComponent-> InitAbilityActorInfothisthis;
	}

	// ASC MixedMode复制要求ASC所有者的所有者为控制器。
	SetOwnerNewController;
}

// 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.

专用服务器查找

https://answers.unrealengine.com/questions/502967/dedicated-server-find-session-issues.html?sort=oldest

Dedicated servers must have "Use Presence" and "Allow Join Via Presence" set to false. Maybe that could solve your problem :)