跳转至

Horde 技术框架深度解析:Epic Games 的 CICD 实战之道


UE5 技术交流群

加入 UE5 技术交流群

如果您对虚幻引擎5的图形渲染技术感兴趣,欢迎加入我们的 UE5 技术交流群!

扫描上方二维码添加个人微信 wlxklyh,备注"UE5技术交流",我会拉您进群。

在技术交流群中,您可以: - 与其他UE开发者交流渲染技术经验 - 获取最新的GDC技术分享和解读 - 讨论图形编程、性能优化、构建工具流、动画系统等话题 - 分享引擎架构、基建工具等项目经验和技术难题


源视频信息:[UFSH2025]Horde技术框架入门 | Jack Condon Epic Games 开发者关系资深工程师
视频链接:https://www.bilibili.com/video/BV1F3UEBBETR
视频时长:43分45秒
内容说明:本文由 AI 基于视频内容生成,结合截图与字幕进行深度技术解析


导读

核心观点一:Horde 不仅仅是一个 CICD 系统,它是 Epic Games 为 Unreal Engine 生态量身打造的模块化开发基础设施平台。

核心观点二:与传统 CICD 工具(Jenkins、TeamCity)不同,Horde 深度集成 BuildGraph 和 Perforce,将构建逻辑与调度系统解耦,实现更灵活的分布式编译与测试。

核心观点三:通过 Horde + UGS + Gauntlet 的组合拳,可以构建从代码提交到多平台自动化测试的完整 DevOps 闭环。

前置知识要求: - 熟悉 Unreal Engine 项目结构 - 了解基本的 CICD 概念 - 有 Perforce 或 Git 版本控制经验 - 理解 BuildGraph 脚本系统(推荐但非必需)


第一部分:背景与痛点 - 为什么需要 Horde?

游戏开发中的构建困境

在大型 Unreal Engine 项目开发中,团队常常面临以下痛点:

Screenshot 1

测试不足导致的设备兼容性问题:开发阶段缺乏对目标平台的充分测试,导致打包后才发现性能或兼容性问题。这在移动端和主机平台尤为突出。

Screenshot 2

代码冲突与构建破坏:当多人协作时,未经验证的代码提交可能破坏主干构建,影响整个团队的开发进度。传统的"在我机器上能跑"问题在这里尤为常见。

引擎编译的时间黑洞:团队成员各自编译引擎源码,不仅浪费大量 CPU 资源,还导致环境不一致。一个中型团队每天可能浪费数十小时在重复编译上。

Screenshot 3

QA 测试周期过长:手动测试流程繁琐,覆盖率低,无法在每次提交后快速验证功能完整性。

CICD 的核心价值

Screenshot 4

持续集成(Continuous Integration) 的本质是:每次代码推送时,自动触发构建和测试流程,及早发现问题。

Screenshot 5

持续交付/部署(Continuous Delivery/Deployment) 则进一步将构建产物自动部署到 QA 环境、预发布环境甚至生产环境。

Screenshot 6

这种自动化流程的核心优势在于: - 快速反馈:从提交到发现问题的时间从数小时缩短到数分钟 - 环境一致性:所有构建在标准化的 Agent 机器上执行 - 资源优化:集中式编译避免重复劳动 - 质量保障:强制执行测试门禁,提升代码质量


第二部分:Horde 核心架构解析

Horde 的定位与特性

Screenshot 7

Horde 是 Epic Games 内部用于支持 Fortnite、Unreal Engine 等项目的服务集合。它的核心特点是:

为 Unreal Engine 深度优化:

Screenshot 8

  • 工作空间结构与 Epic 的项目组织方式一致
  • 原生集成 UAT(Unreal Automation Tool)、Gauntlet 测试框架、UGS(Unreal Game Sync)
  • 提供开箱即用的构建模板,大幅降低配置成本

Screenshot 9

模块化设计:Horde 的功能是可插拔的,你可以只使用其中的某些模块,例如: - 仅作为 UBA(Unreal Build Accelerator)的远程编译协调器 - 仅用于管理预编译二进制文件(Precompiled Binaries) - 作为完整的 CICD 平台

Screenshot 10

经过实战检验:Horde 不仅服务于 Fortnite 这样的超大规模项目,还支持 Unreal Engine 本身的开发,甚至 Epic Games Store(Fab)也依赖它。这意味着它在高并发、大规模团队场景下的稳定性已得到充分验证。

Screenshot 11

深度绑定 Perforce 和 BuildGraph:这是 Horde 的双刃剑。如果你的团队已经在使用这两个工具,Horde 的集成会非常顺滑;但如果你使用 Git 或其他构建系统,则需要额外的适配工作。

架构层级:从 Global 到 Stream

Screenshot 12

Horde 的配置采用分层设计,这与典型的企业项目组织结构高度契合:

Screenshot 13

配置文件层级关系:

Global.json (全局配置)
    ├── Project.json (项目配置)
    │       ├── Stream.json (流配置)
    │       │       ├── Job Templates (作业模板)
    │       │       └── Parameters (参数定义)
    │       └── Stream.json (另一个流)
    └── Project.json (另一个项目)

这种设计的优势在于: - 权限分离:全局配置由 DevOps 团队管理,Stream 配置可以授权给各开发分支的负责人 - 配置复用:通用设置在上层定义,下层继承并覆盖 - 版本控制:配置文件可以存储在 Perforce 中,与代码同步演进

Screenshot 14

组织结构映射: - 公司级别:一个 Perforce 服务器,对应一个 Global 配置 - 项目级别:多个游戏项目(如 ProjectA、ProjectB),每个对应一个 Project 配置 - 分支级别:每个项目的 Main、Dev、Release 分支,对应不同的 Stream 配置


第三部分:快速上手 - 从零搭建 Horde 环境

安装 Horde Server

Screenshot 15

Windows 平台快速部署:

Screenshot 16

最简单的方式是从 GitHub Releases 下载 MSI 安装包:

https://github.com/EpicGames/UnrealEngine/releases

Screenshot 17

选择与你的 Unreal Engine 版本匹配的 Horde 版本(例如 5.5.4),下载 Horde-Server-{version}.msi。

Screenshot 18

安装完成后,Horde 会自动启动一个本地 Web 服务(默认端口 13340),你可以通过浏览器访问 http://localhost:13340。

Screenshot 19

首次启动的欢迎页面:

Screenshot 20

安装后的欢迎页面提供了详细的文档链接,包括配置指南、API 文档等。这些文档会随着 Horde 版本更新而同步,确保你看到的是最新的信息。

Screenshot 21

版本信息查询:在 Horde Dashboard 的设置页面可以查看当前安装的确切版本号,这对于后续排查问题非常重要。

配置第一个项目:以 Lyra 为例

Screenshot 22

Horde 的配置主要通过 JSON 文件完成。虽然文件数量较多,但逻辑清晰。

步骤 1:在 Global 配置中注册项目

Screenshot 23

编辑 Global.json,在 BuildPlugin 部分添加项目引用:

{
  "plugins": {
    "build": {
      "projects": [
        {
          "id": "lyra",
          "name": "Lyra Sample Game",
          "path": "projects/lyra.project.json"
        }
      ]
    }
  }
}

Screenshot 24

这里的 path 是相对于 Global.json 的路径。Horde 提供了大量示例配置,你可以直接复制 ue.project.json 并重命名为 lyra.project.json。

Screenshot 25

步骤 2:定义 Project 配置

lyra.project.json 的主要职责是声明该项目包含哪些 Stream(分支):

{
  "id": "lyra",
  "name": "Lyra",
  "streams": [
    {
      "id": "lyra-main",
      "name": "Main",
      "path": "streams/lyra-main.stream.json"
    }
  ],
  "logo": "/images/lyra-logo.png"
}

Screenshot 26

步骤 3:配置 Stream 和 Job Templates

Screenshot 27

lyra-main.stream.json 是最核心的配置文件,它定义了该分支可以执行哪些构建任务(Job)。

Screenshot 28

Horde 预置了丰富的 Job 模板,涵盖常见场景:

Screenshot 29

预置的 Job 类别:

Screenshot 30

  • 增量构建(Incremental Builds):为 UGS 用户生成预编译二进制文件
  • 打包与测试(Package & Test):支持多平台打包并自动运行测试

Screenshot 31

  • 预提交测试(Pre-submit Tests):在代码合入前运行验证,通过后自动提交

Screenshot 32

  • 独立引擎构建(Standalone Engine Builds):为不使用 UGS 的团队提供完整引擎编译

Screenshot 33

  • 实用工具示例(Utility Demos):展示如何自定义 UI 和参数

第四部分:Agent 管理 - 分布式构建的基石

Agent 的角色与工作机制

Screenshot 34

当你在 Horde Dashboard 中触发一个 Job 时,可能会看到"等待 Agent"的状态。

Screenshot 35

Agent 是什么?

Screenshot 36

Agent 是运行在物理机或虚拟机上的工作进程,负责执行 Horde 分配的构建任务。它的核心特性包括:

Screenshot 37

  • 主动连接:Agent 主动连接 Horde Server,而非 Server 推送任务
  • 状态监控:Horde 实时监控 Agent 的 CPU、内存、磁盘使用情况

Screenshot 38

  • 任务分配:基于 Pool(资源池)和 Agent 属性进行智能调度

Screenshot 39

例如,一个需要 Windows + UE5 环境的任务,只会分配给标记为 Win-UE5 Pool 的 Agent。

安装和注册 Agent

Screenshot 40

下载 Agent 安装包:

Screenshot 41

在 Horde Dashboard 的 Downloads 页面,可以找到针对不同平台的 Agent 安装包。

Screenshot 42

对于 Windows,直接下载 MSI 安装包并运行。

Screenshot 43

Agent 注册流程(Enrollment):

安装完成后,Agent 会自动弹出注册请求窗口。这是一个安全机制,确保只有经过授权的机器才能加入 Horde 集群。

在 Horde Dashboard 的 Agent Enrollment 页面,你会看到待批准的 Agent 请求,点击批准即可完成握手。

Screenshot 44

Agent 信息面板:

Screenshot 45

注册成功后,你可以在 Agent 列表中看到: - 状态(Status):Idle(空闲)、Working(工作中)、Offline(离线) - 当前任务:如果正在执行任务,会显示 Job 链接

Screenshot 46

  • 磁盘空间:防止因磁盘满导致构建失败

Screenshot 47

  • 所属 Pool:决定该 Agent 可以接收哪些类型的任务

Screenshot 48

Pool 机制:任务与资源的匹配

Screenshot 49

Pool 的设计哲学:

Pool 本质上是一种标签系统,用于描述 Agent 的能力。例如:

Screenshot 50

  • Win-UE5:Windows 平台,安装了 UE5 开发环境
  • Mac-UE5:macOS 平台,可用于 iOS/Mac 构建

Screenshot 51

  • Win-UE5-GPU:Windows + 独立显卡,用于运行图形测试

Screenshot 52

自动分配规则:

Screenshot 53

在 Global.json 中可以定义 Pool 的自动分配条件:

{
  "compute": {
    "pools": [
      {
        "id": "win-ue5",
        "condition": "OSFamily == 'Windows' && HasUE5 == true"
      }
    ]
  }
}

Screenshot 54

实用技巧:运行 HordeAgent.exe --ListProperties 可以查看当前机器的所有可用属性,这对于编写 Pool 规则非常有帮助。

第一次成功的构建

Screenshot 55

当 Agent 准备就绪后,重新触发之前等待的 Job:

Screenshot 56

Horde 会自动找到匹配的 Agent 并开始执行。

Screenshot 57

任务分配逻辑:

Screenshot 58

Job 模板中定义的 pool 参数(如 Win-UE5)与 Agent 的 Pool 标签匹配,Horde 会优先选择空闲且符合条件的 Agent。

Screenshot 59

构建成功的标志:

Screenshot 60

构建完成后,你可以在 Job 详情页看到:

Screenshot 61

  • 生成的 Artifacts:可下载的构建产物(如 .zip 包)

Screenshot 62

  • 日志输出:详细的编译和打包日志
  • 执行时间:每个步骤的耗时统计

第五部分:深入 BuildGraph - Horde 的任务引擎

BuildGraph 与 Horde 的关系

Screenshot 63

关键认知:Horde 本身并不执行构建逻辑,它只是调度器。真正的构建工作由 BuildGraph 完成。

Screenshot 64

Job 的本质:

Screenshot 65

一个 Horde Job 实际上是对 BuildGraph 的一次调用,包含:

Screenshot 66

  • Script 路径:指向 .xml 格式的 BuildGraph 脚本
  • Target 名称:要执行的具体 Node(节点)

Screenshot 67

  • 参数传递:从 Horde UI 收集的用户输入,转化为 BuildGraph 的命令行参数

Screenshot 68

引擎自带的强大脚本:

UE5 在 Engine/Build/Graph/ 目录下提供了 BuildAndTestProject.xml,这是一个功能完备的构建脚本,支持: - 多平台编译(Windows、Mac、Linux、Android、iOS、Console) - 自动化测试(Editor Tests、Packaged Tests、Gauntlet) - 增量构建优化 - DDC(Derived Data Cache)管理

大多数团队可以直接使用这个脚本,无需从零编写 BuildGraph。

Job 参数的 UI 映射

Screenshot 69

用户看到的界面:

当你在 Horde Dashboard 中启动一个 Job 时,会看到一系列可配置的选项(如平台、配置、是否运行测试等)。

Screenshot 70

背后的配置:

这些选项在 Stream.json 的 parameters 字段中定义:

{
  "templates": [
    {
      "id": "package-build",
      "name": "Package Build",
      "parameters": [
        {
          "name": "Platform",
          "type": "List",
          "options": ["Win64", "Android", "iOS"],
          "default": "Win64"
        },
        {
          "name": "Configuration",
          "type": "List",
          "options": ["Development", "Shipping"],
          "default": "Development"
        }
      ]
    }
  ]
}

Screenshot 71

参数类型支持:

Screenshot 72

  • List:下拉选择框

Screenshot 73

  • Bool:复选框
  • Text:文本输入框
  • Tag:用于 BuildGraph 的 -set 参数

Screenshot 74

这些参数最终会转化为 BuildGraph 的命令行参数,例如:

RunUAT.bat BuildGraph 
  -Script=BuildAndTestProject.xml 
  -Target=PackageAndTest 
  -set:Platform=Android 
  -set:Configuration=Shipping

实用工具:Template Editor

Screenshot 75

可视化编辑器:

Screenshot 76

Horde 提供了一个在线的 Template Editor,可以实时预览 JSON 配置对应的 UI 效果。

Screenshot 77

使用流程: 1. 在 Horde Dashboard 的 Utilities 页面找到 Template Editor 2. 粘贴你的 Job 模板 JSON

Screenshot 78

  1. 实时查看 UI 渲染结果
  2. 调整满意后,复制回 Stream.json

Screenshot 79

虽然不是完全的所见即所得编辑器,但对于快速迭代 UI 布局非常有用。


第六部分:自动化测试 - 从编译到设备验证

打包与测试的完整流程

Screenshot 80

触发一个完整的测试 Job:

Screenshot 81

选择 "Package and Test" 模板,配置目标平台后启动。你会看到 Job 被拆分为多个 Step: 1. Compile Editor:编译编辑器版本 2. Cook Content:烘焙资源 3. Package Build:打包目标平台 4. Run Editor Tests:在编辑器中运行测试 5. Run Packaged Tests:在打包后的 Build 中运行测试

Screenshot 82

Agent Pool 的分层使用:

Screenshot 83

注意到测试步骤需要 Win-UE5-GPU Pool。这是因为运行图形测试需要独立显卡,而编译步骤只需要 Win-UE5 Pool。

Screenshot 84

Windows Service vs User Mode

Agent 的两种运行模式:

Screenshot 85

  • Windows Service 模式:后台运行,开机自启,但无法启动 GUI 应用(如 UE Editor)
  • User Mode:在用户登录后运行,可以启动 Editor 和打包后的游戏

Screenshot 86

切换到 User Mode:

如果你需要运行 Editor Tests 或 Packaged Tests,必须: 1. 停止 Horde Agent Service 2. 以当前用户身份运行 HordeAgent.exe

Screenshot 87

Editor Tests vs Packaged Tests:

Screenshot 88

  • Editor Tests:在 UnrealEditor.exe 中运行,速度快,适合快速验证逻辑

Screenshot 89

  • Packaged Tests:在打包后的 .exe 中运行,更接近真实环境,能发现 Shipping 配置特有的问题

Screenshot 90

自动化测试的实际运行:

Screenshot 91

当测试开始时,你会看到 Editor 或游戏自动启动,执行预定义的测试用例,然后自动关闭。整个过程无需人工干预。

Screenshot 92

测试完成后,Horde 会收集测试报告,并在 Dashboard 中展示通过/失败的用例。


第七部分:Pre-flight 机制 - 代码合入前的最后防线

"在我机器上能跑"问题的终结者

Screenshot 93

经典场景:开发者在本地测试通过后提交代码,结果破坏了主干构建,导致整个团队无法工作。

Screenshot 94

Pre-flight 的核心思想:在代码真正合入主干之前,先在 Horde 上运行一遍完整的构建和测试流程。只有通过后,才允许提交。

Screenshot 95

配置 P4V 扩展

Screenshot 96

下载 P4V Extension:

Screenshot 97

在 Horde Dashboard 的 Downloads 页面,找到 "Perforce Extensions" 下载包。

Screenshot 98

安装步骤:

  1. 解压下载的 .zip 文件
  2. 运行 Install.bat,这会向 P4V 注册自定义菜单

Screenshot 99

  1. 编辑 HordeExtension.ini,填入 Horde Server 的 URL:
[Horde]
ServerUrl=http://your-horde-server:13340

Screenshot 100

这个扩展不仅支持 Pre-flight,还提供了很多便捷功能(如快速查看 Job 状态、同步到特定 CL 等)。

使用 Pre-flight 提交代码

Screenshot 101

在 P4V 中触发 Pre-flight:

Screenshot 102

  1. 右键点击你的 Pending Changelist

Screenshot 103

  1. 选择 "Horde -> Pre-flight and Submit"

Screenshot 104

  1. P4V 会自动打开 Horde Dashboard,并预填好 Shelved CL 编号

Screenshot 105

  1. 勾选 "Auto-submit if successful",然后启动 Job

Screenshot 106

工作流程:

Screenshot 107

  • Horde 会从 Perforce 拉取你的 Shelved 变更
  • 在干净的 Agent 环境中应用这些变更
  • 运行预定义的测试(编译、单元测试、打包等)
  • 如果全部通过,自动执行 p4 submit
  • 如果失败,保留 Shelved CL,你可以修复后重试

Screenshot 108

适用于任何 Job:

Screenshot 109

实际上,Horde 的每个 Job 模板都支持 "Shelved CL" 和 "Auto-submit" 选项。你甚至可以在 Pre-flight 中运行完整的打包和设备测试,确保代码在所有目标平台上都能正常工作。

Screenshot 110

P4V Extension 的本质:

Screenshot 111

这个扩展只是一个便捷的启动器,它的作用是自动填充 Horde Web 页面的表单。即使不安装扩展,你也可以手动在 Horde Dashboard 中输入 Shelved CL 编号来触发 Pre-flight。


第八部分:调度策略 - 自动化触发与 Nightly Builds

Schedule 配置详解

Screenshot 112

问题场景:每次提交代码后都要手动触发 Job 吗?能否在每晚自动运行完整的测试套件?

Screenshot 113

答案是 Schedule 机制:

Screenshot 114

在 Stream.json 的 Job 模板中,可以配置 schedule 字段,定义自动触发条件。

Screenshot 115

Schedule 配置示例:

{
  "templates": [
    {
      "id": "incremental-build",
      "name": "Incremental Build",
      "schedule": {
        "enabled": true,
        "maxActive": 2,
        "maxChanges": 10,
        "requireSubmittedChange": true,
        "filter": ["*.cpp", "*.h", "*.cs"],
        "patterns": [
          {
            "interval": 5,
            "minTime": "09:00",
            "maxTime": "18:00"
          }
        ]
      }
    }
  ]
}

Screenshot 116

参数解析:

Screenshot 117

  • enabled:总开关,方便调试时临时禁用

Screenshot 118

  • maxActive:同时运行的 Job 数量上限(防止资源耗尽)

Screenshot 119

  • maxChanges:允许 Schedule 落后主干的最大 CL 数量(避免积压过多)

Screenshot 120

  • requireSubmittedChange:只有在有新提交时才触发(避免空跑)

Screenshot 121

  • filter:文件过滤规则

Screenshot 122

例如 ["*.cpp", "*.h"] 表示只有 C++ 代码变更时才触发,美术资源变更不触发编译。

Screenshot 123

  • patterns:轮询策略

Screenshot 124

interval: 5 表示每 5 分钟检查一次 Perforce,minTime/maxTime 限制触发时间窗口(如工作时间)。

监控 Schedule 状态

Screenshot 125

Dashboard 视图:

在 Stream 的 Summary 页面,可以看到所有启用的 Schedule,包括上次触发时间、下次预计触发时间等。

Screenshot 126

日志查看:

Horde Server 的日志中会记录每次轮询的详细信息,包括: - 检测到的新 CL - 是否满足 Filter 条件 - 为什么触发或跳过

Screenshot 127

这对于调试 Schedule 规则非常有用。更多高级配置(如 Cron 表达式、依赖触发等)可以参考官方文档。


第九部分:UGS 集成 - 为团队提供统一的代码同步体验

UGS 的价值主张

Screenshot 128

什么是 Unreal Game Sync(UGS)?

Screenshot 129

UGS 是 Epic 开发的 Perforce 前端工具,专为 Unreal Engine 项目优化。它的核心优势包括:

Screenshot 130

  • 智能过滤同步:只同步当前平台需要的文件(如 Windows 开发者不同步 Mac 二进制)
  • 自动引擎编译:检测到引擎代码变更时,自动触发本地编译

Screenshot 131

  • 引擎与项目锁步:确保引擎版本与项目代码始终匹配
  • 元数据展示:在 CL 列表中显示构建状态、测试结果等信息

Screenshot 132

  • 预编译二进制下载:无需本地编译引擎,直接下载 Horde 构建的二进制文件

Screenshot 133

历史痛点:

过去,UGS 需要单独搭建 Metadata Server(通常是 SQL Server),配置复杂。现在,Horde 原生支持 UGS Metadata,一行配置即可搞定。

快速部署 UGS

Screenshot 134

步骤 1:配置 Perforce 地址

Screenshot 135

在 Global.json 中添加 Perforce 服务器信息:

{
  "perforce": {
    "servers": [
      {
        "serverAndPort": "perforce.company.com:1666",
        "userName": "build-user"
      }
    ]
  }
}

这样,团队成员安装 UGS 时,服务器地址会自动填充。

Screenshot 136

步骤 2:下载并安装 UGS

Screenshot 137

在 Horde Dashboard 的 Downloads 页面,下载 UnrealGameSync-{version}.msi。

Screenshot 138

安装时,只需输入 Horde Server 的 URL,其他配置会自动从服务器拉取。

Screenshot 139

步骤 3:打开项目

Screenshot 140

UGS 的"项目"概念是 Stream + UProject 文件 的组合。

Screenshot 141

选择你的 Perforce Stream(如 //Lyra/Main),然后选择 LyraStarterGame.uproject。

Screenshot 142

UGS 会自动同步代码,并根据需要编译引擎。

配置 UGS Metadata

Screenshot 143

Metadata 的作用:

Screenshot 144

UGS 可以在每个 CL 旁边显示额外信息,例如: - 该 CL 的构建状态(成功/失败) - 测试覆盖率 - 性能基准测试结果 - 开发者备注

Screenshot 145

启用 Horde Metadata 服务:

Screenshot 146

编辑引擎目录下的 Engine/Programs/UnrealGameSync/UnrealGameSync.ini:

[Default]
MetadataServer=http://your-horde-server:13340

Screenshot 147

注意:这个文件应该提交到引擎的 Perforce Stream 中,而不是项目 Stream。这样所有使用该引擎的项目都能共享配置。

Screenshot 148

配置完成后,重启 UGS,你会看到 CL 列表中出现构建状态图标。

Screenshot 149

对比传统方案:

Screenshot 150

以前需要搭建独立的 SQL Server、配置数据库权限、编写同步脚本。现在只需一行配置,Horde 内置的 MongoDB 会自动处理所有数据存储。

全局消息通知

Screenshot 151

场景需求:

Screenshot 152

当项目进入 Feature Lock 阶段,或者主干构建被破坏时,需要通知所有开发者停止提交代码。

Screenshot 153

UGS 消息窗口:

Screenshot 154

可以在 UnrealGameSync.ini 中配置全局消息:

[Default]
MetadataServer=http://horde-server:13340

[Notifications]
Message=Feature Lock in effect! Please do not submit code until further notice.
MessageColor=Red
MessageLink=https://wiki.company.com/feature-lock-policy

Screenshot 155

也可以针对特定 Stream 配置消息。在项目的 Build/UnrealGameSync.ini 中:

[//Lyra/Main]
Message=Main branch is currently broken. Use Dev branch instead.
MessageColor=Yellow

Screenshot 156

消息会在 UGS 顶部以醒目的横幅显示,支持超链接。


第十部分:预编译二进制 - 让美术和策划告别编译

问题背景

Screenshot 157

典型矛盾:

Screenshot 158

  • 程序员:需要引擎源码,方便调试和定制
  • 美术/策划:只想用编辑器,不关心源码,更不想等待 30 分钟的编译

Screenshot 159

UGS 的解决方案:

UGS 可以自动检测引擎代码变更,并在同步后触发本地编译。但这仍然需要每个人的机器都有完整的编译环境。

Screenshot 160

更优雅的方案:

Screenshot 161

让 Horde 在服务器上编译引擎,生成预编译二进制(Precompiled Binaries),然后团队成员直接下载。

Screenshot 162

这样,非程序员无需安装 Visual Studio,也无需等待编译。

启用预编译二进制下载

Screenshot 163

在 UGS 中切换模式:

Screenshot 164

打开 UGS 的 Options 页面,选择 "Use Precompiled Binaries"。

Screenshot 165

切换后,你会发现很多 CL 变成灰色,这表示这些 CL 没有可用的预编译二进制。

Screenshot 166

生成预编译二进制:

Screenshot 167

在 Horde Dashboard 中运行 "Incremental Build" Job。这个 Job 会: 1. 编译引擎的所有模块 2. 将二进制文件打包成 .zip 3. 上传到 Horde 的 Artifact 存储

Screenshot 168

自动化策略:

建议为 "Incremental Build" Job 配置 Schedule,在每次引擎代码提交后自动触发。这样,团队成员在 UGS 中始终能找到最新的预编译二进制。


第十一部分:跨平台测试 - 从 PC 到移动设备的自动化

设备管理:Device Dashboard

Screenshot 169

挑战场景:

Screenshot 170

  • 团队只有一台 iPhone 13,多个开发者需要轮流测试
  • 每次测试都要手动打包、手动部署、手动运行

Screenshot 171

Horde + Gauntlet 的解决方案:

Screenshot 172

Gauntlet 是 UE 自带的设备测试框架,Horde 提供了设备管理和调度功能。

Screenshot 173

配置 Android 设备测试

Screenshot 174

步骤 1:在 Horde 中注册设备

Screenshot 175

在 Horde Dashboard 的 Devices 页面,点击 "Add Device":

Screenshot 176

{
  "name": "Samsung Galaxy S21",
  "platform": "Android",
  "address": "192.168.1.100:5555"  // ADB 连接地址
}

Screenshot 177

步骤 2:指定负责的 Agent

Screenshot 178

设备必须连接到某台 Agent 机器(通过 USB 或 ADB over Wi-Fi)。

Screenshot 179

步骤 3:创建 DevKit Pool

Screenshot 180

在 Global.json 中定义一个新的 Pool:

{
  "compute": {
    "pools": [
      {
        "id": "win-devkit-automation",
        "name": "Windows DevKit Automation"
      }
    ]
  }
}

Screenshot 181

然后手动将负责设备的 Agent 加入这个 Pool。

Screenshot 182

Screenshot 183

步骤 4:运行测试 Job

Screenshot 184

触发 "Package and Test" Job,选择 Android 平台。Horde 会: 1. 在编译 Agent 上打包 APK 2. 将 APK 传输到 DevKit Agent 3. 通过 ADB 安装到设备 4. 运行 Gauntlet 测试脚本 5. 收集测试结果并上传

Screenshot 185

实际运行效果:

Screenshot 186

你会看到设备自动启动游戏,执行预定义的操作(如进入关卡、触发特定事件),然后自动退出。

Screenshot 187

整个过程无需人工干预,测试报告会自动出现在 Horde Dashboard 中。


第十二部分:Blueprint 测试 - 让 TA 也能写自动化测试

Functional Test 插件

Screenshot 188

设计哲学:

Screenshot 189

传统的自动化测试需要 C++ 代码,这对非程序员是个门槛。UE 的 Functional Test 插件允许用 Blueprint 编写测试用例。

Screenshot 190

创建测试:

Screenshot 191

  1. 启用 "Functional Testing Editor" 插件
  2. 创建一个继承自 FunctionalTest 的 Blueprint 类

Screenshot 192

  1. 命名规则:必须以 FTest_ 开头(如 FTest_PerformanceCheck),这样 Gauntlet 才能自动发现

Screenshot 193

示例:FPS 性能测试

Screenshot 194

以下是一个简单的性能测试 Blueprint: - 播放一个 Level Sequence - 每帧记录 FPS - 测试结束时,检查最低 FPS 是否达标 - 使用 Finish Test 节点报告成功/失败

Screenshot 195

重要提示:这只是演示概念,真正的性能测试应该使用 Unreal Insights 和 Gauntlet 的专用性能分析工具。

Screenshot 196

本地验证:

Screenshot 197

在编辑器中打开 Session Frontend,可以看到所有 FTest_ 开头的测试。点击运行,立即查看结果。

Screenshot 198

测试日志会输出到 Output Log,包括自定义的性能数据(如最高/最低/平均 FPS)。

在 Horde 中查看测试结果

Screenshot 199

Automation Hub:

Screenshot 200

Horde 的 Automation Hub 页面聚合了所有项目的测试结果。你可以快速看到哪些 CL 引入了测试失败。

Screenshot 201

失败详情:

Screenshot 202

点击失败的测试,可以看到完整的日志输出,包括 Blueprint 中打印的自定义消息。

Screenshot 203

例如,性能测试失败时,日志会显示:

[FTest_PerformanceCheck] Minimum FPS: 28 (Expected: 30)
[FTest_PerformanceCheck] Test FAILED

Screenshot 204

趋势分析:

Screenshot 205

Horde 会保留历史测试数据,你可以生成趋势图,观察性能是否随版本迭代而下降。

Screenshot 206

(注:为了演示失败场景,示例中故意在场景中放置了大量高多边形模型以降低 FPS)


第十三部分:高级主题 - Docker 部署与配置管理

为什么选择 Docker?

Screenshot 207

跨平台需求:

Screenshot 208

虽然 Windows MSI 安装简单,但很多团队需要在 Linux 服务器上部署 Horde(尤其是云环境)。

Screenshot 209

一致性保证:

Screenshot 210

Docker 容器确保 Horde Server、MongoDB、Redis 等依赖的版本完全一致,避免环境差异导致的问题。

Screenshot 211

构建 Horde Docker 镜像

Screenshot 212

使用 BuildGraph 脚本:

Screenshot 213

UE 源码中包含 Engine/Source/Programs/Horde/BuildHorde.xml,这是一个专门用于构建 Horde 的 BuildGraph 脚本。

Screenshot 214

关键节点:

Screenshot 215

  • Build Horde Server:编译 Horde 后端(.NET 应用)
  • Build Horde Dashboard:编译前端(React 应用)
  • Build Horde Agent:编译 Agent 程序
  • Build UGS:编译 Unreal Game Sync

Screenshot 216

  • Build Bundled Docker Image:将所有组件打包成一个 Docker 镜像

Screenshot 217

执行构建:

RunUAT.bat BuildGraph 
  -Script=Engine/Source/Programs/Horde/BuildHorde.xml 
  -Target="Build Bundled Docker Image" 
  -set:OutputDir=D:/HordeBuild

Screenshot 218

构建完成后,你会得到一个 Docker 镜像文件,可以部署到任何支持 Docker 的环境。

使用 Docker Compose 部署

Screenshot 219

依赖管理:

Screenshot 220

Horde 依赖 MongoDB(存储配置和元数据)和 Redis(缓存和任务队列)。

Screenshot 221

示例 docker-compose.yml:

version: '3.8'
services:
  horde-server:
    image: horde-server:latest
    ports:
      - "13340:13340"
    volumes:
      - ./data:/app/data
    depends_on:
      - mongo
      - redis

  mongo:
    image: mongo:6.0
    volumes:
      - mongo-data:/data/db

  redis:
    image: redis:7.0
    volumes:
      - redis-data:/data

volumes:
  mongo-data:
  redis-data:

Screenshot 222

启动服务:

docker-compose up -d

Screenshot 223

配置文件挂载:

Screenshot 224

注意 volumes 部分将 ./data 目录挂载到容器内。这样,你可以在宿主机上编辑 Global.json 等配置文件,Horde 会自动重新加载。

Screenshot 225

但在生产环境中,这种方式不够灵活。更好的做法是将配置存储在 Perforce 中。

配置文件的版本控制

Screenshot 226

问题:

如果 Horde Server 部署在云端,如何方便地更新配置?每次都要 SSH 登录服务器修改文件吗?

Screenshot 227

解决方案:Perforce 托管配置

Screenshot 228

Horde 支持直接从 Perforce 读取配置文件。在 Server.json 中:

{
  "globalConfig": "//HordeConfig/Main/Global.json",
  "perforce": {
    "userName": "horde-service",
    "password": "your-password"
  }
}

Screenshot 229

优势:

Screenshot 230

  • 版本历史:所有配置变更都有 Perforce 历史记录
  • Code Review:配置修改可以走 Shelved CL + Pre-flight 流程

Screenshot 231

  • 权限控制:利用 Perforce 的 ACL 管理谁能修改哪些配置

Screenshot 232

路径解析规则:

Screenshot 233

当配置文件从 Perforce 加载时,其中的相对路径会基于 Perforce 的工作空间解析。

Screenshot 234

最佳实践:

Screenshot 235

  • 将 Global.json 和 Project.json 存储在独立的 Perforce Stream(如 //HordeConfig/Main)

Screenshot 236

  • 将 Stream.json 存储在各项目的代码库中(如 //Lyra/Main/Build/Horde/Stream.json)

Screenshot 237

这样,项目团队可以自主管理自己的 Job 定义,而全局配置由 DevOps 团队集中管理。

Screenshot 238

Screenshot 239


第十四部分:开发者体验优化

JSON Schema 智能提示

Screenshot 240

痛点:

Screenshot 241

Horde 的配置文件字段众多,手写 JSON 容易出错。

Screenshot 242

解决方案:JSON Schema

Screenshot 243

Horde Server 提供了完整的 JSON Schema 定义,可以在 Visual Studio Code 或 Visual Studio 中启用智能提示。

Screenshot 244

配置方法(Visual Studio):

Screenshot 245

  1. 打开 工具 -> 选项 -> 文本编辑器 -> JSON -> Schema
  2. 添加 Schema Catalog URL:
http://your-horde-server:13340/api/v1/schema

Screenshot 246

效果:

编辑 Global.json 时,输入 " 会自动弹出可用字段列表,鼠标悬停会显示字段说明。这极大降低了配置错误率。

用户权限与认证

Screenshot 247

默认行为:

Screenshot 248

Horde 初始安装时没有启用认证,任何人都可以访问和操作。这在本地测试时很方便,但生产环境必须启用权限控制。

Screenshot 249

认证方式选择:

Screenshot 250

Horde 支持两种认证方式: - OIDC(OpenID Connect):对接企业的 SSO 系统(如 Okta、Azure AD) - Horde 内置认证:简单的用户名/密码系统

Screenshot 251

启用内置认证:

编辑 Server.json:

{
  "authMethod": "Horde"
}

Screenshot 252

重启 Horde Server 后,首次访问会要求设置管理员密码。

Screenshot 253

用户管理:

Screenshot 254

在 Dashboard 的 User Accounts 页面,可以创建用户并分配到不同的组:

Screenshot 255

  • Admins:完全控制权限
  • Developers:可以启动 Job、查看日志
  • Viewers:只读权限,适合 QA 和管理层

Screenshot 256

权限粒度:

Screenshot 257

权限可以细化到 Project 和 Stream 级别。例如,ProjectA 的开发者无法启动 ProjectB 的 Job。

Screenshot 258

用户可以在 User Preferences 页面查看自己的权限列表,方便排查权限问题。


第十五部分:扩展与集成

HTTP API:自动化的无限可能

Screenshot 259

Swagger 文档:

Screenshot 260

Horde 提供了完整的 RESTful API,所有 Dashboard 的功能都可以通过 API 调用。

Screenshot 261

访问 http://your-horde-server:13340/swagger 可以查看完整的 API 文档,并在线测试。

Screenshot 262

示例:创建全局通知

Screenshot 263

API Endpoint: POST /api/v1/notices

请求体:

{
  "message": "Server maintenance scheduled at 2PM",
  "startTime": "2024-01-15T14:00:00Z",
  "finishTime": "2024-01-15T16:00:00Z"
}

Screenshot 264

Swagger UI 允许你直接在浏览器中测试 API,无需编写代码。

Screenshot 265

实际应用场景:

Screenshot 266

  • CI/CD 集成:在 GitLab Pipeline 中调用 Horde API 触发构建
  • 监控告警:当 Horde Job 失败时,通过 API 查询详情并发送到 Slack
  • 自定义 Dashboard:为非技术人员制作简化版的构建状态页面

Screenshot 267

在 UE Editor 中集成 Horde:

Screenshot 268

示例:创建一个 Editor Utility Widget,显示当前项目的构建状态。开发者无需离开编辑器,就能知道主干是否健康。

Slack 集成

Screenshot 269

通知机制:

Screenshot 270

Horde 内置 Slack 集成,可以在构建失败时自动 @ 相关开发者。

配置方法: 1. 在 Slack 中创建 Incoming Webhook 2. 在 Global.json 中配置:

{
  "notifications": {
    "slack": {
      "webhookUrl": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
      "channel": "#build-alerts"
    }
  }
}
  1. 在 Job 模板中启用通知:
{
  "templates": [
    {
      "id": "main-build",
      "notifications": {
        "onFailure": ["slack"]
      }
    }
  ]
}

当构建失败时,Slack 会收到包含 Job 链接、失败原因、责任人(基于 Perforce 提交记录)的消息。

UBA 与 Zen Server:编译加速的未来

Screenshot 271

UBA(Unreal Build Accelerator):

Screenshot 272

Horde 可以作为 UBA 的协调器,将本地编译任务分发到远程 Agent,实现分布式编译。这类似于 IncrediBuild,但完全免费且深度集成 UE。

Screenshot 273

Zen Server 作为 Cooked Output Store:

Screenshot 274

在移动端开发时,每次迭代都要重新 Cook 和部署几 GB 的资源。启用 Zen Server 后:

Screenshot 275

  • 设备上只部署一个小的配置文件(指向 Zen Server 的 IP)
  • 游戏运行时按需从 Zen Server 流式加载资源

Screenshot 276

这将部署时间从 5 分钟缩短到 10 秒,极大提升移动端迭代速度。


第十六部分:Studio Telemetry - 数据驱动的 DevOps 决策

为什么需要遥测数据?

Screenshot 277

盲目优化的问题:

Screenshot 278

很多团队在投入 DevOps 工具时,不知道应该优先解决哪个痛点。是编译慢?还是 DDC 命中率低?还是编辑器启动慢?

Screenshot 279

Studio Telemetry 插件:

这是 UE 内置的遥测系统,可以自动收集开发者的工作数据,例如: - 编辑器启动时间 - 编译耗时 - DDC 命中率 - 崩溃频率 - 资源加载时间

Screenshot 280

数据会上传到 Horde,并在 Dashboard 中生成可视化报表。

配置 Telemetry

Screenshot 281

步骤 1:启用插件

在项目的 DefaultEngine.ini 中:

[StudioTelemetry]
Enabled=true
ServerUrl=http://your-horde-server:13340

Screenshot 282

步骤 2:配置 Dashboard 视图

Screenshot 283

在 Global.json 中定义要展示的指标:

{
  "telemetry": {
    "stores": [
      {
        "id": "editor-startup",
        "metrics": [
          {
            "name": "EditorStartupTime",
            "aggregation": "Average"
          }
        ]
      }
    ],
    "views": [
      {
        "id": "dev-experience",
        "name": "Developer Experience",
        "charts": [
          {
            "title": "Editor Startup Time (7 Days)",
            "metric": "EditorStartupTime"
          }
        ]
      }
    ]
  }
}

Screenshot 284

Horde 提供了丰富的示例配置,涵盖常见的性能指标。

Screenshot 285

步骤 3:查看报表

在 Dashboard 的 Telemetry 页面,你可以看到: - 团队平均编辑器启动时间的趋势 - DDC 命中率是否因为服务器问题下降 - 哪些开发者的机器配置明显低于团队平均水平

这些数据可以指导你优先投入资源解决最影响效率的问题。


第十七部分:实战总结与最佳实践

方案对比:Horde vs 传统 CICD

方案 A:Horde - 🟢 优势: - 开箱即用的 UE 集成(UAT、Gauntlet、UGS) - 深度绑定 BuildGraph,任务定义与调度解耦 - 预编译二进制、设备管理等游戏开发特有功能 - Epic 内部实战验证,稳定性有保障 - 🔴 劣势: - 强依赖 Perforce(Git 支持需要额外工作) - 社区生态不如 Jenkins/GitLab CI 成熟 - 学习曲线较陡(需理解 BuildGraph) - 🎯 适用场景: - 使用 Perforce 的 UE 项目 - 需要多平台自动化测试 - 团队规模 > 10 人

方案 B:Jenkins + 自定义脚本 - 🟢 优势: - 灵活性极高,可以集成任何工具 - 丰富的插件生态 - 社区支持强大 - 🔴 劣势: - 需要从零配置 UE 相关的所有流程 - 缺少 UGS、预编译二进制等开箱即用功能 - 维护成本高(需要专人负责) - 🎯 适用场景: - 使用 Git 且不打算切换到 Perforce - 团队有专职 DevOps 工程师 - 需要集成非 UE 的构建流程(如后端服务)

避坑指南

坑 1:Agent 的 Service 模式无法运行 GUI 测试

  • 现象:Job 卡在"等待 Agent",或者测试步骤被跳过
  • 原因:Windows Service 无法启动 UE Editor 或打包后的游戏
  • 解决:将测试 Agent 切换到 User Mode,并确保用户保持登录状态

坑 2:BuildGraph 脚本路径错误

  • 现象:Job 启动失败,提示找不到 .xml 文件
  • 原因:Horde 配置中的路径是相对于 Perforce Workspace 的,不是相对于本地磁盘
  • 解决:使用 Perforce 路径格式(如 //UE5/Main/Engine/Build/Graph/BuildAndTestProject.xml)

坑 3:预编译二进制版本不匹配

  • 现象:UGS 下载了预编译二进制,但编辑器启动失败
  • 原因:Incremental Build Job 使用的引擎版本与当前 Stream 不一致
  • 解决:确保 Incremental Build 的 Schedule 配置了正确的 Stream 过滤规则

坑 4:设备测试失败但日志不清晰

  • 现象:Gauntlet 测试报告显示失败,但看不到具体错误
  • 原因:设备日志没有正确上传到 Horde
  • 解决:在 BuildGraph 脚本中添加 <Property Name="GauntletLogDir" Value="$(RootDir)/Logs" />,确保日志被标记为 Artifact

坑 5:配置修改不生效

  • 现象:修改了 Global.json,但 Dashboard 没有变化
  • 原因:Horde 有配置缓存机制
  • 解决:在 Dashboard 的 Settings 页面点击 "Reload Configuration",或重启 Horde Server

最佳实践清单

✅ 配置管理 - 将 Global.json 和 Project.json 存储在独立的 Perforce Stream - 将 Stream.json 存储在各项目的代码库中 - 使用 JSON Schema 启用智能提示 - 定期备份 MongoDB 数据(包含 Job 历史和 Telemetry 数据)

✅ Agent 规划 - 编译 Agent:高 CPU 核心数(16+),大内存(64GB+),SSD - 测试 Agent:独立显卡,保持用户登录状态 - DevKit Agent:物理连接测试设备,稳定的网络 - 使用 Pool 标签精细控制任务分配

✅ Job 设计 - 优先使用引擎自带的 BuildAndTestProject.xml,避免重复造轮子 - 为不同场景创建多个 Job 模板(快速验证 vs 完整测试) - 配置合理的 Schedule,避免资源浪费 - 使用 Pre-flight 作为代码合入的强制门禁

✅ 团队协作 - 为非程序员启用预编译二进制下载 - 使用 UGS 的消息通知功能同步团队状态 - 配置 Slack 集成,及时通知构建失败 - 定期查看 Telemetry 数据,识别效率瓶颈

✅ 安全与权限 - 生产环境必须启用认证 - 使用 Perforce ACL 控制配置文件的修改权限 - 为不同角色创建专用账号(如 horde-service、build-agent) - 定期审计 Admin 权限的使用


结语

Screenshot 286

Horde 的核心价值:

Screenshot 287

Horde 不仅仅是一个构建工具,它代表了 Epic Games 在大规模游戏开发中积累的 DevOps 最佳实践。通过将 CICD、版本控制、测试自动化、遥测分析整合到一个平台,Horde 帮助团队: - 缩短反馈周期:从提交到发现问题,从数小时降到数分钟 - 提升代码质量:Pre-flight 机制强制执行测试门禁 - 优化资源利用:预编译二进制、分布式编译避免重复劳动 - 数据驱动决策:Telemetry 数据指导 DevOps 投入方向

Screenshot 288

持续学习资源:

Screenshot 289

  • 官方文档:Horde Dashboard 内置的文档会随版本更新
  • GitHub Issues:https://github.com/EpicGames/UnrealEngine (需要 Epic 账号)
  • Unreal Slackers 社区:Discord 服务器中有 #horde 频道
  • GDC Talks:搜索 "Epic Games Build Infrastructure" 可以找到相关演讲

Screenshot 290

下一步行动:

如果你的团队正在考虑引入 Horde,建议按以下步骤推进: 1. 概念验证(1 周):在本地安装 Horde,配置一个简单的编译 Job 2. 小规模试点(2-4 周):选择一个非关键项目,配置完整的 CICD 流程 3. 团队培训(1 周):为开发者讲解 UGS、Pre-flight 等工具的使用 4. 全面推广(1-2 月):逐步将所有项目迁移到 Horde,优化 Agent 配置 5. 持续优化:基于 Telemetry 数据,不断调整 Schedule 和测试策略

Screenshot 291

感谢阅读这篇长文!希望它能帮助你理解 Horde 的设计哲学,并在实际项目中发挥价值。如果有任何问题,欢迎在 UE5 技术交流群中讨论。


关于作者:本文基于 Jack Condon(Epic Games 开发者关系资深工程师)在 UFSH2025 上的演讲整理而成。Jack 在 Epic 负责 Horde 的推广和技术支持,拥有丰富的大规模项目 DevOps 经验。

版权声明:本文内容来源于公开技术分享,截图版权归 Epic Games 所有。文章仅供学习交流使用,不得用于商业用途。