Skip to content

Building our Modding Framework#

Building our modding framework is designed to be mostly plug-and-play. In most cases, the only thing you need to be aware of is Augment's manifest generation pipeline.

How to Use It#

Important

Manifest generation is slow. Only run these targets when building for the first time, after making significant engine changes, or when preparing a release build.

Add one of the manifest generation targets to your build command:

--target gen_manifest_phase1

Which only runs the first phase.

or

--target gen_manifest_phase2

Which only runs the second phase. See full manifest generation if you're building fresh.

Phase 1: AST Manifest Generation#

gen_manifest_phase1 runs Augment's libclang-based AST walker to generate the bulk of the symbol metadata used by the modding system.

Characteristics:

  • Takes roughly 30 minutes on modern high-end hardware.
  • Only needs to be rerun when source-level symbols have changed significantly.
  • Intended for first-time setup, major refactors, and release builds.

Because of the runtime cost, developers should avoid running this target as part of their normal iteration loop.

Phase 2: Debug Symbol Generation#

gen_manifest_phase2 performs debug symbol analysis and generates the final symbol mappings used at runtime.

Characteristics:

  • Takes roughly 1 minute.
  • Must be rerun whenever the game binary changes and you intend to use mods against that build.
  • Much cheaper than Phase 1 and safe to run regularly.

In practice, most developers will rarely need to think about this step. However, if you're actively developing both the game and mods at the same time, forgetting to rerun Phase 2 can result in stale symbol mappings and difficult-to-diagnose issues.

Full Manifest Generation#

For convenience, the combined target remains available:

--target gen_manifest

This executes both phases in sequence:

  1. gen_manifest_phase1
  2. gen_manifest_phase2

Use this when performing a clean setup, generating manifests for distribution, or whenever you're unsure whether existing manifest data is still valid.

Performance Notes#

Manifest generation can make use of multiple CPU cores.

Set:

AUGMENT_JOBS=X

Where X is less than or equal to your CPU's core count.

Higher values can significantly reduce generation time, but will consume additional system resources while running.

Skipping Manifest Generation#

The game does not require manifest generation to build or run.

If you are not working on mods, simply omit the manifest generation targets from your build command. The game will build normally, and build times will be substantially shorter.

Manifest generation is only required when producing or updating the metadata consumed by the modding framework.

COMING SOON:#

Improving our modding system#

We will always be accepting PRs helping to improve our modding system with updated field names. Updated field names don't even have to change the code directly, through our annotation driven codegen system. This reads comments you add above functions and uses it to drive manifest format output.

Accepted commands:
(Note this is used to refer to the function you're overriding.)

Lifetime#

  • /// @singleton: instance <- Tells the generator not to attempt to hook a constructor and instead just redirects to the instance directly.
  • /// @constructor <- This tells the generator to hook this during constructor registry generation and shuts up the warning at build-time.
  • /// @destructor <- This tells the generator to hook this during destructor registry generation and shuts up the warning at build-time. These two are used to redirect constructor/destructor registry. This registry is used to automatically infer instances at runtime for cold mixin calls. If you run: Mixin.call("foo_bar",{ "i32" }, { 12 }) where foo is a class name, we can automatically resolve the instance for the class.

API#

  • /// @rename: new old deprecated(version) <- This tells the generator that new should also accept old as an alias until version + 2. This helps us retain compatibility for old mods as the codebase evolves into the forseeable future. Names will change as more fields are documented. This is the best line of defense against hard cut-offs.

Safety#

  • /// @unsafe <- Tells the generator this function is unhookable.

  • /// @pre_assert: (expression) <- Where expression is any valid expression. e.g: arg1FieldName != BAD_INPUT.

  • /// @post_assert: (expression) <- Where expression is any valid expression.
    e.g: returnValue != BAD_OUTPUT
  • /// @pre_nonnullable: arg0 <- Expands into @pre_assert: (returnValue != 0)
  • /// @post_nonnullable: arg0 <- Expands into @post_assert: (returnValue != 0)