7.0 KiB
Contributing to Weave Loader
Thanks for your interest in contributing. This guide covers how the project is organized, how to build it, and what conventions to follow.
Project Overview
Weave Loader is split into four main components plus an example mod:
| Component | Language | Purpose |
|---|---|---|
| WeaveLoader.Launcher | C# | Console app that launches the game suspended and injects the runtime DLL |
| WeaveLoaderRuntime | C++ | DLL injected into the game process; handles PDB parsing, hooking, .NET hosting, and all native game interaction |
| WeaveLoader.Core | C# | Loaded inside the game process by the runtime; discovers mods, manages lifecycle |
| WeaveLoader.API | C# | Public API that mod authors reference; contains registries, events, and the IMod interface |
| ExampleMod | C# | Working sample mod that registers a block, item, recipe, and event handler |
Communication Flow
Launcher ──(inject)──> Runtime (C++) ──(hostfxr)──> Core (C#) ──(reflection)──> Mods
▲ │
└──────────── P/Invoke (NativeExports) ─────────────────────┘
Mods call Registry.Block.Register(...) in C#, which calls NativeInterop.native_register_block(...) via P/Invoke, which arrives in NativeExports.cpp in the C++ runtime, which calls GameObjectFactory::CreateTile(...) to construct a real game Tile object through resolved PDB symbols.
Building
Prerequisites
- Visual Studio 2022+ with C++ Desktop Development and .NET 8 workloads
- CMake 3.24+
- .NET 8.0 SDK
- The target game must have been compiled with PDB generation (
/Zior/ZI)
C++ Runtime
cd WeaveLoaderRuntime
cmake -B build -A x64
cmake --build build --config Release
The output is WeaveLoaderRuntime.dll in build/Release/.
C# Projects
dotnet build WeaveLoader.sln -c Debug
All outputs go to build/.
Full Clean Build
dotnet clean WeaveLoader.sln
cd WeaveLoaderRuntime && cmake --build build --target clean && cd ..
dotnet build WeaveLoader.sln -c Debug
cd WeaveLoaderRuntime && cmake --build build --config Release
Development Guidelines
Core Principles
-
No game source modifications. All interaction with the game is through DLL injection, PDB symbol resolution, and MinHook detours. Never suggest changes to the game's own source code.
-
Fix root causes, not symptoms. If something crashes, understand why at the engine level. Don't add null checks around broken pointers -- resolve the actual construction or lifecycle issue.
-
Use PDB symbols, not hardcoded offsets. Game updates change binary layouts. All function pointers must be resolved from the PDB at runtime using their decorated (mangled) names.
C++ Runtime (WeaveLoaderRuntime/)
-
Symbol resolution: Add new game functions in
SymbolResolver.cpp. Usellvm-pdbutil dump --publicsor the built-inPdbParser::DumpMatching()to find the correct mangled name. MSVC mangles are sensitive to calling convention, const qualifiers, and parameter types -- double-check them. -
Hooking: New hooks go in
GameHooks.cpp(implementation) andHookManager.cpp(installation). Always store the original function pointer so the hook can call through to the original. -
Native exports: Functions called by C# are declared in
NativeExports.cppwithextern "C" __declspec(dllexport). Keep signatures simple (primitive types,const char*). Add corresponding[DllImport]entries inWeaveLoader.API/NativeInterop.cs. -
Logging: Use
LogUtil::Log(message)for normal output (goes toweaveloader.log). Never useprintforstd::cout. -
Memory safety: The game's memory layout is discovered at runtime. Always validate pointers before dereferencing. Add diagnostic logging when traversing game structures.
C# API (WeaveLoader.API/)
-
Backward compatibility: The API is what mod authors compile against. Avoid breaking changes to public interfaces. New features should be additive.
-
Thread safety:
OnTick()runs on the game thread. Registration methods are called during init. The API's internal P/Invoke calls are not thread-safe by design. -
Naming: Use
PascalCasefor public members andcamelCasefor parameters. Registry methods follow the patternRegistry.<Type>.Register(id, properties). -
Identifiers: All content IDs use the
"namespace:path"format. TheIdentifierclass handles parsing. Default namespace is"minecraft".
C# Core (WeaveLoader.Core/)
-
Mod isolation: Each mod's lifecycle method is wrapped in try/catch in
ModManager.cs. A failing mod should never crash the loader or other mods. -
Discovery: Mods are found by scanning
mods/*/for assemblies containing types that implementIModand have the[Mod]attribute.
Testing
- Always test DLL injection on a real game build with PDB symbols available.
- Verify that new hooks don't break existing functionality (creative inventory, item names, textures).
- Check
logs/crash.logafter crashes -- the symbolicated stack trace usually points directly to the issue. - Test with multiple mods loaded to ensure isolation works.
Adding a New Registry Type
To add support for a new game object type (e.g., particles):
- PDB: Find the constructor and relevant setters using
PdbParser::DumpMatching("ParticleType")orllvm-pdbutil - SymbolResolver: Add the mangled symbol constants and resolution in
SymbolResolver.cpp - GameObjectFactory: Add a
CreateParticle()function that allocates memory and calls the resolved constructor - NativeExports: Add
native_register_particle()as a C export - NativeInterop.cs: Add the
[DllImport]declaration - API: Create
ParticleRegistry.cs,ParticleProperties.csinWeaveLoader.API/Particle/ - Registry.cs: Add
public static ParticleRegistry Particle { get; }to the facade - ExampleMod: Add a usage example
Adding a New Game Hook
- Find the target function's mangled name in the PDB
- Add the symbol constant and resolution in
SymbolResolver.cpp - Define the function pointer typedef and
Original_*variable inGameHooks.h - Implement
Hooked_*inGameHooks.cpp, callingOriginal_*at the appropriate point - Add
MH_CreateHook/MH_EnableHookcalls inHookManager.cpp
Commit Messages
- Use present tense: "Add particle registry" not "Added particle registry"
- Be specific: "Fix creative tab pagination after mod item injection" not "Fix bug"
- Prefix with area when helpful: "runtime: resolve Tile::setLightLevel from PDB"
Pull Requests
- Fork the repository and create a feature branch from
main - Include a clear description of what the PR does and why
- Ensure both C++ and C# projects build without errors
- Test DLL injection on a real game build if possible
- Keep PRs focused -- one feature or fix per PR