# Unity

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

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

## ✔️ 准备工作

<details>

<summary><a href="https://github.com/edgegap/edgegap-unity-plugin">安装 Edgegap 的 Unity 专用服务器快速入门插件</a></summary>

</details>

{% hint style="info" %}
**对你的服务器构建有信心吗？** 跳转到 [#customize-server-image](#customize-server-image "mention") 或 [高级功能](/zh/learn/advanced-features.md) 了解更多。
{% endhint %}

## ⚙️ 1. 连接账户

☑️ 登录并确认你的 Unity 控制台中没有与 Edgegap 插件相关的新错误。

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

<details>

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

`!Success: 400 BAD REQUEST - POST | https://api.edgegap.com/v1/wizard/init-quick-start - {"message": "浏览器（或代理）发送了此服务器无法理解的请求。"}`

* 如果你是通过复制 ZIP 文件安装的，或者使用了一个以这种方式复制安装了插件的示例项目，你需要手动安装包依赖项，包括 Newtonsoft JSON 库，见 [插件官方仓库](https://github.com/edgegap/edgegap-unity-plugin/tree/main?tab=readme-ov-file#instructions-1).
* 如果不是这种情况，请通过 [社区 Discord](https://discord.gg/NgCnkHbsGp) 联系我们寻求帮助。

</details>

## 🔧 2. 构建游戏服务器

无论你使用的是 Windows、Mac 还是 Linux 机器，你都 **需要为 Linux 运行时构建你的服务器**，因为如今大多数云服务提供商（包括 Edgegap）都运行在 Linux 上。别担心，使用我们的插件完成这一步不需要任何 Linux 知识。

☑️ **确认你已安装所需的 Unity Linux 构建工具。**

☑️ 编辑构建设置以 **确保包含所有必需的游戏场景**.

{% hint style="info" %}
**高级 Unity 用户** - 可选地更改 [Unity 构建设置](https://docs.unity3d.com/Manual/BuildSettings.html)。注意！这可能会破坏你的构建。
{% endhint %}

☑️ 可选：从 Edgegap Server Hosting 菜单（在 Hierarchy 窗口中右键 / :heavy\_plus\_sign: ）向你的初始服务器场景添加用于端口验证和环境初始化的 netcode 专用脚本。

<figure><img src="/files/eff6c33e6df7153e5ed6e1c644217fe278c7a43b" alt="" width="360"><figcaption></figcaption></figure>

{% hint style="info" %}
完成步骤 [#id-6.-deploy-a-server-on-edgegap](#id-6.-deploy-a-server-on-edgegap "mention")后，如果你的 netcode 地址或端口与 Edgegap 的 [应用版本端口映射](/zh/learn/bian-pai/application-and-versions.md#other-parameters-optional) 配置不匹配，端口验证脚本将记录警告。
{% endhint %}

{% hint style="success" %}
服务器构建应在 netcode 传输中使用地址 `0.0.0.0`  和端口 `7777`  。如果你自定义了端口，请在你的 [应用和版本](/zh/learn/bian-pai/application-and-versions.md#port-mapping) 中指定相同的值，等你完成后 [#id-5.-upload-to-edgegap](#id-5.-upload-to-edgegap "mention").
{% endhint %}

☑️ 配置完成后，点击 **构建服务器**，等待过程结束，并确认你的 Unity 控制台中没有新的错误。完成这一步后，你的 **项目根目录中将出现一个新文件夹** - `Builds/EdgegapServer/ServerBuild` .

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

<details>

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

Unity：唯一支持的独立目标是带 OpenXR 的 Windows x64 和 OSX。

* 在构建服务器之前，打开 Packages 并禁用 OpenXR。
* OpenXR 插件仅客户端需要，与 Linux 服务器构建不兼容。将其从服务器构建中排除不会损失任何功能。

</details>

## 🐋 3. 将服务器容器化

在开发团队中工作意味着要共享代码。出问题时，你最不想听到的就是“在我机器上是好的”。游戏服务器必须能在任何机器上稳定运行，因为一个成功游戏的服务器会在全球成千上万台服务器上运行。

为了让你的服务器更可靠，我们使用 Docker——一种虚拟化软件，用于确保从操作系统层面起你的所有服务器代码依赖无论服务器如何、在何处启动，都始终完全一致。

☑️ 现在，先点击 **验证** 按钮，以确保你已完成 [开发者工具](/zh/unity/developer-tools.md#usage-requirements).

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

* **构建路径** 是你的服务器构建产物的相对路径，暂时保留默认值即可。

{% hint style="warning" %}
Docker 只接受相对于项目根文件夹的构建路径， **请将构建保留在项目文件夹内**.
{% endhint %}

* **镜像名称** 是你自定义的唯一标识，用于在发布前标记你的服务器构建。
  * 通常，这会包含你的游戏名称——例如“my-game-server”。
* **镜像标签** 是指向镜像特定版本的标识符。
  * 术语“构建产物”有时也用于指代镜像的特定版本。
  * 时间戳是很好的标签选择，例如 `2024.01.30-16.23.00-UTC` .
* **Dockerfile 路径** 可用于自定义你的镜像构建方案。
  * 我们建议暂时保留默认设置，你可以稍后在章节中了解更多 [#customize-server-image](#customize-server-image "mention").
* **可选的 docker 构建参数** 可用于进一步向 Docker 指定更细微的设置。
  * 我们建议暂时保留默认设置，你可以 [稍后在 Docker 文档中阅读更多内容](https://docs.docker.com/reference/cli/docker/image/build/#options).

☑️ 配置完成后，点击 **使用 Docker 将其容器化**，等待过程结束，并确认你的 Unity 控制台中没有新的错误。完成这一步后，你的 **新镜像会出现在你的本地机器上**。你可以在 Docker Desktop 的 Local（默认）下的 Images 选项卡中验证，或者在 docker CLI 中运行 `docker images` .

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

<details>

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

`/bin/bash：docker：未找到命令` ，或者 `找不到 Packages\com.edgegap.unity-servers-plugin\Editor`

* 首先，确保你已完成 [开发者工具](/zh/unity/developer-tools.md#usage-requirements).
* 确认你已经验证了你的 Edgegap 账户，你应该已经通过电子邮件收到了验证链接。
* 在更新 Docker Desktop 后，某些设置可能会被重置。请尝试前往 Docker Desktop Settings / Advanced，并在“Choose how to configure the installation of Docker’s CLI tools:”中选择“System (requires password)”。

***

`docker build 需要且仅需要 1 个参数`

* 请确认你的镜像标签不包含任何空白字符（空格、制表符）。重新输入镜像标签值可确保你没有意外复制此类字符。

***

`(HTTP code 400) unexpected - invalid tag format`

* 这是一个 [macOS Docker 版本 4.33 的已知问题](https://github.com/docker/for-win/issues/14258)，请考虑回退到 4.32 或升级到 4.35。

***

`ERROR: failed to solve: ubuntu:22.04: failed to resolve source metadata for http://docker.io/library/ubuntu:22.04: failed to authorize: failed to fetch oauth token`

* 你在中国吗？你的连接可能会被防火长城中断。请尝试手动运行 `docker pull ubuntu:22.04` 在命令行中（按 Win+R 打开命令行，然后输入 `cmd` 并按回车）。

***

`System.IndexOutOfRangeException: Index 超出了数组范围。`

* 如果你是通过下载 ZIP 安装我们的 Unity 快速入门插件，你的 Unity Editor 缓存可能已损坏。请尝试删除插件副本，并改用 git URL 或 Unity 资产商店安装。你将不再需要 Newtonsoft.JSON 包，因为它会与其他源一起自动包含。

***

我的 docker 镜像大小非常大（超过 1GB）/ 非常小（低于 100mb），这样可以吗？

* 在某些情况下这可能没问题，只要你能运行服务器并成功连接（见 [#id-4.-test-your-server-locally](#id-4.-test-your-server-locally "mention")）。如果不是这种情况，请考虑检查你的构建选项，将其重置为默认值，并逐步添加选项来查看它们如何影响构建大小。另请参见 [#optimize-server-build-size](#optimize-server-build-size "mention").

***

我遇到了本文档任何地方都未提到的其他问题。

* 首先，请尝试 [更新你的 Edgegap 插件](https://github.com/edgegap/edgegap-unity-plugin?tab=readme-ov-file#update-the-plugin-in-unity) ，因为我们可能已经发布了修复。如果这没有帮助，请通过我们的 [社区 Discord](https://discord.gg/NgCnkHbsGp) 联系我们，我们会立即与你一起调查。

</details>

## 🧪 4. 在本地测试服务器

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

* **服务器镜像标签** 来自上一步。
  * 默认使用你使用插件构建的最新标签。
* **可选的 docker run 参数** 可用于公开多个端口，或在 macOS 机器上运行你的镜像。
  * 如果需要，你可以为容器发布多个端口，只需为每个端口添加参数 `-p {内部端口}/{协议}` ，例如 `-p 8080/tcp -p 7777/udp` 以便同时发布并映射你的服务器端口 `8080` 到一个随机外部端口用于 TCP 连接，以及服务器端口 `7777` 到一个随机外部端口用于 UDP 连接。
  * **在你的 Transport 或特定 netcode 设置中查找服务器端口配置。**
  * 如果你使用的是 ARM 架构机器（macOS M1、M2、M3 等），你应该会在可选 docker 构建参数中看到此可选参数： `--platform=linux/amd64` .

☑️ 配置完成后，点击 **部署本地容器**，等待过程结束，并确认你的 Unity 控制台中没有新的错误。完成这一步后， **将启动一个新容器** 在你的开发机器上。

{% hint style="info" %}
更多详情请参阅 Docker Desktop / Containers，或 Docker CLI 命令 `docker ps` .
{% endhint %}

☑️ 现在是时候 **将你的 Unity Editor 游戏客户端连接到本地 docker 容器** 以验证你的服务器镜像是否正常工作。找到你的 netcode 客户端设置并输入：

* `localhost` 或 `0.0.0.0` （在大多数情况下等同）替代服务器 IP，
* Docker Desktop / Containers / edgegap-server-test 中找到的随机外部端口值。

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

<details>

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

我无法使用 Unity Editor 游戏客户端连接到本地 docker 容器。

* 首先，确保容器状态为 Up，而不是 Restarting 或 Exited，这通常表示运行时异常。如果你的容器没有运行，请通过 Docker Desktop 的 Containers 选项卡（点击你的容器）查看其日志，或者使用 `docker logs {container_id} --timestamps` 通过 docker CLI。
* 接下来，请确认你服务器构建中的 Network Manager 端口设置与 **可选的 docker run 参数**中发布的端口一致。如果不一致，请尝试重置或手动更改此输入字段的值以匹配 `{container}` 端口到你的 Network Manager 设置。在你的 netcode 设置中查找协议。
* 最后，请确认你的 Unity Editor 游戏客户端 netcode 设置使用的是在 **可选的 docker run 参数** 中发布的端口（见上方截图）。

***

`（Segmentation fault）- core dumped`

* 如果你使用的是 ARM 架构机器（macOS M1、M2、M3 等），你应该会在可选 docker 构建参数中看到此可选参数： `--platform=linux/amd64` 。如果不是，请尝试重置此输入字段的值。

***

`在 SceneObjects 中找不到 SceneId 9120233082191360994。`

* 这可能意味着你尝试加载的场景未正确包含在构建中，这是旧版插件中的已知问题。为解决此问题，请尝试更新你的 netcode 集成版本，或者 [更新你的 Edgegap 插件](https://github.com/edgegap/edgegap-unity-plugin?tab=readme-ov-file#update-the-plugin-in-unity).

***

`http2: server: error reading preface from client //./pipe/docker_engine: file has already been closed`

* 这是一个 [Docker Desktop for Windows 旧版本的已知问题](https://github.com/docker/for-win/issues/13611)。请更新你的 Docker Desktop 应用并重试容器化。

***

`Curl error 35: Cert handshake failed. Fatal error. UnityTls error code: 7`

* 此错误表明根 SSL 证书验证存在问题，这是旧版插件中的已知问题。为修复它，请尝试 [更新你的 Edgegap 插件](https://github.com/edgegap/edgegap-unity-plugin?tab=readme-ov-file#update-the-plugin-in-unity).

</details>

## ☁️ 5. 上传到 Edgegap

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

* **应用名称** 在 Edgegap 上可以与你的镜像名称一致，也可以自定义。
  * 我们目前选择复制你的镜像名称。
* **应用版本** 在 Edgegap 上可以与你的标签一致，也可以自定义。
  * 时间戳是应用版本名称的很好的选择，例如 `2024.01.30-16.50.20-UTC` .
  * 多个应用版本可能指向同一个镜像标签，例如 `v1.1.0` 和 `dev` .
  * 了解更多关于 [应用和版本](/zh/learn/bian-pai/application-and-versions.md) 稍后。
* **服务器镜像名称** 来自步骤 [#id-3.-containerize-your-game-server](#id-3.-containerize-your-game-server "mention").
* **服务器镜像标签** 来自步骤 [#id-3.-containerize-your-game-server](#id-3.-containerize-your-game-server "mention").

{% hint style="success" %}
在 **Docker Desktop / Images**.
{% endhint %}

☑️ 配置完成后，点击 **上传镜像并创建应用版本**，等待过程结束，并确认你的 Unity 控制台中没有新的错误。

☑️ 你将被带到我们的 [Dashboard](https://app.edgegap.com/)，在那里你可以配置可选设置。完成这一步后，将会 [创建一个新的应用版本](https://app.edgegap.com/application-management/applications/list)，并且你的 [构建产物将被标记并上传到 Edgegap 的容器仓库](https://app.edgegap.com/registry-management/repositories/list).

☑️ 现在系统会提示你为新的应用版本定义一个端口。请确保设置与你在步骤 [#id-4.-test-your-server-locally](#id-4.-test-your-server-locally "mention") 中的服务器端口值相同，来源于你的 Transport 或特定 netcode 设置。

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

## 🚀 6. 部署到云端

☑️ 现在我们将执行最后的测试并 **将你的 Unity Editor 游戏客户端连接到云端部署**。输入来自该部署的游戏客户端连接详细信息：

* **主机** **URL** 指向服务器 IP，通常在 `NetworkManager` 组件中。
* **外部端口** 映射到 [服务器的内部监听端口](/zh/learn/bian-pai/application-and-versions.md#port-mapping)，通常在 Transport 组件中。

<details>

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

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

* 首先，确保部署状态为 Ready，并且你的部署日志中没有运行时异常或错误。如果你的部署已停止，请在我们的 [Dashboard](https://app.edgegap.com/deployment-management/deployments/list).
* 如果你使用的是 Mirror netcode，你需要在你的 [“Auto Start Server”](https://mirror-networking.gitbook.io/docs/hosting/edgegap-hosting-plugin-guide#build-and-push) 中选择 `NetworkManager` ，然后重新构建、推送并重新部署你的服务器。
* 如果你使用的是 FishNet netcode，你需要在你的 [“Start on Headless”](https://fish-networking.gitbook.io/docs/manual/components/managers/server-manager#settings-are-general-settings-related-to-the-servermanager) 中启用 `ServerManager`，然后重新构建、推送并重新部署你的服务器。
* 如果你使用的是 Photon Fusion 2 netcode，请确保你的服务器正在传递部署公网 IP、外部端口以及 `roomCode` 给服务器，并在客户端的 [“NeworkRunner.StartGame”](https://doc.photonengine.com/fusion/current/manual/network-runner#creating-or-joining-a-room) 参数 `StartGameArgs`中使用相同的房间代码。部署 ID（例如 `b63e6003b19f`）是个很好的选择，因为它在全球范围内唯一，并且客户端可通过 [Matchmaker](/zh/learn/pi-pei/matchmaker-in-depth.md) 和 [深入了解](/zh/learn/pi-pei/matchmaker-in-depth.md#injected-environment-variables).
* 接下来，请确认你服务器构建中的 netcode 设置里的端口设置与 [应用版本](https://app.edgegap.com/application-management/applications/list)中的内部端口一致。你可以通过编辑 [应用版本](https://app.edgegap.com/application-management/applications/list) 来更改端口映射，而无需重新构建。在你的 netcode 集成中查找协议。
* 请确保你的游戏客户端连接的是部署详情页面上显示的 **外部端口** ，出于安全原因，此值始终会随机化。
* 如果你在 netcode 集成中使用的是安全 WebSocket（WSS）协议，请确保你的 [应用版本](https://app.edgegap.com/application-management/applications/list) WSS 端口的端口配置已启用 TLS 升级。
* 你在中国并且正在使用 [Smart Fleets](https://docs.edgegap.com/docs/deployment/session/fleet-manager/fleet)吗？你的连接可能会被防火长城阻止。请考虑在你的舰队中添加一个位于中国的服务器，或者使用 VPN 连接。

***

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

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

***

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

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

***

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

* 一旦部署状态变为 Ready，你的游戏引擎初始化就会开始。此过程可能需要几秒到几分钟，在此期间服务器不会接受玩家连接。
* 考虑优化你的服务器初始化以缩短这段时间。
* 游戏客户端应以 1 秒间隔重试连接一小段时间（取决于你的初始化时长），之后应返回匹配。
* 考虑添加一个加载场景，以便服务器可以与客户端同时执行初始化（在 Unreal Engine 中还可同时进行关卡切换），同时同步两边的状态。

***

我的 Meta Quest 设备出现 `HTTP 0: 无法解析目标主机` .

* 在为 Android 目标构建 Unity 应用时，你的 Internet Access 权限可能会从输出的 APK 客户端构建产物中被自动移除。
* 重新添加权限（需要之后重新构建客户端）：
  * Project Settings / OpenXR / :gear: Meta Quest Support / Force Remove Internet Permissions（取消勾选）。
  * Player Settings / Internet Access（设置为 require）。

***

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

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

***

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

* 这可能不是问题，因为游戏引擎在服务器初始化期间往往会执行 CPU 密集型操作。如果从部署开始后的 2-3 分钟 CPU 使用率仍未下降，你可能需要优化服务器或增加应用版本资源。
* 降低 tick rate 可以影响 CPU 使用率，因为服务器会执行更少的消息操作。
* 如果你使用的是 Mirror netcode，你需要在你的 [“Auto Start Server”](https://mirror-networking.gitbook.io/docs/hosting/edgegap-hosting-plugin-guide#build-and-push) 中选择 `NetworkManager` ，然后重新构建、推送并重新部署你的服务器。
* 如果你使用的是 FishNet netcode，你需要在你的 [“Start on Headless”](https://fish-networking.gitbook.io/docs/manual/components/managers/server-manager#settings-are-general-settings-related-to-the-servermanager) 中启用 `ServerManager`，然后重新构建、推送并重新部署你的服务器。
* 免费层限制为 1.5 vCPU 和 3GB 内存（RAM）。
* 在创建新应用版本时，你可以增加分配的资源。你可以在 Dashboard 中复制你的应用版本并按需调整这些值，而无需重新构建服务器或镜像。

***

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

* 这是由超出分配的内存量引起的。请考虑通过对象池、压缩或删除场景中不需要的对象来优化内存使用。
* 确保你的项目正在加载包含你的 `NetworkManager` 的默认场景，并且该场景已包含在 Unity 的 Build Settings 中。
* 免费层限制为 1.5 vCPU 和 3GB 内存（RAM）。
* 在创建新应用版本时，你可以增加分配的资源。你可以在 Dashboard 中复制你的应用版本并按需调整这些值，而无需重新构建服务器或镜像。

***

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

* 只要你保持在分配的应用版本内存量以内，这就不是问题。
* 超过分配的应用版本内存量将导致 `OOM kill` （见上文）。

***

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

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

</details>

## 👉 下一步

### 停止部署

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

对于平滑关闭，我们强烈建议在你的游戏中实现自停止 API：

<details>

<summary>Unity C# 自停止 API 代码片段示例</summary>

```csharp
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class EdgegapSelfStop : MonoBehaviour
{
    private const int MaxRetries = 2;
    private const int TimeoutSeconds = 30;
    private string url = Environment.GetEnvironmentVariable("ARBITRIUM_DELETE_URL");
    private string token = Environment.GetEnvironmentVariable("ARBITRIUM_DELETE_TOKEN");

    public void Run()
    {
        StartCoroutine(SendDeleteWithRetry());
    }

    private IEnumerator SendDeleteWithRetry()
    {
        if (string.IsNullOrEmpty(url))
        {
            Debug.LogError("Edgegap | ARBITRIUM_DELETE_URL is not set.");
            yield break;
        }

        if (string.IsNullOrEmpty(token))
        {
            Debug.LogError("Edgegap | ARBITRIUM_DELETE_TOKEN is not set.");
            yield break;
        }

        int attempt = 0;

        while (attempt <= MaxRetries)
        {
            attempt++;

            using (UnityWebRequest request = UnityWebRequest.Delete(url))
            {
                request.timeout = TimeoutSeconds;
                request.SetRequestHeader("Authorization", token);

                yield return request.SendWebRequest();

                if (request.result == UnityWebRequest.Result.Success)
                {
                    Debug.Log("Edgegap | DELETE request succeeded.");
                    yield break;
                }

                Debug.LogWarning(
                    $"Edgegap | DELETE attempt {attempt} failed.\n" +
                    $"Result: {request.result}, Error: {request.error}"
                );

                if (attempt == MaxRetries)
                {
                    Debug.LogError("Edgegap | DELETE request failed after all retries.");
                    yield break;
                }
            }
        }
    }
}
```

</details>

{% hint style="info" %}
如果发生崩溃或内存耗尽，你的 Unity 服务器将自动重启。
{% endhint %}

### 注入变量

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

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

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

```csharp
string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ARBITRIUM_REQUEST_ID"))
  // 客户端代码
)
{
  } else {
// 服务器代码
  }
}
```

### 匹配

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

{% hint style="success" %}
[**阅读更多关于匹配的信息**](/zh/learn/pi-pei.md) **即可自动、准时地部署**，当玩家上线时。
{% endhint %}

### 优化服务器构建

**仅重新构建自上次构建以来发生更改的资源。**

可以考虑使用 [Unity 的增量构建](https://docs.unity3d.com/Manual/incremental-build-pipeline.html) 来加快构建时间。

* 可以考虑使用 [Unity 的增量构建](https://docs.unity3d.com/Manual/incremental-build-pipeline.html) 来加快构建时间。

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

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

**禁用网格的静态批处理以减小镜像大小。**

* [禁用静态批处理可加快构建、上传和部署速度。](https://docs.unity3d.com/Manual/DrawCallBatching.html)

**压缩网格以减小镜像大小。**

* [将网格压缩设为 High 以加快构建、上传和部署速度。](https://docs.unity3d.com/6000.0/Documentation/Manual/compressing-mesh-data-optimization.html)
* 顶点压缩不会影响镜像大小。

**实现资源的条件式延迟加载。**

* 通过以下方式排除仅客户端使用的资源 [将纹理和网格设置为禁用 CPU 读写](https://docs.unity3d.com/6000.0/Documentation/Manual/dedicated-server-optimizations.html).
* 可以考虑使用 [Unity Addressables](https://docs.unity3d.com/Packages/com.unity.addressables@2.1/manual/index.html) 用于你的客户端构建，以通过 [按需加载资源](https://docs.unity3d.com/Packages/com.unity.addressables@1.19/manual/LoadingAddressableAssets.html)来加快构建和部署速度，或者通过检查是否存在 [部署](/zh/learn/bian-pai/deployments.md#injected-environment-variables).

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

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

### 自定义服务器镜像

我们也支持添加你自己的 Dockerfile，适用于因构建大小优化、额外依赖或更复杂启动流程而需要更强控制镜像的用户。你可以在步骤 [#id-3.-containerize-your-game-server](#id-3.-containerize-your-game-server "mention")中可选地提供自定义 Dockerfile 的路径。现在我们会分享一些“自己动手”的技巧和最佳实践。

**在使用 Websockets 或 HTTPS 请求时遇到问题？**

* 如果你遇到 `Curl error 35: Cert handshake failed. Fatal error. UnityTls error code: 7` 别慌，这在较旧的基础（`FROM`）镜像中是已知问题，其中包含过期的根证书。你可以通过更新到较新的基础镜像版本（例如 `ubuntu:22.04`），并运行 `update-ca-certificates` 来修复它，请将以下内容添加到你的 Dockerfile：

  ```docker
  FROM ubuntu:22.04

  RUN apt-get install -y ca-certificates && \
      apt-get clean && \
      update-ca-certificates
  ```

{% hint style="info" %}
遇到难题了？我们在我们的 [社区 Discord](https://discord.gg/MmJf8fWjnt) 中随时乐意提供帮助。
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.edgegap.com/zh/unity.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
