# 虚幻引擎

通过实践学习，并在 Edgegap 上部署你的第一个专用服务器。本指南结束时，你将免费使用 Edgegap 部署一个专用服务器。

使用 Docker Desktop 构建是开始上手最快、最简单且最可靠的方法。

{% embed url="<https://youtu.be/q7ljcr9rAWE>" %}

## ✔️ 准备工作

<details>

<summary><a href="https://open.docker.com/extensions/marketplace?extensionId=edgegap/docker-extension">安装 Edgegap Quickstart Docker 扩展</a></summary>

* 通过 Docker Desktop / Extensions / Browse 安装，或者 [使用链接](https://open.docker.com/extensions/marketplace?extensionId=edgegap/docker-extension).

<figure><img src="https://3334189208-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FraFWUCJWPZYvBMLieBUZ%2Fimage.png?alt=media&#x26;token=186886ac-accb-421d-b560-3ccefa7f29cb" alt=""><figcaption></figcaption></figure>

</details>

{% hint style="info" %}
**对你的服务器构建有信心了吗？** 跳转到 [#customize-server-image](#customize-server-image "mention") 和 [advanced-features](https://docs.edgegap.com/zh/learn/advanced-features "mention").
{% endhint %}

## ⚙️ 1. 配置项目 <a href="#id-1-configure-project" id="id-1-configure-project"></a>

{% hint style="info" %}
这种方法不需要下载 Unreal Engine 源代码，也不需要从源代码构建！
{% endhint %}

☑️ 首先 **验证你的 Unreal Engine 版本** - 已根据你的项目文件预填值。

☑️ **输入 GitHub 用户名和** [**PAT**](#user-content-fn-1)[^1] 来自 [#preparation](#preparation "mention")，用于从 GitHub 下载依赖项。

## 🔧 2. 构建游戏服务器 <a href="#id-2-build-game-server" id="id-2-build-game-server"></a>

现在我们将构建并烘焙你的项目，并将其打包成一个易于复用的 docker 镜像。

☑️ 你可以配置以下选项（或保留默认值）：

* **镜像名称** 是你自定义的唯一标识，用于在发布前标记你的服务器构建。
  * 通常，这会包含你的游戏名称，例如“my-game-server”。
* **镜像标签** 是指向你镜像特定版本的标识。
  * 术语“构建产物”有时也用于指代镜像的特定版本。
  * 时间戳是很好的标签选择，例如 `2024.01.30-16.23.00-UTC`  （默认）。

☑️ **构建项目** 在你对配置满意后。完成此步骤后，会在你的本地 Docker 客户端中添加一个包含 Linux 游戏服务器可执行文件的新镜像。

✅ 你现在可以继续下一步了。

## 🧪 3. 在本地测试服务器 <a href="#id-3-test-server-locally" id="id-3-test-server-locally"></a>

☑️ **选择你想在本地运行的镜像标签** （远程镜像将会被下载）。另外，还可以提供更多 [docker run 参数](https://docs.docker.com/reference/cli/docker/image/build/#options) 来自定义你的本地测试：

* `-p 7777:7777/udp` - 这是你的本地容器 [端口映射](https://docs.edgegap.com/zh/learn/bian-pai/application-and-versions#port-mapping),
* `-e ARBITRIUM_PORT_GAMEPORT_INTERNAL=7777`  是一个 [环境变量](#environment-variables) 用于模拟真实的 Edgegap 部署，告诉你的游戏服务器要监听用于玩家连接的内部端口。

☑️ 当你对配置满意后，点击 **启动本地服务器**。完成此步骤后，将会 **启动一个新容器** 在你的开发机器上。

☑️ 现在是时候把你的 Unreal Engine Editor（PIE）游戏客户端连接到本地服务器容器了。使用 `~`  （波浪号）打开 Unreal PIE 控制台，并使用 `open <ip>:<port>`:

* `ip`  = `localhost`  或 `127.0.0.1`  （在大多数情况下等同），
* `port`  = Docker GUI 中容器随机分配的外部端口值。

✅ 你现在可以继续下一步了。

<details>

<summary>故障排除和常见问题</summary>

无法将客户端连接到服务器 - `请求超时。` , `请求超时` , `ConnectionFailed` ，或 `端口验证失败`

* 首先，确保容器已启动，并且日志中没有运行时错误。
* 请验证 `docker run` 命令中的端口值是否匹配。
* 请确保你的游戏客户端连接到容器详情页面上显示的 **外部端口** ，由于安全原因，该值始终会随机变化。
* 请确保你已按步骤所述重命名目标文件并配置游戏构建 [#id-1-configure-project](#id-1-configure-project "mention").

***

我的容器已启动，但之后几分钟内我仍然无法连接。

* 容器启动后，游戏引擎初始化就会开始。此过程可能持续几秒到几分钟不等，在此期间服务器不接受玩家连接。
* 考虑优化服务器初始化以缩短这段时间。
* 游戏客户端应以 1 秒间隔重试连接，持续有限时间（取决于你的初始化时长），然后应返回匹配队列。
* 考虑添加一个加载场景，使服务器能够在与客户端同时进行初始化（对于 Unreal Engine 来说还包括关卡跳转），同时同步双方状态。

***

`警告：无法为绑定地址创建套接字`

* 请通过 Fab 资源商店安装 Epic 的 Steam Subsystem 插件。
* 当使用从 GitHub 下载的 SteamCore 源码版本与 Edgegap Integration Kit（EGIK）时，由于 Epic Games 的插件分发政策，不包含 Epic 的 Steam Subsystem 插件。

***

我已连接，但我的屏幕完全是黑的。

* 请确认你设置了正确的 **游戏默认地图** 在以下位置 **编辑 / 项目设置 / 地图与模式**.

</details>

## ☁️ 4. 发布到 Edgegap <a href="#id-4-publish-to-edgegap" id="id-4-publish-to-edgegap"></a>

☑️ **选择一个应用名称** 用于在 Edgegap 上标记和分组相似镜像。

☑️ **选择你想发布的镜像标签** 和 **上传镜像**。完成此步骤后，你的服务器镜像将上传到 Edgeap Registry，并在你的网页浏览器中创建一个新的 [应用版本](https://docs.edgegap.com/zh/learn/bian-pai/application-and-versions) 。 **请确保创建你的** [**端口映射**](https://docs.edgegap.com/zh/learn/bian-pai/application-and-versions#port-mapping) **在提示时，** 使用默认&#x503C;**.**

{% hint style="success" %}
发现了 bug，需要再次重新构建/发布？使用 **从源代码重新构建** 来 [#id-2.-build-game-server](#id-2.-build-game-server "mention") 和 [#id-4.-publish-to-edgegap](#id-4.-publish-to-edgegap "mention") **通过当前扩展输入值快速完成。**
{% endhint %}

✅ 你现在可以继续下一步了。

## 🚀 5. 部署到云端 <a href="#id-5-deploy-to-cloud" id="id-5-deploy-to-cloud"></a>

☑️ 现在我们将进行最终测试并 **将你的 Unreal Engine Editor 连接到云端部署**。获取你的 **部署主机** 以代替服务器 IP，以及部署的 **外部端口**，在游戏客户端中打开 Unreal 控制台（波浪号 `~`）并输入 `open {host}:{port}` .

<details>

<summary>故障排除和常见问题</summary>

无法将客户端连接到服务器 - `请求超时。` , `请求超时` , `ConnectionFailed` ，或 `端口验证失败`

* 首先，确保部署状态为 Ready，并且部署日志中没有运行时异常或错误。如果你的部署已停止，请在我们的 [仪表板](https://app.edgegap.com/deployment-management/deployments/list).
* 中查看日志。请验证你服务器构建的网络代码设置中的端口配置是否与 [App 版本](https://app.edgegap.com/application-management/applications/list)中的内部端口匹配。对于插件构建，端口会自动为你设置。你可以通过编辑 [App 版本](https://app.edgegap.com/application-management/applications/list) 来更改端口映射，而无需重新构建。在你的网络代码集成中查找你的协议。
* 请确保你的游戏客户端连接到容器详情页面上显示的 **外部端口** 在部署详情页面上显示的值，由于安全原因，该值始终会随机变化。
* 请确保你已按步骤所述重命名目标文件并配置游戏构建 [#id-1.-configure-project](#id-1.-configure-project "mention").
* 你是否位于中国，并且正在使用 [智能舰队](https://docs.edgegap.com/docs/deployment/session/fleet-manager/fleet)？你的连接可能会被防火长城阻止。考虑在你的舰队中添加位于中国的服务器，或使用 VPN 连接。

***

我的部署已就绪，但之后几分钟内我仍然无法连接。

* 一旦部署状态变为 Ready，游戏引擎初始化就会开始。此过程可能持续几秒到几分钟不等，在此期间服务器不接受玩家连接。
* 考虑优化服务器初始化以缩短这段时间。
* 游戏客户端应以 1 秒间隔重试连接，持续有限时间（取决于你的初始化时长），然后应返回匹配队列。
* 考虑添加一个加载场景，使服务器能够在与客户端同时进行初始化（对于 Unreal Engine 来说还包括关卡跳转），同时同步双方状态。

***

`警告：无法为绑定地址创建套接字`

* 请通过 Fab 资源商店安装 Epic 的 Steam Subsystem 插件。
* 当使用从 GitHub 下载的 SteamCore Integration Kit（SIK）源码版本与 Edgegap Integration Kit（EGIK）时，由于 Epic Games 的插件分发政策，不包含 Epic 的 Steam Subsystem 插件。

***

我已连接，但我的屏幕完全是黑的。

* 请确认你设置了正确的 **游戏默认地图** 在以下位置 **编辑 / 项目设置 / 地图与模式**.
* 请验证 [Unreal Engine 版本兼容性检查已被禁用](#id-2.-configure-game-server-builds) 在 `DefaultEngine.ini`.

***

我的部署已停止/重启，我再也无法访问它的日志了。

* 如果服务器进程因异常崩溃，我们的系统会尝试自动重启服务器。建议先在本地测试你的服务器，以找出根本原因。
* 我们只会在部署期间保留日志，如果你希望在部署停止后查看日志，请 [集成第三方日志存储](https://docs.edgegap.com/docs/deployment/endpoint-storage).
* 参见 [#id-5.-deployment-stopped](https://docs.edgegap.com/zh/learn/bian-pai/deployments#id-5.-deployment-stopped "mention") 了解导致部署停止的所有原因。

***

我的部署在 X 分钟后自动停止了。

* 免费套餐部署有 60 分钟限制，请考虑升级你的账户。
* 根据我们的服务器清理策略，为了进行基础设施维护，并防止在部署未正确关闭时产生意外费用，所有部署在运行 24 小时后都会被终止。对于长时间运行的服务器，请考虑使用 [si-you-ji-qun](https://docs.edgegap.com/zh/learn/bian-pai/si-you-ji-qun "mention") 与 [chi-jiu-hua](https://docs.edgegap.com/zh/learn/bian-pai/chi-jiu-hua "mention").
* 参见 [#id-5.-deployment-stopped](https://docs.edgegap.com/zh/learn/bian-pai/deployments#id-5.-deployment-stopped "mention") 了解导致部署停止的所有原因。

***

如果有玩家离开我的部署，会发生什么？

* 默认情况下，服务器不会拒绝玩家连接。玩家认证由你的开发者决定，因为可以使用许多不同的方法和玩家认证提供商。
* 游戏客户端可能会在本地存储连接信息，以便在客户端意外崩溃时尝试重新连接。
* 要允许玩家加入进行中的游戏，可以考虑使用 [#backfill](https://docs.edgegap.com/zh/learn/pi-pei/matchmaker-in-depth#backfill "mention") 或 [会话](https://docs.edgegap.com/docs/deployment/session).

***

我的服务器在变为就绪后显示 100% CPU 使用率。

* 这可能不是问题，因为游戏引擎在服务器初始化期间往往会执行大量 CPU 密集型操作。如果在部署开始后 2-3 分钟 CPU 使用率仍未下降，你可能需要优化服务器或增加应用版本资源。
* 降低 tick 速率有助于通过处理更少消息来控制 CPU 使用率。
* 在免费套餐中，你的资源限制为 1.5 vCPU 和 3GB 内存（RAM）。
* 在创建新的应用版本时，你可以增加分配的资源。你可以在我们的仪表板中复制你的应用版本并根据需要调整这些值，而无需重新构建服务器或镜像。

***

我的部署反复重启并显示错误 `OOM kill`

* 这是由于超过了分配的内存量所致。建议使用对象池、压缩或移除场景中不需要的对象来优化内存使用。
* 在免费套餐中，你的资源限制为 1.5 vCPU 和 3GB 内存（RAM）。
* 在创建新的应用版本时，你可以增加分配的资源。你可以在我们的仪表板中复制你的应用版本并根据需要调整这些值，而无需重新构建服务器或镜像。

***

有时，我服务器的内存（RAM）使用会突然飙升到很高，这会有问题吗？

* 只要你保持在分配的应用版本内存额度内，就没有问题。
* 超过分配的应用版本内存额度将导致 `OOM kill` （见上文）。

***

同一台机器上运行的其他服务器会影响我的服务器性能吗？

* 不会，我们的平台可确保分配的资源不会被其他工作室或共享基础设施上的其他服务器使用。在 Edgegap 上，不会有“吵闹的邻居”。

</details>

## 👉 下一步

### 停止部署

了解各种 [停止部署](https://docs.edgegap.com/zh/learn/bian-pai/deployments#id-5.-deployment-stopped) 的方法，在比赛结束且玩家离开后使用。

如果你按照本指南并使用我们的 Docker 扩展构建，你只需调用方法 `FGenericPlatformMisc::RequestExit` 。我们已在打包镜像中添加了一个管理服务器进程的脚本，它会自动执行平滑的部署关闭。

要自定义服务器生命周期管理，请修改我们的 [示例 `StartServer.sh`](https://github.com/edgegap/edgegap-unreal-buildutils/blob/main/StartServer.sh)  脚本。

{% hint style="info" %}
更愿意从 Unreal 中管理生命周期？参见 [#integration-kit](https://docs.edgegap.com/zh/developer-tools#integration-kit "mention") 以获取自停止 API 蓝图。
{% endhint %}

{% hint style="warning" %}
连接你的 [endpoint-storage](https://docs.edgegap.com/zh/docs/endpoint-storage "mention") 以获取部署日志，否则它们将被删除！
{% endhint %}

### 注入变量

通过访问注入的环境变量，读取部署 ID、服务器 IP 地址、服务器位置等有用信息。每个部署都会自动包含：

* [部署变量](https://docs.edgegap.com/zh/learn/bian-pai/deployments#injected-environment-variables) - 由 Edgegap 自动提供，
* [匹配变量](https://docs.edgegap.com/zh/learn/pi-pei/matchmaker-in-depth#injected-environment-variables) - 在使用时由 Edgegap 自动提供 [pi-pei](https://docs.edgegap.com/zh/learn/pi-pei "mention"),
* [应用版本变量](https://docs.edgegap.com/zh/learn/bian-pai/application-and-versions#injected-variables) - 可由你配置的自定义键值对。

**通过检查 Edgegap 变量是否已设置，验证当前实例是游戏客户端还是服务器：** ：

```cpp
if (
  FPlatformMisc::GetEnvironmentVariable(TEXT("ARBITRIUM_REQUEST_ID")).empty()
)
{
  // 客户端代码
} else {
  // 服务器代码
}
```

### 服务器性能分析

要理解并优化 Edgegap 上的服务器性能问题，请探索 [#container-logs](https://docs.edgegap.com/zh/learn/bian-pai/deployments#container-logs "mention"), [#container-metrics](https://docs.edgegap.com/zh/learn/bian-pai/deployments#container-metrics "mention")以及更多 [#dashboard-monitoring](https://docs.edgegap.com/zh/learn/bian-pai/deployments#dashboard-monitoring "mention") 工具供你使用。

你也可以在 Edgegap 中使用现有的 Unreal Engine 性能分析工具：

* [在你的 Unreal Engine 服务器中配置跟踪](https://dev.epicgames.com/documentation/en-us/unreal-engine/developer-guide-to-tracing-in-unreal-engine) （内置和自定义事件）：
  * 使用以下方式将跟踪保存到服务器磁盘 `-tracefile`，上传到第三方存储并离线分析，
  * 或者使用以下方式流式传输跟踪数据 [#port-mapping](https://docs.edgegap.com/zh/learn/bian-pai/deployments#port-mapping "mention") 用于内部端口 `1981` 通过 UDP 协议。
* 分析 [内存洞察](https://dev.epicgames.com/documentation/en-us/unreal-engine/memory-insights-in-unreal-engine) 和 [网络洞察](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-insights-in-unreal-engine) 和 [#optimize-server-builds](#optimize-server-builds "mention").

### 匹配

手动启动部署、粘贴 URL 和端口对于实时游戏来说是不够的。

{% hint style="success" %}
[**阅读更多关于匹配的信息**](https://docs.edgegap.com/zh/learn/pi-pei) **以便在玩家上线时自动、即时部署**。
{% endhint %}

### 优化服务器构建

**配置资源分块，以将仅客户端资源与服务器资源分离。**

* 了解 [Epic 的资源分块技术和建议](https://dev.epicgames.com/documentation/en-us/unreal-engine/preparing-assets-for-chunking-in-unreal-engine) 。

**排除仅客户端需要、服务器运行不需要的资源和插件。**

* 了解 [构建时资源和插件排除](https://dev.epicgames.com/community/learning/tutorials/Kp1k/unreal-engine-build-time-asset-and-plugin-exclusion).

**审查你的内容烘焙策略。**

* 考虑使用 [动态烘焙（COTF）](https://dev.epicgames.com/documentation/en-us/unreal-engine/build-operations-cooking-packaging-deploying-and-running-projects-in-unreal-engine#cookonthefly) 来延迟客户端资源烘焙并加快服务器构建。

**实施关卡流送，以减少运行时内存占用。**

* 如果你的设计让玩家大多集中在同一地图区域， [关卡流送可以将服务器的内存使用量](https://dev.epicgames.com/documentation/en-us/unreal-engine/level-streaming-in-unreal-engine) 降低 60% 以上，并提升客户端性能！

**只包含服务器运行绝对需要的内容。**

* 将未使用的文件复制到镜像中会导致镜像臃肿、上传时间更长、缓存速度更慢，以及整体服务器启动更慢。 [查看 Docker 镜像优化建议](https://docs.docker.com/build-cloud/optimization/#dockerignore-files).

<details>

<summary>示例 <code>.dockerignore</code> 文件以移除多余文件。</summary>

```docker
# 编译后的对象文件
*.slo
*.lo
*.o
*.obj

# 预编译头文件
*.gch
*.pch

# 编译后的动态库
*.so
*.dylib
*.dll

# Fortran 模块文件
*.mod

# 编译后的静态库
*.lai
*.la
*.a
*.lib

# 可执行文件
*.exe
*.out
*.app
*.ipa

# 这些项目文件可由引擎生成
*.xcodeproj
*.xcworkspace
*.sln
*.suo
*.opensdf
*.sdf
*.VC.db
*.VC.opendb

# 预编译资源
**/SourceArt/**/*.png
**/SourceArt/**/*.tga

# 构建
**/Build/*

# 白名单 PakBlacklist-<BuildConfiguration>.txt 文件
!**/Build/*/
**/Build/*/**
!**/Build/*/PakBlacklist*.txt

# 不要忽略 Build 中的图标文件
!**/Build/**/*.ico

# 由编辑器生成的配置文件
**/Saved/*
**/Intermediate/*
**/DerivedDataCache/*
**/Binaries/*
**/Build/*
**/Releases/*
**/Packaged/*
```

</details>

**考虑使用** [**多阶段 Docker 构建（链接）**](https://docs.docker.com/build/building/multi-stage/)**.**

* 将大型服务器依赖项拆分到单独的镜像中，以便在多阶段构建中复用。Docker 会缓存每一层，并简单地复用上一版本，除非特别指示，否则会跳过上传这一部分，从而为你节省带宽和等待上传完成的时间。
* 如果你不确定为什么某个 Dockerfile 命令会报错，可以尝试在本地调试。在问题发生前创建一个新阶段（再添加一个第二个 `FROM` 命令），使用 `--target` 指示构建过程在有问题的阶段停止，然后 `docker exec -it {container} /bin/bash` 进入容器内部的交互式终端。之后，你可以在基础镜像中使用 shell 命令进一步排查（例如 `top` 在 ubuntu 上）。

### 自定义服务器镜像

我们也支持为那些由于构建体积优化、额外依赖或更复杂启动流程而需要更多镜像控制的用户添加自己的 Dockerfile。接下来我们将分享一些“自己动手”的技巧和最佳实践。

[^1]: 个人访问令牌
