FOnline Engine
Current master GitHub
Documentation Docs/en/how-to/build/index.md

Build Workflow

This document explains how to approach FOnline builds without hard-coding assumptions from one project into another.

Source paths inspected

  • ../BuildTools/README.md
  • ../BuildTools/Init.cmake
  • ../BuildTools/validate.sh
  • ../BuildTools/validate.cmd
  • ../BuildTools/buildtools.py
  • ../BuildTools/docs_cli.py
  • Docs/en/reference/buildtools/index.md
  • ../BuildTools/PackageInterface.json
  • ../BuildTools/docs_package.py
  • en/reference/packages/index.md
  • ../Examples/MinimalProject/
  • ../Examples/MinimalMultiplayer/
  • ../BuildTools/cmake/stages/Init.cmake
  • ../BuildTools/cmake/stages/ProjectOptions.cmake
  • ../BuildTools/cmake/stages/EngineSources.cmake
  • ../BuildTools/cmake/stages/Codegen.cmake
  • ../BuildTools/cmake/stages/ScriptsAndBaking.cmake
  • ../BuildTools/cmake/stages/Applications.cmake
  • ../BuildTools/cmake/stages/Packages.cmake
  • ../BuildTools/cmake/stages/Finalize.cmake
  • ../BuildTools/cmake/helpers/*.cmake
  • ../Source/Applications/TestingApp.cpp
  • ../Source/Tests/README.md

Use the embedding project as the build root

FOnline is normally built through a game repository that embeds the engine as Engine/. Configure and build from the game root unless a focused engine-only command explicitly says otherwise.

Reasons:

  • Target names are project-defined.
  • .fomain controls game-specific configuration.
  • Generated scripting APIs are project-dependent.
  • Package names, signing, resources, and deployment settings belong to the product.
  • Platform presets usually live in the embedding project’s CMakePresets.json.

Typical workflow

  1. Open the game repository root.
  2. Inspect available presets with CMake or the IDE integration used by the project.
  3. Configure the smallest preset that covers your change.
  4. Build the narrowest relevant target.
  5. Run the corresponding test, package, or launch target.
  6. Update documentation if the workflow or behavior changed.

Engine-owned first build

The repository includes one stable exception to project-specific target naming: Examples/MinimalProject. It proves a clean headless embedding path without Last Frontier, TLA, or another game checkout.

From the engine root, use the host-specific validation target:

cd Examples\MinimalProject
python validate.py
cd Examples/MinimalProject
python3 validate.py

Both routes configure and build the baker plus headless server, bake the minimal AngelScript project, run the server with networking disabled and an in-memory database, and require the lifecycle markers documented in First FOnline Headless Project. Pinned Windows and Linux CI lanes are verified.

The next Engine-owned route builds the desktop client, headless client, headless server, and baker, then tests metadata, content, login, map loading, localized text, remote calls, and replicated state:

cd Examples\MinimalMultiplayer
python validate.py
cd Examples/MinimalMultiplayer
python3 validate.py

The source and manual launch path are documented in Minimal Multiplayer and First Playable Client.

Prerequisites

Use the Support Matrix before turning a build profile into a release claim. The generated matrix distinguishes required compilation, executable smoke evidence, and source-only profiles; device, renderer, package, service, and store acceptance remain project-owned.

The exact list depends on host OS and target platform, but common tools include:

  • Git
  • CMake
  • Python 3
  • A C++20-capable compiler/toolchain
  • Platform SDKs for the targets you build
  • Visual Studio or Build Tools on Windows-oriented workflows
  • Emscripten and Node.js for Web builds
  • JDK and Android NDK for Android builds

Prefer the embedding project’s documented setup because it may pin specific SDK/tool versions.

Windows 7 compatibility lane

The win32-win7 and win64-win7 build-platform keys are native-Windows MSVC lanes pinned to toolset v143,version=14.44; they fail early on a non-Windows host. FO_BINARY_OUTPUT_POSTFIX is an independent build identity, not an implication of the -win7 platform name. When a project builds with a value such as Win7, its matching package declaration must use the same value on that one entry: BINARY Client Windows win32-win7 Raw+Zip+Wix POSTFIX Win7.

Before packaging or publishing that lane, inspect every linked EXE and DLL:

python BuildTools/check_windows7_imports.py --require-large-address-aware <client.exe> <client-runtime.dll>

The check parses PE imports and rejects the curated Windows 8+ exports, absent libraries, and unsupported API-set contracts described in Testing. This includes imports from a statically linked managed runtime. A passing static check is not a live Windows 7 SP1 startup test. The embedding project owns the concrete toolset installation, binary paths, package matrix, CI gate, and live-host acceptance.

Windows x86 address space

AddExecutableApplication in BuildTools/cmake/helpers/Build.cmake links every 32-bit Windows engine executable with /LARGEADDRESSAWARE, including client hosts, headless applications, servers and tools. The decision follows platform and pointer size, not a project target name or binary postfix; shared libraries do not set the process limit.

On 64-bit Windows, this permits an x86 user address space of up to 4 GB rather than 2 GB. On 32-bit Windows 7 the default remains 2 GB; the flag adds no physical RAM. check_windows7_imports.py --require-large-address-aware checks the finished EXE’s PE flag alongside import compatibility and does not impose that flag on DLLs. Neither gate proves representative map loading, sustained memory behavior or acceptance on an actual Windows 7 host.

Fetching through a mirror of your own

prepare-workspace downloads the toolset, the Android SDK/NDK, the MSVC SDK and the LLVM sources from whoever publishes them. Each of those is a machine you do not run, and a dropped connection costs the job that is waiting on it. An embedding project may put a host of its own in front of them; the engine only needs to be told where it is, so nothing about that host is compiled in and everything travels in the environment:

variable what it configures
FO_DOWNLOAD_MIRROR Base URL of a pull-through mirror. https://host/path is fetched as <mirror>/host/path instead.
FO_WORKSPACE_CACHE Base URL for prepared workspaces. Emscripten is keyed by SDK version, host OS, and architecture; the MSVC SDK tree is keyed by xwin version and contained architectures. Each complete tree is built once and downloaded whole afterwards.
FO_CI_TOKEN Bearer token for the two addresses above. It is sent only to their own scheme and host, never to an upstream one.
FO_CI_CA Extra trust anchors, added to the system store rather than replacing it, for a machine whose root store cannot be repaired.

Unset, every one of them leaves the download path exactly as it was.

Two behaviours are deliberate. A download is checked against the upstream Content-Length, because a dropped connection ends the read instead of raising and an archive cut in half unpacks into a failure far from its cause. And a workspace cache that is empty, unreachable or refusing is only a miss: it exists to make the build faster and independent of other people’s servers, not to become another way for it to fail.

emsdk and xwin fetch their own packages, so mirroring the engine’s direct downloads does not cover them. Their complete prepared results are therefore what the workspace cache holds. A corrupt or incomplete Emscripten cache object is discarded and rebuilt locally; cache creation and upload remain best-effort. Cached trees are extracted through the standard data-only tar filter after a path-boundary check. Extraction lands in a temporary sibling first, and only the named complete SDK directory is promoted, so an archive cannot overwrite another prepared workspace tree. The xwin cache follows the same restore rule.

Where build logic lives

Use the generated BuildTools CLI reference for the exact main commands, arguments, defaults, choices, and executable help output.

Use the generated package interface reference for DefinePackage grammar, accepted targets/platforms/architectures, pack-token compatibility, payload layouts, and output artifacts. Follow Packaging and Release for the build/bake/package order, platform procedures, artifact evidence, signing, acceptance, and recovery boundaries. Keep a game’s concrete package matrix in that embedding project’s documentation.

For a library, SDK, framework, or runtime payload owned by the game repository, follow Project-Local Dependencies. Create a project CMake target, append it to the narrowest consumed FO_*_LIBS list supported by the pinned revision, and validate both its compiled feature state and packaged runtime state.

  • BuildTools overview.
  • BuildTools Pipeline — staged CMake pipeline and change routing.
  • ../BuildTools/cmake/ — reusable CMake modules and staged generation/build/package logic.
  • ../BuildTools/Init.cmake — project-facing CMake entry point and strict stage dispatcher.
  • Embedding project root — product-level presets, configuration, and target selection.

Validation by change type

When BuildTools/buildtools.py::create_parser() changes, regenerate and check the CLI model/pages before validating the affected command in an embedding project.

When package declarations or payload behavior change, update BuildTools/PackageInterface.json, regenerate/check its model/pages, run validate_package_interface.cmake and test_packaging_matrix.py, then build RunPackagingChecks, RunTutorialPackageChecks, or the narrower affected product package target from the owning example/project. These example targets are opt-in and are not part of the required Engine validation registry. The Engine fixtures prove native raw/archive/config/updater mechanics; they do not replace a game’s signing, install, deployment, or rollback lane.

Keep build docs maintainable

Do not copy a full preset list into engine docs. Presets change per game and per branch. Instead, explain ownership and link to the concrete project document that owns exact commands.

Validation checklist

  1. Confirm the command or preset belongs to the embedding project before documenting exact names in engine docs.
  2. For BuildTools changes, reconfigure the smallest affected preset and run the generated target that exercises the changed stage.
  3. For runtime changes, run focused tests first and then the project RunUnitTests target when practical.
  4. For package/platform changes, validate the owning package/debug doc in the same change.
  5. Update BuildTools Pipeline, Testing, or platform docs when the build workflow itself changes.
  6. Run the matching starter smoke when a BuildTools, baking, scripting, application-startup, or embedding-boundary change can affect the canonical minimal project.
  7. Regenerate affected contract models and run the aggregate generated contract diff for project-facing API, CMake, CLI, or package changes.
Start typing to search.