-
Notifications
You must be signed in to change notification settings - Fork 0
Ecosystem Survey
Auto-generated from the repo docs by
tools/sync_wiki.sh— edit the source Markdown in the repo, not this wiki page.
📁 ARCHIVED NOTE — frozen 2026-07-12, not maintained. A point-in-time survey of other Humankind plugins against 2021–2024 game versions. Re-verify every class/method name against current game DLLs before relying on one.
Compiled 2026-07-12 from a GitHub-wide search (repo/code/README search + walking each known modder's account), then reading each repo's README and key sources. Everything below was verified against the actual code, not just descriptions. Class/method names are as they appear in those repos (2021–2024 game versions) — re-verify signatures against current game DLLs (ilspycmd) before relying on one.
Why this document: these repos collectively map which Amplitude systems are patchable and how the community solved problems we share (lifecycle timing, per-entity runtime state, sim→presentation sequencing, distribution). Use it as a technique index when building the next Factory feature.
| Repo | Problem it solves | Headline technique for us | License | Last activity |
|---|---|---|---|---|
| Theadd/Humankind-GUI-Tools | In-game toolbox: unit spawner, cheats, live map editor, free cam |
PostAndTrackOrder(...).UponCompletion(...) + PresentationArmy.DoUpdateMesh
|
MIT | 2024-10 |
| Theadd/Modding.Humankind.DevTools | Framework library: game-state facades, lifecycle, hotkeys, window kit | Readiness gates; Interop-vs-Simulation dual access map | MIT | 2022-11 |
| Theadd/HumankindHacks | Cheat/QoL plugins + shared-project framework v2 |
AssetHunter (find which loaded bundle holds an asset); zero-Harmony coroutine architecture |
none | 2022-06 |
| Theadd/ModdingTools | CLI that scaffolds a BepInEx plugin workspace | Assembly publicizer + wildcard-reference build props; hot-reload loop | none | 2023-01 |
| Gedemon/HK-CultureUnlockFromTerritory | Cultures unlock only if you hold their historical territory |
Sandbox.ThreadStart/ThreadStarted lifecycle trio; Databases.GetDatabase<T>().Touch(row)
|
none | 2022-10 |
| Gedemon/HK-Uchronia | TCL successor + battle postures, AI faction choice, debug overlay | Per-entity save extension keyed by GUID; presentation controllers callable from Update()
|
none | 2022-04 |
| Gedemon/HK-Cognomen | Dynamic empire names from civics/ideology/era | Postfix the game's SimulationEventRaised_* handlers (our fire-on-attack pattern) |
none | 2022-04 |
| Gedemon/HK-DelayedAITurn | "Humans first, then AI" turn mode | Order veto: prefix DepartmentOfCommunication.ProcessOrder*, return false
|
none | 2022-03 |
| shakee2/shakee.Humankind.FameByScoring | Fame from periodic scoring rounds instead of era stars |
MajorEmpire.Serialize postfix → mod state inside vanilla saves; full lobby-option recipe |
MIT | 2023-01 |
| rykolu/HkFPCameraModSimple | First-person fly camera (F2) | Disable PresentationCameraMover, add own fly-cam, nearClipPlane = 0.01 → in-game model inspection |
none | 2022-01 |
| rykolu/HkModEntry | Pre-BepInEx custom mod loader (discontinued) | Anti-pattern (patched game DLL); repo doubles as decompiled Amplitude.UI reference |
none | 2021-09 |
| Touhma/Humankind_Plugin | JSON-configurable balance knobs XML mods can't reach | Postfix Initialize() on data definitions; write-defaults-then-read JSON config |
none | 2021-09 |
One author, one layered stack: DevTools is the base library-mod, GUI-Tools the flagship built
on it (hard [BepInDependency]), HumankindHacks a later monorepo re-extracting the framework,
ModdingTools an offline scaffolder. All BepInEx 5 / IMGUI.
Problem solved. Resurrects the 100+ hidden debug windows Amplitude shipped disabled inside the
game (MilitaryCheats, DistrictPainter, PawnAnimation, Pawns, GPUProfilerWindow, …) and
adapts ~15 into a toolbox: spawn any unit anywhere, paint districts, free camera, cheats, live map
editor.
How. All state changes go through the order bus — SandboxManager.PostOrder(new OrderSpawnArmy { WorldPosition = …, UnitDefinitions = new[]{ unitDefinitionName } }), OrderDamageUnits,
OrderChangeUnitsXP, editor orders like EditorOrderCreateExtensionDistrictAt. Reads selection via
Snapshots.ArmyCursorSnapshot.PresentationData and resolves GUID → presentation object with
Presentation.PresentationEntityFactoryController.GetArmy((ulong)guid). Extends the sealed
PresentationCursorController by caching its private MethodInfos and injecting a custom cursor
subclass — grafting behavior into presentation controllers without Harmony.
What we could use.
-
The sim→presentation refresh handshake — the single most relevant find:
PostAndTrackOrder(order, empireIndex).UponCompletion(() => army.DoUpdateMesh(false, false)). A sanctioned "make this pawn re-evaluate its visuals after the sim change lands" primitive — exactly the sequencing our respawn-after-load and freeze/strip effects currently approximate with frame delays. -
Instant test harness:
OrderSpawnArmy+PresentationCursorController.ChangeToDefaultCursor(new StaticString(unitDefName))spawns our Era-6 units on turn 1. Wiring this to a debug hotkey would remove most of our test friction (no more playing/dev-console to reach late eras). - Multiplayer/version gate: fetches a
VERSIONfile from raw.githubusercontent.com and disables all tools whenIsOnlineGame && LatestVersion != PLUGIN_VERSION— cheap desync guard for when the Factory ships publicly.
Problem solved. Gives other plugins documented facades (HumankindGame, HumankindEmpire),
a reliable loaded/new-turn lifecycle, attribute-driven hotkeys ([InGameKeyboardShortcut],
[OnGameHasLoaded] — discovered by scanning all loaded assemblies for [DevToolsModule]), and an
IMGUI window framework. Hot-reloads via BepInEx ScriptEngine (deploy to BepInEx\scripts\).
What we could use.
-
Readiness gates, reusable verbatim: game is safe to touch when
SandboxManager.IsStarted && Amplitude.Mercury.Presentation.Presentation.HasBeenStarted; UI root exists whenGameObject.Find("/WindowsRoot/SystemOverlays") != null. Its only Harmony patch — postfixAIController.InitializeOnLoad/InitializeOnStart, prefixOnNewTurnStarted— is how the whole framework learns loaded-vs-new-game-vs-turn. Cleaner anchors than our timer heuristics. -
The Interop-vs-Simulation dual-access map:
Amplitude.Mercury.Interop.AI.Entities.*= fast read snapshots,Amplitude.Mercury.Simulation.*= live sim. DevTools documents which to use when — the seam our pawn code lives in. - The
[DevToolsModule]assembly-scan pattern is a ready design if the Factory ever wants third-party model packs to self-register runtime hooks. - A central
R.Fieldsreflection cache (every privateFieldInfoin one file) so a game patch breaks exactly one file.
Problem solved. Cheat/QoL plugins (EndlessMovingArmies etc.) plus the framework as shared
projects. Notable: EndlessMovingArmies uses no Harmony at all — a coroutine polls until services
exist, then a MonoBehaviour loop reads/writes via Interop snapshots.
What we could use.
-
AssetHunter:AssetBundle.GetAllLoadedAssetBundles().Where(b => b.Contains(assetPath))— finds which loaded vanilla bundle contains a named asset at runtime. Directly attacks our "find the borrowed vanilla visual" problem without the Unity-Inspector detour. - The zero-Harmony architecture is more game-update-resilient for purely reactive effects — worth weighing for future per-pawn effects that only need to observe, not intercept.
- Ships its
GUISkinin an AssetBundle embedded as a manifest resource inside the DLL — single-file distribution trick for our packaging goal. - Honesty pattern for MP: announces itself in game chat (
HumankindGame.TrySendChatMessage) and honors anEnableInOnlineSessionsconfig.
Problem solved. dotnet-CLI that generates a complete plugin workspace for any Unity game:
detects the game install, installs BepInEx templates, and — the key part — publicizes every game
DLL into lib/references/ with a Directory.Build.props that wildcard-references them and
post-build-copies the plugin to BepInEx\scripts\ for ScriptEngine hot reload.
What we could use.
- Publicized references would eliminate most of our reflection: private members become direct calls that break at compile time after a game update instead of silently at runtime. Strong candidate for HumankindAssetFramework.csproj (generate locally, never distribute the publicized DLLs — that's how ModdingTools stays in the legal clear).
- The whole repo is a blueprint for our "distributable package for any Humankind modder" goal: game-path detection, templates, dry-run UX.
Four repos sharing one toolkit (HumankindModTool, credited to "AOM") and one architecture:
publicized DLLs, PatchAll() in Awake(), patch classes organized by target namespace.
All unlicensed and dormant since 2022 — treat as technique documentation, don't vendor code.
Problem solved. "True Culture Location": you can only become Rome if you hold Latium. Territory renaming, historical city names, culture-change territory loss, extra empire slots.
What we could use.
-
The Sandbox lifecycle trio — the canonical init/teardown skeleton:
Sandbox.ThreadStartprefix = entering game (init per-game state),ThreadStartedpostfix = world fully loaded (safe to touch databases),ThreadStartpostfix = exiting (clear all statics). Our respawn-after-load logic could anchor here instead of timers. -
Databases.GetDatabase<T>().Touch(row)— the universal "inject a database row at load time" API, proven forLocalizedStringElement(localization for custom unit names!),GameOptionDefinition,UIMapper. -
Map-hash-keyed external JSON with LoadOrder merging: third parties drop
*TCL.jsonin a known folder; the plugin discovers, validates against a map hash, and merges by priority. A proven shape if ourhaf_models.jsonever needs multi-pack merging. - Its
CollectibleManagerPatchcomments enumerate the definition databases relevant to us:PresentationUnitDefinition,UnitVisualAffinityDefinition,BuildingVisualAffinityDefinition. And its district code shows visual affinity is mutable simulation state (district.InitialVisualAffinityName = …) — the sim-side counterpart of our presentation-side pawn work, potentially a cleaner lever someday.
Problem solved. TCL successor: battle "postures" with forced auto-resolve, AI faction-choice rework, F3 territory debug overlay.
What we could use.
-
Per-battle persistent state: postfix
Battle.Serialize, keep aBattleExtensionkeyed bybattle.GUID— the template if fire-on-attack effects ever need battle-scoped state that survives saves. -
Presentation controllers are callable directly from plugin
Update():Presentation.PresentationTerritoryHighlightController.SetTerritoryVisibility(i, bool),PresentationCursorController.ChangeToDiplomaticCursor(...)— same static-hub family as ourPawnManagerwork; useful for debug overlays on model placement. -
UIManager.IsUiVisible = false+ a hotkey = clean-screenshot mode for reviewing injected models. - Parameter smuggling (encoding extra data into an existing int argument between UI order and sim processing) — hacky but proven way through the order pipeline without defining new orders.
Problem solved. Dynamic empire titles ("Celtic Kingdom", "People's Democratic Republic") derived from civics/ideology/era. A single ~1200-line file deliberately written as a modding tutorial — the best "how to write a Humankind Harmony mod" document in the ecosystem.
What we could use.
-
The ready-made pattern for our fire-on-attack hook: instead of subscribing to
SimulationEvent<T>.Raisedourselves, postfix the game's existing handler methods — e.g.CultureManager.SimulationEventRaised_CivicChoiceChanged(object sender, SimulationEvent_CivicChoiceChanged e). The handler already receives the typed event; no subscription lifecycle to manage; fires exactly when the sim does. (FameByScoring below shows the complementary variant: patchingSimulationEvent_X.Raiseitself.) -
Presentation-readiness flag: postfix
PresentationAvatarController.DoStart/DoShutdownto flip anisPresentationStartedstatic — a cleaner "presentation is alive" signal than timing. - Some behavior lives in stored delegates, not methods — Cognomen swaps
NotificationsController.AllConfigs[i].GetDescriptionviaNotificationDataConfig.Forge(...). Worth remembering when a Harmony target seems to not exist. - Its comments also document: prefer postfix over replacement "to reduce mod's maintenance between
game's patches"; the game writes HTML logs to
Documents\Humankind\Temporary Files.
Problem solved. "Humans first" turn mode: AI empires wait until every human has clicked End Turn. One file, ~230 lines, five patches — a good skeleton reference.
What we could use.
-
Order interception: prefix
DepartmentOfCommunication.ProcessOrderEmpireReady(order)andreturn falseto swallow the order — there's aProcessOrderXper order type, so this is the general veto point in the turn pipeline. -
AIController.RunAIDecisionCyclemarks the AI-turn boundary; the sandbox runs on its own thread (the mod blocks it withThread.Sleeppolling — possible there, never on the main thread).
Problem solved. Fame from periodic Military/City/State/Economy scoring rounds instead of era stars, with ~10 new lobby options and a fame-history tooltip on the empire banner.
What we could use.
-
Mod state inside vanilla saves — the standout pattern, and one of only two MIT repos:
a
MajorEmpireExtension : Amplitude.Serialization.ISerializableholding mod data, written/read by a postfix onMajorEmpire.Serialize(Serializer)viaserializer.SerializeElement("MajorEmpireExtension", …), switching onserializer.SerializationMode(Read/Write). Allocation onMajorEmpire.InitializeOnStartpostfix, teardown onSandbox.ThreadStart. Adaptable to per-unit state — if model assignments or freeze-flags ever need to survive save/load without our sidecar JSON, this is how. -
Session-type detection: its
Sandbox.ThreadStartpatch castsSandboxThreadStartSettings.Parameter()toSandboxStartSettings/ScenarioStartSettings/GameSaveDescriptorto distinguish new game vs save-load vs scenario — a more principled home for ourrespawnAfterLoadflag than detecting after the fact. -
The full lobby-option recipe (
HumankindModTool/GameOptionHelper.cs): prefixOptionsManager<GameOptionDefinition>.Load,ScriptableObject.CreateInstance<GameOptionDefinition>(),Touch()it plus%{key}Titlelocalization plusOptionUIMapperrows → a real in-game option instead of a BepInEx config file. Read back viaServices.GetService<IGameOptionsService>().GetOption(new StaticString(key)).CurrentValue. - Proven combat-event targets for us:
SimulationEvent_BattleTerminated.Raise,SimulationEvent_UnitKilledByOther.Raise,SimulationEvent_NewTurnBegin.Raise. -
UI injection by transform path:
GameObject.Find("WindowsRoot/InGameOverlays/EmpireBanner/ _FamePennant/FameScore/")then attach a child GameObject with Amplitude UI components — the quick-and-dirty way to add HUD elements.
Problem solved. F2 toggles a first-person WASD fly camera. ~150 lines, no Harmony.
What we could use.
-
A near copy-pasteable in-game model-inspection camera:
GameObject.Find("Camera")→GetComponent<PresentationCameraMover>()(Amplitude's RTS camera driver) →enabled = false, add your own fly-cam MonoBehaviour, setnearClipPlane = 0.01f. Lets us orbit injected pawns at close range in the real renderer — the truthful complement to our editor preview (a custom posed viewer is impossible; this sidesteps it entirely). - Its comments document the scene's cameras (
ImpostorCamera,AvatarCamera,Camera,MainView,UIFxCamera) and the pawn render layers:PresentationPawn,PresentationPawnHidden,PresentationPawnGhost,PresentationPawnGhostAndOpaque— useful if we ever need layer tricks on injected geometry.
Problem solved. A 2021 mod loader that patched Amplitude.UI.dll itself (decompile → inject a
DLL-scanner into UIUpdatingManager.Update() → recompile). Author's own verdict: "Discontinued,
since BepInEx does this but better."
Takeaways. Validates our BepInEx no-exe-patching approach — replacing shipped DLLs breaks every
game update. Two residual nuggets: a convention-based plugin contract (<Name>.Main.Load() +
optional per-frame OnUpdate) if the Factory ever wants drop-in model packs with runtime hooks; and
the repo is a grep-able decompiled snapshot of Amplitude.UI circa 1.0.x.
Problem solved. Makes code-initialized balance values (game-speed multipliers, era-star requirements, pollution thresholds, end-game conditions) configurable via JSON — the plugin half of a data-mod + plugin pairing, existing precisely because XML/data mods can't touch definition fields set in code.
What we could use.
-
Postfix
Initialize()on data definitions (GameSpeedDefinition,EraDefinition, …) — the general lever for anything living in anAmplitude.Mercury.Data.Simulationdefinition. Relevant if the registry ever needs to adjustUnitDefinition/pawn definitions in code rather than assets. -
Write-defaults-then-read config: first run serializes the built-in defaults out to
BepInEx/config/...json; later runs read user edits back. Self-documenting — a nice touch forhaf_models.jsontemplate generation. - Compact Harmony idioms:
___PrivateFieldparameter injection to read private manager state; prefix with__result+return falsefor full replacement. - A (disabled) postfix on
Databases.GetDatabase(Type)that dumps every game database to JSON — a poor-man's data-mining tool worth resurrecting for exploration. - Manages publicized game DLLs in per-game-version folders (
1.0.2.139/,1.3.248/) — sane layout for tracking game patches.
-
The order bus + completion callback (
SandboxManager.PostAndTrackOrder(...).UponCompletion(...)→PresentationArmy.DoUpdateMesh) is the ecosystem's sanctioned sim→presentation sequencing primitive. Our respawn-after-load and any future per-pawn effect should evaluate it. -
Lifecycle anchors instead of timers — a complete, proven set exists:
Sandbox.ThreadStart(prefix=enter / postfix=exit) +ThreadStarted(world ready) +Presentation.HasBeenStarted+PresentationAvatarController.DoStart/DoShutdown+ session-type detection viaSandboxThreadStartSettings.Parameter()casts. -
Two proven combat-hook idioms for fire-on-attack-style effects: postfix the game's
SimulationEventRaised_*handler methods (Cognomen), or patchSimulationEvent_X.Raiseitself (FameByScoring —BattleTerminated,UnitKilledByOtherare proven targets). -
Databases.GetDatabase<T>().Touch(row)injects database rows at load time — localization for custom unit names, real lobby options (full recipe in FameByScoring'sGameOptionHelper), UI mappers. -
Save-file extensions — postfix
Serialize(Serializer)on any sim entity + anISerializablesidecar = mod state inside vanilla saves, no external files. - Publicized assemblies (ModdingTools' generate-locally approach) would convert our runtime reflection risk into compile-time errors after game patches.
-
Dev-velocity trio:
OrderSpawnArmydebug spawner + FP inspection camera + ScriptEngine hot reload would together remove most of our test friction. - Licensing reality: only GUI-Tools, DevTools, and FameByScoring are MIT. Everything else has no license (default all-rights-reserved) — borrow techniques and patch targets freely, never copy code verbatim into anything we distribute. Several repos also commit game DLLs; don't imitate that.
- Everything is dormant (mostly 2021–2022; only GUI-Tools saw 2024 activity). Patch targets documented here predate recent game versions — treat this file as a map, and re-verify each signature with ilspycmd before use.
Get started
- Getting Started
- Installation
- Troubleshooting
- Authoring State and Deployment
- Mod Editor version.xml Recovery
- Building
- Backup
Author models and behavior
- Editor Tools
- Factory Manual
- Vehicle Lab Quickstart
- Animated Models
- Animation Pitfalls
- Textures
- Unit Size
- Unit Combat Behavior
- Formations
- Pawn Props
- Projectiles
- Game Sound Lab
- Firing on Attack
- Turn Ease
- Facing Persistence
- Donor Clip Flight
Districts and wonders
Ship and operate
Internals and project