> For the complete documentation index, see [llms.txt](https://docs.edgegap.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.edgegap.com/zh/docs/sample-projects/unity-netcodes/mirror-on-edgegap-websocket.md).

# Mirror WebGL

本指南将帮助你使用 [Mirror](https://mirror-networking.com/)的 Websocket Transport，并为 Unity 项目在 Edgegap 上创建一个无头服务器。

本指南将使用开源示例项目 `Tanks`，可在 Mirror 示例中的以下位置找到 `Assets/Mirror/Examples/Tanks`.

你可以在我们的 [GitHub](https://github.com/edgegap/mirror-webgl)

### 台球示例

如果你更喜欢通过台球示例观看逐步视频教程，请在此观看：

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

{% hint style="success" %}
本视频展示了使用最新 [Unity](/zh/unity.md) 构建工具的更新后的构建流程。
{% endhint %}

### 切换传输

在准备构建游戏服务器之前，我们首先需要对基础场景做一些更改。

* 打开 `Scene.unity` 位于 `Assets/Mirror/Examples/Tanks/Scenes`;
* 在 `NetworkManager` 游戏对象中，移除 `KcpTransport` 脚本，并将其替换为 `SimpleWebTransport` 位于 `Assets/Mirror/Transports/SimpleWeb`，同时还要更新 `NetworkManager` 脚本组件中的 Transport 字段以使用这个新传输。请确保 `Auto Start Server Build` 选项也已勾选。
* 根据你的 Mirror 版本，你可能需要更改/更新 `NetworkManagerHUD` ，以便它能与除 KCP 之外的其他传输方式一起工作，并允许你在加入服务器前在 HUD 中输入端口值。

请记下用于网络通信的端口，称为 `[GAME PORT]`。在本例中，使用的端口是 `7778`.

### 构建游戏服务器与容器化

{% hint style="info" %}
为了便于容器化和部署过程，可以在我们的 [GitHub](https://github.com/edgegap/edgegap-unity-plugin) 上使用最新版的 Edgegap Unity Plugin 来自动化该过程。有关如何使用此插件的更多信息，请查看我们的 [文档](broken://pages/3abea5cdd9e3a9c090704ebd2b7b5d09a77d50a4).

如果你愿意，也可以按照以下逐步说明进行。
{% endhint %}

准备好游戏后，前往 `Build` Unity 编辑器中的界面，位于 `File -> Build Settings` 顶部菜单中。请根据你的 Unity 版本选择正确的预设。

* 在 2021.2 版本之前：
  * 设置 `目标平台` 设置为 `Linux`;
  * 设置 `架构` 设置为 `x86_64`;
  * 勾选 `Server Build` 选项。
* 否则：
  * 设置 `平台` 设置为 `Dedicated Server`;
  * 设置 `目标平台` 设置为 `Linux`.

然后点击构建，并选择一个名为 `linux_server` 的新空文件夹作为文件目标。将 `linux_server` 文件夹复制到第二个空文件夹中，该文件夹将被称为 `[SERVER BUILD]` 文件夹。将以下 `Dockerfile` 和 `boot.sh` 文件添加到 `[SERVER BUILD]` 文件夹：

#### Dockerfile

```
FROM ubuntu:bionic
MAINTAINER <author_detail>

ARG DEBIAN_FRONTEND=noninteractive
ARG docker_version=17.06.0-ce

RUN
    apt-get update && \
    apt-get install -y libglu1 xvfb libxcursor1 ca-certificates && \
    update-ca-certificates && \
    apt-get clean

EXPOSE 7778/TCP

COPY linux_server/  /root/linux_server/
COPY boot.sh        /boot.sh

WORKDIR /root/
ENTRYPOINT ["/bin/bash", "/boot.sh"]
```

#### boot.sh

```
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24:32' /root/linux_server/[YOUR GAME].x86_64 -batchmode -nographics
```

请确保将 `[YOUR GAME]` 占位符替换为生成文件的名称

然后，在 `[SERVER BUILD]` 文件夹中启动命令提示符；运行以下 Docker 命令来创建构建镜像并将其推送到私有仓库：

{% hint style="warning" %}
对于 ARM CPU（Mac M1、M2 等）用户，请添加 `--platform linux/amd64`  选项到你的构建命令中。
{% endhint %}

#### 使用 Linux

```bash
# 构建镜像
docker build . -t <IMAGE_NAME>:<IMAGE_TAG>

# 登录，提示将要求输入密码
docker login -u '<REGISTRY_USERNAME>' <REGISTRY_URL>

# 为镜像添加另一个与仓库对应的标签
docker image tag <IMAGE_NAME>:<IMAGE_TAG> <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_TAG>

# 推送镜像
docker push <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_TAG>
```

#### 使用 cmd

```bash
# 构建镜像
docker build . -t <IMAGE_NAME>:<IMAGE_TAG>

# 登录，提示将要求输入密码
docker login -u <REGISTRY_USERNAME> <REGISTRY_URL>

# 为镜像添加另一个与仓库对应的标签
docker image tag <IMAGE_NAME>:<IMAGE_TAG> <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_TAG>

# 推送镜像
docker push <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_TAG>
```

#### 使用 PowerShell

```bash
# 构建镜像
docker build . -t <IMAGE_NAME>:<IMAGE_TAG>

# 登录，提示将要求输入密码
docker login -u '<REGISTRY_USERNAME>' <REGISTRY_URL>

# 为镜像添加另一个与仓库对应的标签
docker image tag <IMAGE_NAME>:<IMAGE_TAG> <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_TAG>

# 推送镜像
docker push <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_TAG>
```

### 将服务器部署到 Edgegap

在 Edgegap 仪表板登录后，转到 `Applications & Games` 页面。点击 `Create New` 右上角的按钮进入应用表单。以下是各字段及其正确填写方式：

* 应用名称：可以是你想用于轻松识别该应用的任何显著名称。
* 镜像：可以是你想用于轻松识别该应用的任何特定镜像。
* 版本名称：你可以使用版本名称来描述你正在部署的版本范围。例如“demo”、“production”、“v1”、“v2”
* 容器：
  * 仓库： “\[URL]”，其中 \[URL] 是你可以在 Container Repository 页面显示的凭据值。
  * 镜像仓库： “\[PROJECT]/\[YOUR GAME]”，其中 \[PROJECT] 和 \[YOUR GAME] 是你之前在推送 docker 镜像时使用的值。
  * 标签： “\[TAG]”，其中 \[TAG] 是你之前在推送 docker 镜像时使用的值。
  * 勾选“使用私有仓库”
  * 私有仓库用户名： “\[USERNAME]”，其中 \[USERNAME] 是你的凭据中的值。
  * 私有仓库令牌： “\[TOKEN]”，其中 \[TOKEN] 是你的凭据中的值。
  * 要求：保持不变。
  * 端口：点击 `+ 添加端口` 链接添加一个新端口，并添加以下条目：
    * `7778` - WS - 启用 TLS 升级（Beta）

{% hint style="warning" %}
如果你使用 WSS 选项，务必要启用 `TLS 升级` 选项。否则，你会在 Edgegap 仪表板的容器日志中遇到类似下面的错误。

```cmd
客户端发来的首字节不是用于握手的 'GET'，而是 16-03-01
```

{% endhint %}

<figure><img src="/files/ff001736dec8cfd90af33907b36c5b99c0369ef3" alt=""><figcaption></figcaption></figure>

应用创建完成后，你可以点击 `Deploy` 按钮继续部署你的游戏服务器。当你的部署的最新状态变为 `Ready`时，你就可以使用游戏的客户端版本连接到服务器。请记下 `主机` url，并在你的部署的 Port Mapping 标签页中记下 `外部端口` ，这是公开可用的。

### 测试客户端

#### 在编辑器中

回到 Unity 编辑器中的坦克场景，选择 `NetworkManager` 游戏对象并更改以下设置：

* 在 `Network Manager` 组件：
  * 将 `Network Address` 设置为服务器部署的 `主机` url；
  * 取消勾选 `Auto Start Server Build` 选项。
* 在 `Simple Web Transport` 组件：
  * 将 `Port` 值设置为 Edgegap 部署的 `外部端口` 例如： `32821`;
  * 请确保 `Client Use WSS` 选项已启用。

<figure><img src="/files/1b2381469f9c85237b200f966c5e243c751de16c" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/6cb8bdcfc6c2a945faf2f1ac5850cb326731c1be" alt=""><figcaption></figcaption></figure>

完成后，点击 `Play` 在编辑器中，然后点击 `Client` 按钮；你将连接到服务器，并在片刻后即可开始游戏。

#### 在 Itch.io 上

要将游戏客户端发布到 Itch，你需要构建一个客户端；你需要安装 `WebGL Build Support` 模块来完成此操作。完成后，返回到 `Build` Unity 编辑器中的界面，位于 `File -> Build Settings`，并选择以下选项：

* 设置 `平台` 设置为 `WebGL`;
* 打开 `Player Settings`。在 `Player -> Publishing Settings`中，将 `Compression Format` 设置为 `Gzip` 并确保 `Decompression Fallback` 选项已选中。保存这些设置，并关闭窗口。

<figure><img src="/files/bc590154ba3d4fd3995e3e84f2c24e6012111e08" alt=""><figcaption></figcaption></figure>

然后点击构建，并选择一个名为 `build` 的新空文件夹作为文件目标。将 `build` 文件夹复制到第二个空文件夹中，该文件夹将被称为 `[CLIENT BUILD]` 文件夹。

游戏构建完成后，将此 `build` 文件夹中的文件压缩为一个 zip 压缩包，并确保它们位于压缩包根目录中。然后只需将该 zip 压缩包上传到你的 Itch 项目，并确保选择在浏览器中运行文件的选项。启动游戏后，你只需确保 HUD 中的网络地址和端口值设置正确，然后点击 `Client` 按钮即可开始游戏。

#### 在 Edgegap 上托管客户端

甚至可以在 Edgegap 上托管你的游戏客户端！为此，请添加以下 `Dockerfile` 和 `nginx.conf` 文件添加到 `[CLIENT BUILD]` 文件夹：

**Dockerfile**

```
FROM nginx:alpine
MAINTAINER <author_detail>

COPY build/ /usr/share/nginx/html
copy nginx.conf /etc/nginx/
```

**nginx.conf**

```
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
worker_connections  1024;
}


http {
include       /etc/nginx/mime.types;
default_type  application/octet-stream;

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log  /var/log/nginx/access.log  main;

sendfile        on;
#tcp_nopush     on;

keepalive_timeout  65;

#gzip  on;

include /etc/nginx/conf.d/*.conf;

server {
# 在 http 服务器配置中添加以下配置
# ...

    # 磁盘上的 Brotli 预压缩数据文件应在启用压缩的情况下提供：
    location ~ .+\.(data|symbols\.json)\.br$ {
        # 因为该文件已在磁盘上预先压缩，所以禁用其按需压缩。
        # 否则 nginx 会尝试进行双重压缩。
        gzip off;
        add_header Content-Encoding br;
        default_type application/octet-stream;
    }

    # 磁盘上的 Brotli 预压缩 JavaScript 代码文件：
    location ~ .+\.js\.br$ {
        gzip off; # 不要对已压缩文件尝试动态 gzip 压缩
        add_header Content-Encoding br;
        default_type application/javascript;
    }

    # 磁盘上的 Brotli 预压缩 WebAssembly 文件：
    location ~ .+\.wasm\.br$ {
        gzip off; # 不要对已压缩文件尝试动态 gzip 压缩
        add_header Content-Encoding br;
        # 通过为以下内容指定正确的 MIME 类型来启用流式 WebAssembly 编译：
        # Wasm 文件。
        default_type application/wasm;
    }

    # 磁盘上的 gzip 预压缩数据文件应在启用压缩的情况下提供：
    location ~ .+\.(data|symbols\.json)\.gz$ {
        gzip off; # 不要对已压缩文件尝试动态 gzip 压缩
        add_header Content-Encoding gzip;
        default_type application/octet-stream;
    }

    # 磁盘上的 gzip 预压缩 JavaScript 代码文件：
    location ~ .+\.js\.gz$ {
        gzip off; # 不要对已压缩文件尝试动态 gzip 压缩
        add_header Content-Encoding gzip;
        default_type application/javascript;
    }

    # 磁盘上的 gzip 预压缩 WebAssembly 文件：
    location ~ .+\.wasm\.gz$ {
        gzip off; # 不要对已压缩文件尝试动态 gzip 压缩
        add_header Content-Encoding gzip;
        # 通过为以下内容指定正确的 MIME 类型来启用流式 WebAssembly 编译：
        # Wasm 文件。
        default_type application/wasm;
    }
}
}
```

继续使用 [之前相同的 Docker 命令](#bootsh) 来将你的游戏客户端镜像构建并推送到私有仓库，但请在 `[CLIENT BUILD]` 文件夹中打开的命令窗口里执行。请确保使用不同于服务器的镜像名称。

然后，在 Edgegap 仪表板上为你的客户端创建一个新应用，并使用以下设置：

* 应用名称：可以是你想用于轻松识别该应用的任何显著名称。
* 镜像：可以是你想用于轻松识别该应用的任何特定镜像。
* 版本名称：你可以使用版本名称来描述你正在部署的版本范围。例如“demo”、“production”、“v1”、“v2”
* 容器：
  * 仓库： “\[URL]”，其中 \[URL] 是你可以在 Container Repository 页面显示的凭据值。
  * 镜像仓库： “\[PROJECT]/\[YOUR GAME]”，其中 \[PROJECT] 和 \[YOUR GAME] 是你之前在推送 docker 镜像时使用的值。
  * 标签： “\[TAG]”，其中 \[TAG] 是你之前在推送 docker 镜像时使用的值。
  * 勾选“使用私有仓库”
  * 私有仓库用户名： “\[USERNAME]”，其中 \[USERNAME] 是你的凭据中的值。
  * 私有仓库令牌： “\[TOKEN]”，其中 \[TOKEN] 是你的凭据中的值。
  * 要求：保持不变。
  * 端口：点击 `+ 添加端口` 链接添加一个新端口，并添加以下条目：
    * `80` - HTTPS

<figure><img src="/files/b9461a9a29a774407018b53530908f43f1201ada" alt=""><figcaption></figcaption></figure>

应用创建完成后，你可以点击 `Deploy` 按钮继续部署你的游戏客户端。当服务器和客户端部署都设置为 `Ready`时，打开游戏客户端的 `主机` url，在指定的 `外部端口` 中在浏览器中打开，在游戏 HUD 中设置正确的值后，你就可以开始游戏了！

<figure><img src="/files/6a09602333ae66b8b5f6f050631b4c4cec9804ef" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
如果你需要有关 Mirror 集成和 websocket 的更多信息，可以参考他们的 [文档](https://mirror-networking.gitbook.io/docs/manual/transports/websockets-transport).
{% endhint %}
