Files
LegacyWeaveLoader/CONTRIBUTING.md

142 lines
7.0 KiB
Markdown

# 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 (`/Zi` or `/ZI`)
### C++ Runtime
```bash
cd WeaveLoaderRuntime
cmake -B build -A x64
cmake --build build --config Release
```
The output is `WeaveLoaderRuntime.dll` in `build/Release/`.
### C# Projects
```bash
dotnet build WeaveLoader.sln -c Debug
```
All outputs go to `build/`.
### Full Clean Build
```bash
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
1. **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.
2. **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.
3. **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`. Use `llvm-pdbutil dump --publics` or the built-in `PdbParser::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) and `HookManager.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.cpp` with `extern "C" __declspec(dllexport)`. Keep signatures simple (primitive types, `const char*`). Add corresponding `[DllImport]` entries in `WeaveLoader.API/NativeInterop.cs`.
- **Logging**: Use `LogUtil::Log(message)` for normal output (goes to `weaveloader.log`). Never use `printf` or `std::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 `PascalCase` for public members and `camelCase` for parameters. Registry methods follow the pattern `Registry.<Type>.Register(id, properties)`.
- **Identifiers**: All content IDs use the `"namespace:path"` format. The `Identifier` class 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 implement `IMod` and 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.log` after 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):
1. **PDB**: Find the constructor and relevant setters using `PdbParser::DumpMatching("ParticleType")` or `llvm-pdbutil`
2. **SymbolResolver**: Add the mangled symbol constants and resolution in `SymbolResolver.cpp`
3. **GameObjectFactory**: Add a `CreateParticle()` function that allocates memory and calls the resolved constructor
4. **NativeExports**: Add `native_register_particle()` as a C export
5. **NativeInterop.cs**: Add the `[DllImport]` declaration
6. **API**: Create `ParticleRegistry.cs`, `ParticleProperties.cs` in `WeaveLoader.API/Particle/`
7. **Registry.cs**: Add `public static ParticleRegistry Particle { get; }` to the facade
8. **ExampleMod**: Add a usage example
## Adding a New Game Hook
1. Find the target function's mangled name in the PDB
2. Add the symbol constant and resolution in `SymbolResolver.cpp`
3. Define the function pointer typedef and `Original_*` variable in `GameHooks.h`
4. Implement `Hooked_*` in `GameHooks.cpp`, calling `Original_*` at the appropriate point
5. Add `MH_CreateHook` / `MH_EnableHook` calls in `HookManager.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