Исправление ошибки unreal engine uba executor ubt error при сборке Unreal Engine 5.8 из исходного кода
Коротко о главном
Руководство посвящено устранению критической ошибки сборки Unreal Engine 5.8 из исходного кода, связанной с конфликтом рекурсивных вызовов в Unreal Build Accelerator (UBA). Описаны три способа решения проблемы: отключение UBA в конфигурационном файле BuildConfiguration.xml, использование флагов командной строки и внесение патча в C#-код фабрики ExecutorFactory.cs для автоматического переключения на ParallelExecutor. Дополнительно даны рекомендации по очистке сборочного окружения, настройке кэширования VFS и лимитированию потоков компиляции для избежания нехватки оперативной памяти. В завершение рассматривается оптимизация Multiplayer-инфраструктуры с помощью платформы готового Backend horizOn.
Мало что так сильно тормозит работу студии, как сломанная сборка движка — особенно когда вы компилируете Unreal Engine 5.8.0 из исходного кода, а Visual Studio внезапно прерывает процесс с непонятным исключением. Сборка падает с необработанным исключением, указывающим прямо на UBAExecutor.cs. Этот конкретный блокер, известный как unreal engine uba executor ubt error, полностью останавливает компиляцию. Он мешает сборочному графу (build graph) завершить генерацию критически важных вспомогательных программ, таких как UnrealHeaderTool. В этом руководстве мы разберем, почему Unreal Build Accelerator (UBA) испытывает проблемы с вложенными вызовами, почему дефолтная конфигурация дает сбой и как пропатчить исходный код движка, чтобы восстановить работоспособность сборочного пайплайна (build pipeline).
Архитектура сборочной системы
Чтобы понять причины этой ошибки, необходимо изучить, как Unreal Build Tool (UBT) координирует компиляцию. Кодовые базы Unreal Engine огромны и часто содержат десятки тысяч исходных файлов. Для их эффективной компиляции UBT выступает в роли мета-системы сборки, которая генерирует графы зависимостей и распределяет задачи компиляции.
Что такое Unreal Build Accelerator?
Epic Games представила Unreal Build Accelerator (UBA) в качестве стандартного исполнителя компиляции (compilation executor) для замены устаревших систем распределения сборки. UBA разработан для ускорения компиляции за счет использования легковесной виртуализированной файловой системы (VFS) для перехвата операций ввода-вывода (file I/O). Он распределяет задачи компилятора между несколькими локальными ядрами или отправляет их на узлы сборки Horde.
На стандартной 64-ядерной машине сборки UBA может сократить время чистой компиляции движка с ~90 минут до менее чем ~25 минут. Однако, поскольку UBA полагается на централизованного локального агента для перехвата ввода-вывода, ему требуется строгий контроль над тем, как запускаются процессы компилятора.
Механизм рекурсивных вызовов UBT
В процессе сборки UBT часто сталкивается с таргетами (targets), которые необходимо собрать перед компиляцией основных бинарников движка. Например, перед компиляцией исполняемого файла UnrealEditor UBT должен скомпилировать UnrealHeaderTool (UHT) для парсинга заголовочных файлов и генерации метаданных рефлексии.
Для этого основной процесс UBT запускает вложенный, вторичный процесс UBT для сборки зависимого таргета. Такое вложенное выполнение называется рекурсивным вызовом UBT. Когда активен рекурсивный вызов UBT, UBT устанавливает внутренний флаг (UnrealBuildTool.IsRecursive = true) и передает переменные окружения, помечающие процесс как вложенный.
Почему UBA Executor падает при рекурсивных вызовах
Сбой происходит из-за того, что архитектура UBA executor не предусматривает поддержку вложенных циклов компиляции. Давайте взглянем на типичный call stack, который выдает UBT при возникновении этой ошибки в Visual Studio:
Unhandled exception: Exception: UBA executor is not expected to be invoked from a recursive UBT call.
at UnrealBuildTool.UBAExecutor.Init(IEnumerable`1 targetDescriptors, ILogger logger) in UnrealEngine\Engine\Source\Programs\UnrealBuildTool\Executors\UnrealBuildAccelerator\UBAExecutor.cs:line 315
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at UnrealBuildTool.UBAExecutor.ExecuteActionsAsync(IEnumerable`1 inputActions, ILogger logger) in UnrealEngine\Engine\Source\Programs\UnrealBuildTool\Executors\UnrealBuildAccelerator\UBAExecutor.cs:line 617
at UnrealBuildTool.ActionGraph.InternalExecuteActions(ActionExecutor Executor, List`1 ActionsToExecute, ILogger Logger) in UnrealEngine\Engine\Source\Programs\UnrealBuildTool\Actions\ActionGraph.cs:line 435
Проверка безопасности в UBAExecutor.cs
Внутри метода UBAExecutor.Init (примерно на строке 315 в UBAExecutor.cs) в движке жестко прописана проверка безопасности рекурсии:
// Engine/Source/Programs/UnrealBuildTool/Executors/UnrealBuildAccelerator/UBAExecutor.cs
public void Init(IEnumerable<TargetDescriptor> TargetDescriptors, ILogger Logger)
{
if (UnrealBuildTool.IsRecursive)
{
throw new Exception("UBA executor is not expected to be invoked from a recursive UBT call.");
}
// Virtualized file system and network initialization follow...
}
Эта проверка безопасности существует по очень важной причине. Локальный агент UBA привязывается к определенным TCP-портам для взаимодействия с виртуализированными вспомогательными процессами компилятора.
Если бы рекурсивный вызов UBT попытался инициализировать второй экземпляр UBA executor, оба экземпляра попытались бы привязаться к одним и тем же сетевым сокетам и вступили бы в конфликт из-за хуков виртуализированной файловой системы. Это привело бы к ошибкам выделения сокетов, повреждению файловой системы или бесконечным взаимным блокировкам сборки (deadlocks). Исключение служит здесь защитным барьером.
Первопричина: сбой выбора Executor
Настоящий баг в сборках Unreal Engine 5.8.0 из исходного кода кроется не в этой проверке. Проблема заключается в том, что логика фабрики исполнителей (executor factory) некорректно обрабатывает рекурсивные вызовы.
Когда UBT определяет, какой именно executor запустить, он обращается к классу ExecutorFactory.cs. Фабрика проверяет, включен ли UBA и доступен ли он на хост-машине.
Однако она не проверяет, является ли текущий процесс UBT рекурсивным запуском. В результате, когда вторичный процесс UBT запускается для компиляции UnrealHeaderTool, фабрика пытается назначить UBAExecutor для вложенной сборки, что и вызывает падение в методе Init.
Ловушка конфигурации XML
Многие разработчики пытаются обойти эту проблему, изменяя глобальный файл BuildConfiguration.xml. Стандартный совет на старых форумах — отключить UBA с помощью следующего тега:
<?xml version="1.0" encoding="utf-8" ?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<BuildConfiguration>
<bAllowUBA>false</bAllowUBA>
</BuildConfiguration>
</Configuration>
Почему bAllowUBA игнорируется в UE 5.8
Если вы примените эту XML-конфигурацию к сборке Unreal Engine 5.8.0 из исходного кода, компилятор все равно попытается запустить UBA и выдаст исключение. Дело в том, что система конфигурации сборки движка была переработана.
Флаг bAllowUBA устарел и больше не используется в логике выбора executor. Теперь поведение UBA контролируется двумя отдельными свойствами: bAllowUBAExecutor и bAllowUBALocalExecutor.
Поскольку UBT не находит bAllowUBAExecutor в вашем XML, он по умолчанию принимает значение true. Это незаметно отменяет вашу попытку отключить UBA.
Правильная структура XML для настройки UBA
Чтобы успешно отключить UBA через конфигурационные файлы, вы должны использовать обновленные имена свойств XML. Ниже приведена правильная структура, которая заставит UBT вернуться к использованию стандартных локальных исполнителей:
<?xml version="1.0" encoding="utf-8" ?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<BuildConfiguration>
<bAllowUBAExecutor>false</bAllowUBAExecutor>
<bAllowUBALocalExecutor>false</bAllowUBALocalExecutor>
</BuildConfiguration>
</Configuration>
Эта конфигурация успешно обходит UBA. Однако полное отключение UBA лишает вас существенного прироста скорости компиляции, который он дает для основных задач сборки.
Пошаговое руководство по устранению ошибки
В зависимости от вашего рабочего процесса, вы можете решить эту проблему глобально, изменив XML-конфигурацию, либо напрямую пропатчив исходный код UBT, чтобы сохранить ускорение сборки для нерекурсивных компиляций.
Способ 1: Переопределение через BuildConfiguration.xml
Если вы не хотите модифицировать исходный код движка, можно отключить UBA глобально. Это самый быстрый способ возобновить компиляцию проекта, хотя время чистой сборки увеличится на ~40%–60% в зависимости от вашего железа.
- Найдите или создайте глобальный файл
BuildConfiguration.xml. В Windows он обычно находится по пути%AppData%\Roaming\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml. В Linux — по пути~/.config/Unreal Engine/UnrealBuildTool/BuildConfiguration.xml. - Откройте XML-файл в текстовом редакторе.
- Замените содержимое обновленной XML-схемой, показанной ниже.
- Сохраните файл и перезапустите сборку в Visual Studio.
<?xml version="1.0" encoding="utf-8" ?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<BuildConfiguration>
<bAllowUBAExecutor>false</bAllowUBAExecutor>
<bAllowUBALocalExecutor>false</bAllowUBALocalExecutor>
</BuildConfiguration>
</Configuration>
Способ 2: Патч исходного кода UBT (рекомендуется)
Поскольку вы собираете Unreal Engine 5.8 из исходного кода, рекомендуемым решением является патч исходного кода UBT на C#. Это позволит использовать UBA для основного таргета компиляции и одновременно принудительно переключаться на стандартный ParallelExecutor при рекурсивных вызовах.
- Перейдите в каталог с исходным кодом UBT:
Engine/Source/Programs/UnrealBuildTool/Executors/. - Откройте файл
ExecutorFactory.csв Visual Studio или текстовом редакторе. - Найдите метод
Create. Этот метод отвечает за оценку вашей конфигурации и возврат соответствующегоActionExecutor. - Измените условный оператор выбора UBA, добавив в него проверку на рекурсивные запуски UBT.
// Engine/Source/Programs/UnrealBuildTool/Executors/ExecutorFactory.cs
public static ActionExecutor Create(BuildConfiguration BuildConfiguration, List<TargetDescriptor> TargetDescriptors, ILogger Logger)
{
// Check if UBA is allowed, available, and NOT running recursively
- if (BuildConfiguration.bAllowUBAExecutor && UBAExecutor.IsAvailable())
+ if (BuildConfiguration.bAllowUBAExecutor && UBAExecutor.IsAvailable() && !UnrealBuildTool.IsRecursive)
{
return new UBAExecutor(BuildConfiguration, Logger);
}
// Fall back to IncrediBuild if configured
if (BuildConfiguration.bAllowXGE)
{
return new XGEExecutor(BuildConfiguration, Logger);
}
// Fall back to standard ParallelExecutor
return new ParallelExecutor(BuildConfiguration, Logger);
}
Этот патч предотвращает выбор UBA executor вложенным экземпляром UBT. Вместо этого рекурсивная сборка (например, сборка UnrealHeaderTool) будет безопасно выполняться с помощью локального ParallelExecutor, в то время как основная компиляция движка продолжит использовать все преимущества UBA.
Способ 3: Флаги командной строки
Если вы запускаете процесс сборки с помощью кастомных скриптов командной строки или в пайплайнах CI/CD, вы можете отключать UBA для конкретных вызовов. Это особенно полезно, если вы хотите оставить UBA включенным для локальных разработчиков, но отключить его на удаленных серверах сборки.
Для этого добавьте флаг -NoUBA к вашей команде сборки UBT:
# Example command to build the editor without UBA
Engine\Build\BatchFiles\Build.bat UnrealEditor Win64 Development -NoUBA
Alternatively, you can force UBT to use the standard parallel executor by specifying it explicitly with the -Executor flag:
Engine\Build\BatchFiles\Build.bat UnrealEditor Win64 Development -Executor=Parallel
Очистка и перегенерация среды сборки
После применения любого из описанных выше исправлений UBT может не скомпилироваться из-за кэшированных файлов сборки или устаревших промежуточных метаданных. Чтобы исправление вступило в силу без сбоев, необходимо очистить сгенерированные бинарные файлы сборочного инструмента и заново сгенерировать файлы проекта.
Для среды Windows
Выполните следующие команды в командной строке или консоли PowerShell, открытой в каталоге с исходным кодом Unreal Engine:
:: Delete the cached UBT assembly and build binaries
rd /s /q Engine\Intermediate\Build\UnrealBuildTool
rd /s /q Engine\Binaries\DotNET\UnrealBuildTool
:: Regenerate the project files
GenerateProjectFiles.bat
Для сред Linux и macOS
Выполните эти команды в терминале:
# Remove cached build tool data
rm -rf Engine/Intermediate/Build/UnrealBuildTool
rm -rf Engine/Binaries/DotNET/UnrealBuildTool
# Regenerate project files
./GenerateProjectFiles.sh
После очистки окружения откройте сгенерированный файл решения (UE5.sln) в Visual Studio и выполните Rebuild для вашего таргета. Теперь сборка должна успешно пройти этап рекурсивной компиляции без возникновения исключения executor.
Практические рекомендации по сборке Unreal Engine из исходного кода
Сборка кастомного форка движка из исходного кода сопряжена с различными трудностями компиляции, оптимизации и упаковки. Применение следующих лучших практик поможет вам поддерживать стабильный и высокооптимизированный пайплайн разработки.
1. Оптимизируйте параллелизм для предотвращения нехватки памяти
Современным компиляторам C++ требуется значительный объем оперативной памяти на каждый поток компиляции. При сборке крупных модулей движка UBT может запускать больше параллельных задач, чем способна вместить ваша системная память RAM, что приводит к чрезмерному использованию файла подкачки (page-file thrashing) или падению компилятора.
Вы можете ограничить максимальное количество ядер процессора, настроив параметры ParallelExecutor в вашем файле BuildConfiguration.xml:
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<ParallelExecutor>
<MaxProcessorCount>16</MaxProcessorCount>
<ProcessorCountMultiplier>1.0</ProcessorCountMultiplier>
</ParallelExecutor>
</Configuration>
Ограничивайте это число из расчета примерно 2 ГБ RAM на один поток. Например, для машины с 32 ГБ оперативной памяти значение MaxProcessorCount следует ограничить до 16.
2. Освойте Asset Stripping для Dedicated Server
При развертывании игры в облаке следует избегать компиляции ненужных клиентских ассетов (таких как текстуры интерфейса, аудио и меши) в бинарный файл вашего Dedicated Server. Это сокращает время запуска сервера и объем занимаемой им памяти, что критически важно для эффективного масштабирования серверных мощностей.
Чтобы узнать, как настроить скрипты сборки для исключения этих ассетов, следуйте нашему подробному руководству по Unreal Engine Dedicated Server Asset Stripping.
3. Проверяйте целостность Blueprint и пакетов на ранних этапах
Успешная сборка движка еще не гарантирует, что упаковка (packaging) вашего проекта пройдет без ошибок. Устаревшие Blueprint или ошибки сериализации часто вызывают падения на финальном этапе упаковки.
Чтобы подобные проблемы не блокировали ваш релизный пайплайн, ознакомьтесь с нашим руководством по устранению ошибки Unreal Package HasValidBlueprint Ensure Crash для настройки автоматических проверок валидации.
4. Правильно настройте кэширование VFS в UBA
Если вы решите оставить UBA включенным с помощью патча на C#, настройте кэш виртуальной файловой системы для локального хранения скомпилированных объектных файлов. Это позволит избежать повторной компиляции неизмененных исходных файлов при переключении между ветками.
Добавьте конфигурационный блок UnrealBuildAccelerator в ваш XML-файл, чтобы включить кэширование:
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<UnrealBuildAccelerator>
<WriteCache>true</WriteCache>
<Cache>127.0.0.1</Cache>
</UnrealBuildAccelerator>
</Configuration>
Убедитесь, что ваш брандмауэр не блокирует порты локального сетевого взаимодействия UBA, которые используются для управления циклом синхронизации кэша.
Развитие вашей архитектуры Backend
Устранение таких проблем сборки, как unreal engine uba executor ubt error, критически важно для поддержания нормального рабочего процесса разработки. Однако настройка локального окружения для компиляции — это лишь первый шаг на пути к созданию современной Multiplayer-игры.
Как только ваш кастомный движок скомпилирован и ваши Dedicated Server готовы, перед вами встанут сложные задачи по созданию инфраструктуры Backend. Написание собственной системы оркестрации серверов, алгоритмов Matchmaking и баз данных персистентного хранения с нуля требует недель разработки.
Настройка Load Balancing, шардинга баз данных и управления SSL-сертификатами может запросто отнять от 4 до 6 недель плотной работы. С платформой horizOn эти сервисы Backend предоставляются уже настроенными и готовыми к масштабированию.
Вместо написания кода инфраструктуры вы можете интегрировать данные игроков, лобби в реальном времени и масштабирование серверов с помощью нескольких простых вызовов API. Это позволит вам развертывать сборки Dedicated Server и управлять состоянием игроков без каких-либо затрат на обслуживание инфраструктуры. Ваша команда сможет сосредоточиться на геймплейных механиках и фичах движка, в то время как horizOn возьмет на себя облачную инфраструктуру.
Следующие шаги
Чтобы устранить эту ошибку сборки, выберите либо быстрое переопределение через XML, либо примените патч исходного кода на C# в ExecutorFactory.cs. Как только ваш пайплайн сборки заработает без сбоев, подумайте над оптимизацией рабочего процесса развертывания. Если вы хотите масштабировать вашу Multiplayer-игру без лишних затрат на хостинг кастомных серверов, попробуйте horizOn бесплатно или изучите нашу документацию по API, чтобы узнать больше.