Rewrite README and CONTRIBUTING with comprehensive documentation

This commit is contained in:
Jacobwasbeast
2026-03-06 23:53:05 -06:00
parent fa195fdc2e
commit 455abc155a
2 changed files with 414 additions and 51 deletions
+111 -18
View File
@@ -1,22 +1,37 @@
# Contributing to Weave Loader
Thank you for your interest in 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 Structure
## Project Overview
- **WeaveLoader.Launcher/** -- C# console app that launches the game and injects the runtime DLL
- **WeaveLoaderRuntime/** -- C++ DLL injected into the game process (hooks, .NET hosting, native exports)
- **WeaveLoader.Core/** -- C# assembly loaded inside the game process (mod discovery, lifecycle management)
- **WeaveLoader.API/** -- C# class library that mod authors reference (IMod, Registry, Events)
- **ExampleMod/** -- Sample mod demonstrating the API
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
- 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
@@ -26,23 +41,101 @@ cmake -B build -A x64
cmake --build build --config Release
```
The output is `WeaveLoaderRuntime.dll` in `build/Release/`.
### C# Projects
```bash
dotnet build Weave Loader.sln -c Release
dotnet build WeaveLoader.sln -c Debug
```
## Guidelines
All outputs go to `build/`.
- Keep game source modifications at zero -- all integration is via injection and hooking
- Test hooks against the latest game build with PDB symbols
- Use namespaced string IDs (`"modid:name"`) for all registered content
- Catch exceptions from mod code to prevent crashes
- Document public API methods with XML doc comments
### 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
- Include a clear description of what your PR does
- Make sure the C# projects build without warnings
- 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