Skip to content

cronofear-dev/utasks

Repository files navigation

.NET Console Application for composing tasks for Unreal Engine (It can be used in other applications as well).

By default, the program lets you build your project in different configurations, build the engine from source, open levels without the editor, package your project or your plugins. This is similar to Unreal Binary Builder, but this program is a CLI so it could be easily hooked to a CI/CD environment, among other things. The default settings are somewhat opinionated (It's setup for Win64). The tasks can easily be modified, removed or extended by just modifying the .json file, and new C# scripts (for more complex behavior) can be created by implementing the program's interfaces.

How to use (Minimal Setup)

  1. Either build the program using the recommended configuration or download the release files.
  2. Copy utasks.exe and utasks.jsonc to the path of your Project (where .uproject lives)
  3. Alternatively copy those files to the path of an Unreal Engine source folder (if you want to build the engine from source).
  4. You can then open utasks.jsonc and make some changes to the settings according to your environment.
  5. Open utaks.exe and use the program.

Features

The program already comes with variables and tasks defined. But you can easily extend/modify it.

See:

Define your own variables in json

// Create a simple path variable pointing to `ParentFolder\\OLD\\UESource`
"TestParentPath" : "${CURRENT_PATH}\\..\\OLD\\UESource",
"TestAbsPath" : "C:\\Program Files\\Epic Games\\UE_5.3\\Engine",
"TestJustAString" : "Just a string",

// Create a variable using the files found in `${CURRENT_PATH}`
// Use glob match pattern to find all `*.uproject` files in that folder
// In `${PathToSearch -> *.uproject}`, the use of the `$` symbol will obtain the first result (Since there should only be a single `.uproject` file in an Unreal Engine project this only gets a single result)
// Upon which we can call simple .NET string manipulation methods such as `->Replace('.uproject','')`
// This essentially turns `MyProject.uproject` in your `Project\` folder into `MyProject` which is the name of the uproject file without extension.
"UProjectFileName" : "${${CURRENT_PATH} -> *.uproject}->Replace('.uproject','')",
// Upon which the value of `UProjectFileName` can be used as a parameter for other variables
"UProjectFilePath" : "${CURRENT_PATH}\\${UProjectFileName}.uproject",

// Similar to how `UProjectFileName` is defined, but
// `${CURRENT_PATH}\\Plugins\\` is appended to `%{${CURRENT_PATH}\\Plugins -> **\\*.uplugin -> !**\\CL* -> !**\\Lyra* -> !**\\OtherPluginName*}`
// `%{PathToSearch -> **\\*.uplugin -> !**\\CL* -> !**\\Lyra* -> !**\\OtherPluginName*}`  applies inclusion and exclusion patterns
// The glob pattern will search all `.uplugin` files recursively and exclude files that start with `CL`, `Lyra` and also exclude `OtherPluginName`
// Since `%{PathToSearch` uses the `%` symbol, it means that it will return all the results.
// So essentially if I were to have 10 plugins that would match the specified patterns, all those values would be stored in `PluginNames` in the form of:
// ["ProjectPath\\Plugins\\MyPlugin\\MyPlugin.uplugin", "ProjectPath\\Plugins\\MyOtherPlugin\\MyOtherPlugin.uplugin", "etc"]
"PluginNames" : "${CURRENT_PATH}\\Plugins\\%{${CURRENT_PATH}\\Plugins -> **\\*.uplugin -> !**\\CL* -> !**\\Lyra* -> !**\\OtherPluginName*}",

// A list of strings can also be explicitly defined
"TargetConfigurations" : ["DebugGame", "Development", "Shipping"],

// Similar to `PluginNames` but merges 2 definitions into 1
"LevelReferences" : ["${CURRENT_PATH}\\Content\\Maps\\%{${CURRENT_PATH}\\Content\\Maps -> **\\*.umap}", "${CURRENT_PATH}\\Content\\OtherMapsFolder\\{${CURRENT_PATH}\\Content\\OtherMapsFolder -> **\\*.umap}"],

Use the variables to compose your own tasks

// The Id is optional and can be used for composing `AutoTasks` of for calling this task from the CLI
"Id" : "BuildProject",
"Title" : "Build the Project (Multiple Configurations)",
"Subtasks" :
[
    // Creates a subtask for building the project in (Win64 | DebugGame Editor) using `EditorTargetName` and `UProjectFilePath` single variables (`$` symbol)
    {
        "Msg" : "Build the Project (Win64 | DebugGame Editor)",
        // The path of the program to run, can be `cmd` or other system programs as well
        "Program" : "${Build_bat}",
        "Args" : "${EditorTargetName} Win64 DebugGame ${UProjectFilePath} -waitmutex"
    },
    // Similarly creates a subtask for building the project in (Win64 | Development Editor)
    {
        "Msg" : "Build the Project (Win64 | Development Editor)",
        "Program" : "${Build_bat}",
        "Args" : "${EditorTargetName} Win64 Development ${UProjectFilePath} -waitmutex"
    },
    // Creates 3 subtasks for each variation of `TargetConfigurations`
    // Note: Only the use of 1 variable list (`%` symbol) is supported atm 
    {
        "Msg" : "Build the Project (Win64 | %{TargetConfigurations} Game)",
        "Program" : "${Build_bat}",
        "Args" : "${GameTargetName} Win64 %{TargetConfigurations} ${UProjectFilePath} -waitmutex"
    }
],
// `FilterByInput` is a C# implementation of `ISubtasksFilters` that allows to choose 1 or more subtasks before running them
"SubtasksFilters" : [ "FilterByInput" ],
// `RunSubtasks` is a C# implementation of `IRunSubtasks` that runs the subtasks by default, in the order they were selected
"SubtasksAction" : "RunSubtasks"
{
    "Id" : "OpenLevel",
    "Title" : "Open Level (No Editor)",
    "Subtasks" :
    [
        // Creates n subtasks for each variation of `LevelReferences`
        {
            "Msg" : "Open Level (%{LevelReferences})",
            "Program" : "${UnrealEditor_exe}",
            "Args" : "${UProjectFilePath} %{LevelReferences} -game -log -nosteam -ResX=1920 -ResY=1080 -WinX=192 -WinY=108 -windowed"
        }
    ],
    // `FilterByInputSingleOption` is a C# implementation that allows to choose only 1 subtasks before running it
    "SubtasksFilters" : [ "FilterByInputSingleOption" ],
    "SubtasksAction" : "RunSubtasks"
},
// For this particular task, the Subtasks are created by calling the C# script `GetPluginSubtasks`.
// `GetPluginSubtasks` is an implementation of `IGetSubtasks` that returns a `List<USubtask>` In this case a list of subtasks in the format:
// "Msg" : "${PluginName} (ParentFolderOfPlugin)",
// "Program" : "${RunUAT_bat}",
// "Args" : "BuildPlugin -Plugin=\"%{PluginName}\" -Package=\"${PluginsOutputPath}\\ParentFolderOfPlugin\" ${PluginPackageArgs}"
{
    "Id" : "PackagePlugins",
    "Title" : "Package Plugins (${PluginPackageArgs})",
    "Subtasks" : "GetPluginSubtasks",
    "SubtasksFilters" : ["FilterByInput"],
    // `RunSubtasksForPlugins` is an implementation of `ISubtasksAction` for plugins that let us confirm the options we selected and shows the list of plugins to be packaged after all the filters have been applied
    // It's essentially an `Are you sure you want to continue?` special action
    "SubtasksAction" : "RunSubtasksForPlugins",
    // `PackagePlugins` is an implementation of `TaskPostAction` that performs an operation on all successSubTasks and failedSubTasks
    // For Plugins that failed to be packaged, removes the folder created for the packaging in `${PluginsOutputPath}\\ParentFolderOfPlugin`
    // For Plugins that were packaged correctly, show more options (Zip Source Files, Zip Compiled Files, Deleting `${PluginsOutputPath}\\ParentFolderOfPlugin`)
    "TaskPostAction" : "PackagePlugins"
},

Use the tasks to compose autotasks

{
    "Id" : "TestLevel1",
    "Title" : "Test Level 1",
    "StepsDescriptions" : ["Generate and Build the Project (Win64 | Development Editor)", "Open Level 1"],
    // Start the task `GenerateAndBuildProject` and buffer the input `0`
    // Then start the task `OpenLevel` and buffer the input `1`
    // It's hard to see what it does by just watching this text, but by the way the program works this will:
    // Build the project in `Development Editor` and then Open the first level in `LevelReferences`
    // This may not be so useful in some scenearios, as the order of `LevelReferences` may change when creating/removing levels in the project
    // This could be `solved` by manually adding some subtasks in the `OpenLevel` task, ensuring the important levels you want to test are always the first choices, for example
    "Steps" : ["GenerateAndBuildProject 0", "OpenLevel 1"]
},

By composing tasks or autotasks, it's possible to call these tasks in with program arguments in the CLI as well (By using the Id). This can be useful for using the program with a CI/CD application:

  • .\utasks.exe a=DefaultBuildEditor "," OpenLevel 1 Will call the AutoTask named DefaultBuildEditor, then call the Task named OpenLevel and buffer the input 1
  • .\utasks.exe a=DefaultBuildEditor "," PackagePlugins 1 "<enter>" "1,2" Similar to the previous example, "<enter>" is a special input for empty and "1,2" is an input that selects the choice 1 and 2 (Ranges are supported as well i.e. "1,3-5")

Limitations and Known issues

The program has many hard edges as it's mostly developed solely for my personal use. It should work well as long as the variables and tasks are defined in a similar manner to the default ones. That being said, these are the limitations and knows issues that you may face:

  • Multiple uses of the % symbol are not supported both for creating and using the variables
  • A variable with multiple values (%) can only be used to define multiple Subtasks atm. It may work if used in other parameters.

About

A .NET CLI task composer for anything, but mostly for Unreal Engine

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages