For adopters who just want to use the Factory, no build is needed — grab a released HumankindAssetFramework.dll. Build only if
you’re changing the plugin.
HumankindAssetFramework.dll)Needs the .NET SDK. The project file is HumankindAssetFramework.csproj. Put a References\ folder next to the .csproj containing:
BepInEx.dll + 0Harmony.dll + MonoMod.RuntimeDetour.dll + MonoMod.Utils.dll + Mono.Cecil.dll — from
<Humankind>\BepInEx\core\ (the MonoMod/Cecil trio are Harmony’s own runtime deps — the unit suite EXECUTES
patches for the shared-seam census tests)UnityEngine*.dll + Newtonsoft.Json.dll — from <Humankind>\Humankind_Data\Managed\
(Newtonsoft is provided by the game at runtime — used for robust registry parsing, since UnityEngine.JsonUtility
silently returns empty in the game’s Mono runtime; Private=false, so it’s not copied into the built plugin.
No Amplitude DLL is needed — the plugin’s only compile-time game surface is string-based reflection; the vestigial
Amplitude.Mercury.Animation.dll reference was dropped 2026-08-17.)No game installed? pwsh tools/fetch-refs.ps1 collects every reference DLL from public sources
(nuget.org, the BepInEx release, BepInEx’s unstripped-Unity mirror) — this is what CI uses; it never overwrites
DLLs you already copied from the game. GitHub Actions builds + runs the full test suite on every push
(.github/workflows/ci.yml), along with every source-only guard — docs links, binding-catalog surface, hot path,
parse shape, editor source, and registry schema parity. Both halves now live in this repo, so parity needs no sibling
checkout. See
Testing.md for which guards run in which lane, and why the hook is not enough on its own.
Then:
dotnet build -c Release
and copy both bin\Release\HumankindAssetFramework.dll and bin\Release\Haf.Schema.dll → <Humankind>\BepInEx\plugins\
— or just run bash tools/deploy-plugin.sh, which copies both. Haf.Schema.dll (netstandard2.0) is the shared model
schema that ModelDef (editor) and ModelEntry (plugin) both inherit — the build produces it automatically via a
ProjectReference, but the plugin won’t load without it (BepInEx reports the missing dependency), so the two ship
together. The editor package consumes the same tracked DLL from editor/Plugins/Haf.Schema.dll.
Refresh the tracked editor DLL when
Haf.Schemachanges. The editor compiles against the DLL undereditor/, not the source project, so a schema change is invisible on the editor side until that in-repo artifact is refreshed — and the failure looks like a compile error naming a type that plainly does exist ('PackValidator' does not contain a definition for 'ValidatePack'), which reads as your mistake rather than a stale binary. Hit exactly this on 2026-08-23 adding the wrapper rules. Iftools/editor_compile_check.shreports a member that you can see inHaf.Schema/, copy the DLL before believing it:cp Haf.Schema/bin/Release/netstandard2.0/Haf.Schema.dll editor/Plugins/Haf.Schema.dll
Blender is needed for .blend import, animated-model import, Strip parts, and Reduce-to-tris decimation —
auto-detected under Program Files, or point the Factory Settings override / EditorPrefs 'ENC.blenderPath' at
blender.exe. Static GLB/OBJ/FBX bakes with neither Strip nor Reduce need no Blender: the GLB path uses the
self-contained packaged editor/Tools~/glbconv/glbconv.exe (no .NET install required, and its Weld & simplify option decimates without
Blender). A dotnet glbconv.dll fallback exists for local dev.
The heavy geometry passes shell out to headless Blender (editor/Tools~/prep_model.py, rig_anim.py, blend_export.py)
rather than being written in C#. The scripts are Python, but Python is only the remote control — the actual
decimation/import/export executes inside Blender’s C/C++ core:
glbconv’s Weld & simplify vertex clustering — survives as the
Blender-free fallback, but clustering averages UVs across seams (scrambles textured skins) and eats thin features,
which is exactly why the COLLAPSE path was added..blend conversion and the animated pipeline (armature slimming,
clip baking, FBX export) — Strip/Reduce ride an install the modder already has for those.glbconv would eliminate both the Blender
dependency for static models and the round-trip cost. Blender would remain only for what genuinely needs a DCC:
.blend files and animation. Tracked in Framework-Review.md.dotnet test Tests/HumankindAssetFramework.Tests.csproj -c Release
An xUnit suite (net471) over the pure registry/parse/era layer — the half that touches no live-game reflection,
and where the bugs have historically been. Needs the same gitignored References\ DLLs as the plugin build (mirrored
into the test bin). Full detail — what’s covered, what’s deliberately out of scope and why, how to add a test — is in
Testing.md.
ENCReload/Tools/package-release.sh builds the player download: the Humankind mod and the HAF plugin that
renders its custom units, in one reproducible artifact.
bash Tools/package-release.sh # -> Distribution/release/ENCReload-<ver>.zip
bash Tools/package-release.sh --no-zip # stage only (fast, for inspection)
bash Tools/package-release.sh --haf <dir> # explicit plugin checkout
Two numbered folders, because the halves install to different roots — the mod is a Humankind Community module, the plugin is a BepInEx DLL, and no single “extract here” covers both:
| In the zip | Goes to |
|---|---|
1_Humankind_game_folder/BepInEx/ |
the Humankind install (the folder with Humankind.exe) |
2_Community_mods_folder/ENCReload.<guid>.<ver>/ |
the Community folder the player’s other mods are in |
READ_ME_FIRST.txt is generated from a tracked template (Tools/release/) with the version and module GUID
substituted, so the shipped instructions cannot drift from what shipped. The plugin is built by the script,
never collected from wherever a DLL happened to be lying — the hand-made v0.1.0 zip ended up 178 commits behind
master exactly that way.
The stale-bundle guard is why this is a script and not a checklist. pack.json’s skel/atlas/clip GUIDs
point into the asset bundle, so a re-bake after the last mod build produces a pair that looks fine and is
broken — “waiting for leaves to load…” forever, or a scrambled texture from a mismatched mesh/atlas pair. The
script refuses to package when any baked asset (or pack.json) is newer than the bundle: the same rule the
District Factory’s health check already applies at authoring time, moved to where it would otherwise reach a
player. It excludes the bake-test fixtures by the same three prefixes the delete guard uses
(__feat_ / __smoketest__ / __convgate__) — a leftover fixture is newer than everything and would otherwise
fail every package.
The Model Factory and other authoring windows live in editor/ and install through Unity Package
Manager. They compile and run in the host Unity project; ENCReload is the reference consumer. Packaged Blender and
converter helpers live under editor/Tools~, and HafPackageContext supplies safe guest-project defaults and derives
the guest pack identity. The deleted baker/ editor snapshot must not return: it was a drifting second source.
baker/ still holdsTwo live things that exist only here, and are built from here:
baker/glbconv/ — the single source of truth for the GLB→OBJ converter (2026-08-17). Build with
dotnet publish -c Release, deploy the exe to editor/Tools~/glbconv/, and A/B-diff the OBJ output
before deploying (procedure in baker/glbconv/BUILD.md).baker/reactor_silhouette.py — the headless-Blender district silhouette helper
(District-Dedicated-Visual.md).