Skip to content

Contributing to ATC and ATO

Development Setup Environment

While ATC can be installed and used by itself, ATO depends on ATC. When contributing to either, your development environment needs to include both to ensure a change in ATC doesn't break ATO.

ATO and ATC work on Windows, Mac, and Linux. Windows and Linux users may follow this guide. macOS users need to change a few things from this tutorial. See Setting up in other environments to see what other steps need to be made on other environments.

The folder structure will look like this:

ATC_TEST_ENV/
└── TemplateProject/
    ├── ATO/
    │   └── Packages/
    │       ├── ato/
    │       ├── unreal-lag/
    │       └── buildgraph/          (real folder)
    ├── BuildGraph/                 (symlink → ATO/Packages/buildgraph)
    ├── TemplateProject.uproject
    ├── package.json
    └── tsconfig.json

Create a root folder. We'll use ATC_TEST_ENV.

Within the root folder, create a project based on the Third Person C++ template. We'll put it under the folder TemplateProject.

Setup pnpm-workspace.yaml in TemplateProject/

# TemplateProject/pnpm-workspace.yaml
packages:
  - ATO/packages/*

onlyBuiltDependencies:
  - esbuild
  - nx

Setup ATO

# Within `ATC_TEST_ENV`
cd TemplateProject
git clone https://github.com/MaximDevoir/ATO.git
cd ATO
pnpm install --frozen-lockfile

Setup NPM within Template Project

# Within `ATC_TEST_ENV`
cd TemplateProject
npm init -y
npx tsc --init
pnpm add -D file:./ATO/packages/ato
pnpm add -D file:./ATO/packages/unreal-lag
pnpm add -D typescript tsx @types/node vitest
mklink /D BuildGraph .\ATO\Packages\buildgraph # Windows only
# ln -s ./ATO/Packages/buildgraph BuildGraph   # Linux/macOS
pnpm add -D c8 # needed for typescript code coverage

Within TemplateProject/tsconfig.json replace contents with

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "Bundler",
    "lib": [
      "esnext"
    ],
    "types": [
      "node"
    ],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "baseUrl": ".",
    "paths": {
      "@ATO/UnrealLag": [
        "./ATO/packages/unreal-lag/src/UnrealLag.ts"
      ],
      "@ATO/ATO": [
        "./ATO/packages/ato/src/index.ts"
      ]
    }
  },
  "include": [
    "BuildGraph/Scripts/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

Within TemplateProject/package.json change from "type": "commonjs" to "type": "module".

Within TemplateProject/package.json add these scripts

{
  "scripts": {
    // For All Environments
    "test": "npm run test:ci",
    "ue:uat": "tsx BuildGraph\\RunUAT.ts",
    "test:directStandalone": "tsx buildgraph/Scripts/RunStandaloneTest.ts --SimpleAutoBuild",
    "test:directDedicated": "tsx buildgraph/Scripts/RunDedicated.tests.ts --SimpleAutoBuild",
    "test:directListen": "tsx buildgraph/Scripts/RunListenServerTest.ts --SimpleAutoBuild",
    "test:directPIE": "tsx buildgraph/Scripts/RunPIETest.ts --SimpleAutoBuild",
    //
    // For Windows
    "test:ci": "npm run ue:uat -- BuildGraph -Script=%CD%\\BuildGraph\\ATCTests.generated.xml -Target=ATCTests",
    "test:standalone": "npm run ue:uat -- BuildGraph -Script=%CD%\\BuildGraph\\ATCTests.generated.xml -Target=Standalone",
    "test:standaloneUI": "node --import tsx BuildGraph\\Scripts\\RunStandaloneUI.ts",
    "test:standaloneAssertions": "npm run ue:uat -- BuildGraph -Script=%CD%\\BuildGraph\\ATCTests.generated.xml -Target=StandaloneAssertions",
    "test:standaloneWithExceptions": "npm run ue:uat -- BuildGraph -Script=%CD%\\BuildGraph\\ATCTests.generated.xml -Target=StandaloneWithExceptions",
    "test:dedicated": "npm run ue:uat -- BuildGraph -Script=%CD%\\BuildGraph\\ATCTests.generated.xml -Target=Dedicated",
    "test:listen": "npm run ue:uat -- BuildGraph -Script=%CD%\\BuildGraph\\ATCTests.generated.xml -Target=Listen",
    "test:PIE": "npm run ue:uat -- BuildGraph -Script=%CD%\\BuildGraph\\ATCTests.generated.xml -Target=PIE"

    // For macOS/Linux
    "test:ci": "npm run ue:uat -- BuildGraph -Script=\"$PWD/BuildGraph/ATCTests.generated.xml\" -Target=ATCTests",
    "test:standalone": "npm run ue:uat -- BuildGraph -Script=\"$PWD/BuildGraph/ATCTests.generated.xml\" -Target=Standalone",
    "test:standaloneUI": "node --import tsx BuildGraph/Scripts/RunStandaloneUI.ts",
    "test:standaloneAssertions": "npm run ue:uat -- BuildGraph -Script=\"$PWD/BuildGraph/ATCTests.generated.xml\" -Target=StandaloneAssertions",
    "test:standaloneWithExceptions": "npm run ue:uat -- BuildGraph -Script=\"$PWD/BuildGraph/ATCTests.generated.xml\" -Target=StandaloneWithExceptions",
    "test:dedicated": "npm run ue:uat -- BuildGraph -Script=\"$PWD/BuildGraph/ATCTests.generated.xml\" -Target=Dedicated",
    "test:listen": "npm run ue:uat -- BuildGraph -Script=\"$PWD/BuildGraph/ATCTests.generated.xml\" -Target=Listen",
    "test:PIE": "npm run ue:uat -- BuildGraph -Script=\"$PWD/BuildGraph/ATCTests.generated.xml\" -Target=PIE"
  }
}

Setup ATC

# Within `ATC_TEST_ENV`
cd TemplateProject
mkdir Plugins
cd Plugins
git clone https://github.com/MaximDevoir/ATC.git
cd ATC
npm install

Add ATC to the template project

// TemplateProject.json
{
  "Plugins": [
    {
      "Name": "ATC",
      "Enabled": true
    }
  ]
}

Within TemplateProject/Source you should have two *.Target.cs files. We need a third for file for the server. You can copy TemplateProject.Target.cs, rename it to TemplateProjectServer.Target.cs and change class and method names from TemplateProjectTarget to TemplateProjectServerTarget and set line set Type = TargetType.Server.

- Source
-- TemplateProject.Target.cs
-- TemplateProjectEditor.Target.cs
-- TemplateProjectServer.Target.cs

We need to modify TemplateProject.Build.cs to optionally include exception handling

// Add header
using System.Linq;

// Within template project method add
bool bUseATCExceptions = Target.GlobalDefinitions.Contains("ATC_WITH_CPP_EXCEPTIONS=1");
bEnableExceptions = bUseATCExceptions;
if (bUseATCExceptions) {
  PublicDefinitions.Add("ATC_WITH_CPP_EXCEPTIONS=1");
} else {
  PublicDefinitions.Add("ATC_WITH_CPP_EXCEPTIONS=0");
}

Setting up the BuildGraph file.

cd TemplateProject
copy .\ATO\BuildGraph\.env.sample .env # Windows
cp ./ATO/BuildGraph/.env.sample .env   # macOS / Linux

Open the .env file. The only setting that is required is ENGINE_DIR and the rest will be auto-discovered.

# .env
ENGINE_DIR=D:/uei/UE5.7.3/Engine

After setting up .env run

cd TemplateProject
npx tsx BuildGraph/GenerateBuildGraphFromTemplate.ts

Finally, since we are using the Third Person Template, we need to set some settings to engine-level content in TemplateProject/Config/DefaultEngine.ini

[/Script/EngineSettings.GameMapsSettings]
GlobalDefaultGameMode=/Script/Engine.GameModeBase
GameDefaultMap=/Engine/Maps/Templates/Template_Default.Template_Default
EditorStartupMap=/Engine/Maps/Templates/Template_Default.Template_Default
ServerDefaultMap=/Engine/Maps/Templates/Template_Default.Template_Default
+MapsToCook = (FilePath="/Engine/Maps/Templates/Template_Default")
+MapsToCook = (FilePath="/Engine/Maps/Templates/OpenWorld")
bUseSplitscreen=False

Your project should now be setup and you should run npm run test:ci. This will orchestrate all tests for each coordinator (Standalone, Listen Server, Dedicated Server, and PIE).

Note: npm run test:ci will take a while the first time.

Setting up in other environments

This tutorial was completed on Windows, but all the tools, scripts, and auto-discovery were designed to be compatible with Linux and macOS but haven't been tested.

macOS

  • BuildGraph's Cook node doesn't support macOS ( see available target platforms). You will have to instrument your own BuildGraph workflow. However, I am seeing conflicting information on what Cook supports on macOS. As such, testing may only be available to PIE coordinator or within the editor. Please let me know if you have any information on this topic. I will update the tutorial accordingly.
  • Dedicated server support with platform MacServer may not be supported.

Testing and Development Requirements

  • Avoid compiler-specific or platform-specific code.
  • All features must be compatible with macOS, Linux, and Windows.
  • ATC must support Clang and MSVC. Avoid compiler-specific macros and platform-specific code.
  • Tests within ATC and ATO must be platform agnostic.
  • Tests within ATC should be template-agnostic. We setup using the Third Person Template. However, any resources used should be engine-level only. Example: Use MAP_WORLD("/Engine/Maps/Templates/OpenWorld"); instead of MAP_WORLD("/Game/Maps/ThirdPerson");.
  • ATO and ATC source files need to be platform-agnostic.

  • Only use default engine-level content.

Development Requirements and Tips

  • Avoid opening symlinked files (e.g. modifying anything in TemplateProject/BuildGraph) and instead open the actual file. You will get accurate type inference, static analysis, and better code completion.
  • Dirty PIE packages preventing PIE testing? When reopening an engine-level map, you may see it as Untitled. Reopen the engine-level map to see the correct name and prevent test execution due to dirty PIE packages.
  • When updating ATCTests.template.xml run cd TemplateProject && npx tsx BuildGraph/GenerateBuildGraphFromTemplate.ts to update your generated file.

ATO

  • When updating ATO files, run pnpm update -r within TemplateProject so that the changes roll into your development environment. Otherwise, stale references will be used.
  • Before submitting a PR, run pnpm change to build a changeset.
  • Run pnpm verify to ensure tests, formatting, and linting pass.
  • Use pnpm format to automatically format the files.

Generating Coverage Reports

Add required packages to TemplateProject

cd TemplateProject
pnpm add -D c8 lcov-result-merger nyc

Inside TemplateProject add .c8rc.json

{
  "all": true,
  "include": [
    "ATO/packages/**/src/**/*.ts"
  ],
  "exclude": [
    "**/node_modules/**",
    "**/dist/**",
    "**/*.test.ts",
    "BuildGraph/**"
  ],
  "extension": [
    ".ts"
  ],
  "reporter": [
    "text",
    "json",
    "lcov"
  ]
}

Add scripts to TemplateProject/package.json

"coverage:ts": "c8 --all --reporter=lcov --reporter=text --reporter=json tsx buildgraph/Scripts/RunAllWithTSCodeCov.ts",
"coverage:oneReport": "node --import tsx buildgraph/MergeAllCoverage.ts",

Generate the coverage report

cd TemplateProject
cd ATO
pnpm coverage

# Back to root
cd TemplateProject
npm run coverage:ts

# Normalize the paths in each lcov report
cd TemplateProject
npm run coverage:oneReport

Uploading the Report for ATO

Inside a PowerShell environment

cd TemplateProject
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest -Uri https://keybase.io/codecovsecurity/pgp_keys.asc -OutFile codecov.asc
gpg.exe --import codecov.asc

Invoke-WebRequest -Uri https://cli.codecov.io/latest/windows/codecov.exe -Outfile codecov.exe
Invoke-WebRequest -Uri https://cli.codecov.io/latest/windows/codecov.exe.SHA256SUM -Outfile codecov.exe.SHA256SUM
Invoke-WebRequest -Uri https://cli.codecov.io/latest/windows/codecov.exe.SHA256SUM.sig -Outfile codecov.exe.SHA256SUM.sig

gpg.exe --verify codecov.exe.SHA256SUM.sig codecov.exe.SHA256SUM
if (
    (Compare-Object
        -ReferenceObject $(($(certUtil -hashfile codecov.exe SHA256)[1], "codecov.exe") -join "  ")
        -DifferenceObject $(Get-Content codecov.exe.SHA256SUM)
    ).Length -eq 0
) { "SHASUM verified" } else { exit 1 }

Back in cmd.exe, add some environment variables

cd TemplateProject
set CODECOV_TOKEN=xxx-xxx-xxx-xxx-xxx
cd ATO
for /f %i in ('git rev-parse HEAD') do set GIT_SHA=%i
for /f %i in ('git branch --show-current') do set GIT_BRANCH=%i

cd TemplateProject
codecov.exe upload-process ^
  --file "coverage/merged/lcov.info" ^
  --network-root-folder ATO ^
  --network-filter packages ^
  --disable-search ^
  --exclude "Intermediate" ^
  --sha %GIT_SHA% ^
  --branch %GIT_BRANCH% ^
  --token %CODECOV_TOKEN% ^
  --git-service github ^
  --slug MaximDevoir/ATO ^
  --fail-on-error

Uploading Coverage Report for ATC

Add a --codecov argument to every action in .generated.xml. The produced coverage reports will automatically be merged.

Add a .covlcov file under TemplateProject, this will normalize the paths for the coverage report.

includeByBaseDir: true
baseDir: Source # or Plugins/YourPlugin/Source

Then, run every script that has the new --codecov argument.

cd TemplateProject
npm run test:standaloneWithExceptions
npm run test:standaloneAssertions
npm run test:ci

Then you need can run the unit tests to further build the coverage report.

Once you are ready to upload, move the merged atc file from coverage/atc/merged.info to inside the TemplateProject/Plugins/ATC/Source folder.

cd TemplateProject
codecov.exe upload-process ^
  --file "merged.info" ^
  --disable-search ^
  --sha %GIT_SHA% ^
  --branch %GIT_BRANCH% ^
  --token %CODECOV_TOKEN% ^
  --git-service github ^
  --slug MaximDevoir/ATC ^
  --fail-on-error