Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

虚幻引擎 服务器端 #226

Open
WangShuXian6 opened this issue Dec 5, 2024 · 1 comment
Open

虚幻引擎 服务器端 #226

WangShuXian6 opened this issue Dec 5, 2024 · 1 comment

Comments

@WangShuXian6
Copy link
Owner

服务器端

@WangShuXian6
Copy link
Owner Author

客户端连接流程

https://dev.epicgames.com/community/learning/tutorials/vwYb/unreal-engine-client-connection-flow

快速了解客户端和服务器在客户端加入游戏时的连接流程。

本页描述了客户端如何连接到服务器(监听模式或专用模式)的一般流程。我们假设服务器已经启动,加载了地图,并且其网络驱动程序(NetDriver)已经开启并在监听连接。

[客户端] UEngine::Browse(...) 被调用,使用要连接的 IP 地址作为 URL。

如果 URL 在游戏启动时作为命令行参数传递,那么这个操作会在解析 Map Override Name 时发生,即在 UGameInstance::StartGameInstance 中进行 [GameInstance.cpp 641]。否则,我们会设置 FWorldContext::TravelURLUWorld::NextURL,这两者都在 UEngine::TickWorldTravel(...) 中使用(见 [UnrealEngine.cpp 14323] 和 [UnrealEngine.cpp 14288])。

[客户端] UEngine::Browse(...) 实例化一个 PendingNetGame,将其安装到 WorldContext 中,并初始化它,调用 UPendingNetGame::Initialize(URL),随后调用 UPendingNetGame::InitNetDriver()

PendingNetGame 是一个处理客户端与服务器之间初始连接握手的对象。

[客户端] 在 UPendingNetGame::InitNetDriver() 中,我们:

  • 为客户端实例化一个 NetDriver(使用 UEngine::CreateNamedNetDriver(...)),并在客户端模式下初始化它,调用 UNetDriver::InitConnect(...)
  • 这会在 NetDriver 中安装一个 UNetConnection,代表客户端与服务器的连接(在 UNetDriver 中被称为 ServerConnection)。
  • UNetDriver::InitConnect(...) 还会告诉 NetDriverPendingNetGame 负责处理控制消息(通过设置 UNetDriver::NotifyPendingNetGame)。

通过 ServerConnection,我们与服务器发起握手(调用 UNetDriver::ServerConnection::Handler::BeginHandshaking(...)),并请求在握手完成时回调 UPendingNetGame::SendInitialJoin

此握手过程是初始化客户端与服务器之间的通信协议。

[客户端] 握手完成后,调用 UPendingNetGame::SendInitialJoin。此操作将初始的 NMT_Hello 消息发送到服务器。

[服务器] UWorld::NotifyControlMessage(...) 接收到 NMT_Hello 消息,进行一些加密处理(取决于协议),并返回一个 NMT_Challenge 消息。

服务器接收到控制消息是因为它在加载地图时,已经将自己设置为监听者(在 UNetDriver::SetWorld 中)。

[客户端] UPendingNetGame::NotifyControlMessage(...) 接收到 NMT_Challenge 消息,并响应发送一个 NMT_Login 消息。

玩家的 PlayerId 会附加在登录消息中。如果客户端已经登录到某个在线系统(例如 EOS),则 PlayerId 应该是他们的 FAccountID(更具体地说,是一个封装了 FAccountIDUniqueNetID)。在进行登录操作时,可能需要使用 SetCachedUniqueNetId 设置 ULocalPlayerPlayerID

[服务器] UWorld::NotifyControlMessage(...) 接收到 NMT_Login 消息。

这会调用 AGameMode::PreLogin(...),允许游戏代码接受或拒绝远程连接,并调用 AGameSession::ApproveLogin(...)。然后,我们广播 FGameModeEvents::GameModePreLoginEvent 委托。

如果客户端发送了 PlayerId,则我们使用它来设置代表该玩家的 UNetConnection 上的 PlayerId

假设 PreLogin 通过,调用 UWorld::WelcomePlayer(...),并向客户端发送 NMT_Welcome 消息。Welcome 消息让客户端知道如果想连接到服务器,需要加载哪个 Level。

[客户端] UPendingNetGame::NotifyControlMessage(...) 接收到 NMT_Welcome 消息。

此时,UPendingNetGame::URL 会基于服务器告诉我们需要加载的 Level 进行设置。

该 URL 然后会在 UEngine::TickWorldTravel(...) 中使用,调用 UEngine::LoadMap(...) [UnrealEngine.cpp 14365]。此时,调用 UEngine::LoadMap(...) 会将 PendingNetGameNetDriver 中移除,停止接收控制消息,并调用 SetWorld(...) 来设置 World,这意味着 World 现在会从 NetDriver 接收控制消息。

通常,LoadMap(...) 会为每个 LocalPlayer 创建一个 PlayerController。我们依然这么做,但它是临时的,之后会被服务器为我们生成的 PlayerController 替代 [LocalPlayer.cpp 301]。

我们会向服务器发送一个 NMT_Netspeed 消息,以便客户端和服务器就网络速度达成一致。

[客户端] 假设地图加载成功,我们调用 UPendingNetGame::TravelCompleted(...)(来自 UEngine::TickWorldTravel(...) [UnrealEngine.cpp 14387])。

这会导致 PendingNetGame 向服务器发送一个 NMT_Join 消息。

此后,PendingNetGame 会清理它的连接,并从 WorldContext 中移除。

[服务器] UWorld::NotifyControlMessage(...) 接收到 NMT_Join 消息,意味着我们需要为客户端生成一个具有远程角色 AutonomousProxyPlayerController

[服务器] 调用 UWorld::SpawnPlayActor(...) 生成玩家控制器(PC)。此函数:

  • 调用 AGameModeBase::Login(...),最终执行 AGameModeBase::SpawnPlayerControllerCommon(...) 来实际生成 PlayerController,然后调用 AGameModeBase::InitNewPlayer(...)
  • 调用 AGameModeBase::PostLogin(...),进而调用 AGameModeBase::HandleStartingNewPlayer(...),然后是 AGameModeBase::RestartPlayer(...),为玩家找到一个起始点,并为 PlayerController 生成一个 Pawn

[客户端] 由于 PlayerController 被设置为复制到拥有的客户端,会为 PlayerController 打开一个 Actor 通道,从而调用 APlayerController::OnActorChannelOpen(...)

这会调用 UNetConnection::HandleClientPlayer(...),并销毁我们在等待连接时创建的临时 PlayerController

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant