Contributor Architecture
For people working on Concord itself. If you're writing a normal mod, you can skip this.
Layers
Concord is split into layers so mod authors get a small API while the runtime work stays isolated.
| Layer | Current project | Job |
|---|---|---|
| Detour | Concord.Detour |
Redirect a game method to a generated wrapper |
| Emit | Concord.Emit |
Build the generated wrapper method |
| Model | Planned Concord.Model |
Track owners, ordering, apply, and unpatch state |
| Public API | Planned Concord |
Expose Patcher, Apply<TPatch>(), PatchAll, and reverse patches |
| Generator | Planned Concord.Generators |
Validate patch templates at build time |
How composition works today
WrapperComposer.Compose does the current low-level work:
- Resolve async and iterator methods to generated
MoveNextmethods. - Clone the original body into a pristine method.
- Copy the original body into a wrapper spine.
- Copy injection bodies into the wrapper.
- Lower marker calls like
CallbackInfo.Cancel(). - Remap shadow fields to real target fields.
- Emit the wrapper method.
The result is a ComposeResult: Wrapper is the generated method to install as the detour replacement, and PristineClone is a clean clone of the original method body.
Detour layer
IDetourBackend is the interface Concord uses to install a wrapper. The default is MonoModDetourBackend, which sits on top of MonoMod.Core. IDetourHandle.Dispose() undoes and disposes the detour. That's the foundation for real unpatching.
Emit layer
BodyCopier owns most of the IL-copying mechanics: cloning original instructions and exception handlers, remapping target arguments, mapping template shadow fields to target fields, lowering callback markers into wrapper locals, splicing around injections, and inserting or wrapping call-site injections.
Invalid shapes fail with ConcordEmitException and a stable CONCxxx code. These codes should stay stable. Users and tests will rely on them.
The rule
When adding a new user-facing feature, keep the beginner API simple first. Internals can be as complex as they need to be, but a mod author's patch should still read like normal C#.