MonoWeaver.Cecil10 0.2.0

dotnet add package MonoWeaver.Cecil10 --version 0.2.0
                    
NuGet\Install-Package MonoWeaver.Cecil10 -Version 0.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MonoWeaver.Cecil10" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MonoWeaver.Cecil10" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="MonoWeaver.Cecil10" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MonoWeaver.Cecil10 --version 0.2.0
                    
#r "nuget: MonoWeaver.Cecil10, 0.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MonoWeaver.Cecil10@0.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MonoWeaver.Cecil10&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=MonoWeaver.Cecil10&version=0.2.0
                    
Install as a Cake Tool

MonoWeaver

MonoWeaver on NuGet MonoWeaver.Cecil10 on NuGet License CI

简体中文

MonoWeaver helps C# mod developers find a piece of compiled game logic and safely change it. You describe the expression you are looking for—such as a damage calculation, a method call, or an if condition—and then choose what should happen before, after, or instead of it.

You normally do not need to search for a fixed list of IL instructions. That makes a hook easier to read and less likely to break when the compiler adds a temporary local or uses a different branch layout.

Typical uses include:

  • changing a calculated value such as damage, price, or cooldown;
  • logging a value without changing the game result;
  • replacing or removing a game action;
  • changing a short-circuit if condition;
  • applying the same matching API to a Cecil MethodDefinition or a MonoMod ILContext;
  • checking the edited method before it is written or executed.

Compatibility

Package Mono.Cecil Target frameworks
MonoWeaver 0.11.2+ netstandard2.0
MonoWeaver.Cecil10 0.10.00.10.4 netstandard2.0

Quick start

Both examples find baseDamage + bonus in Game.Player.ComputeDamage. Lambda parameters bind to target-method parameters with the same names. They use this callback:

using System;

public static class ModHooks
{
    public static int ClampDamage(int value)
        => Math.Min(Math.Max(value, 0), 999);
}

Runtime hook with MonoMod

Use this form when the mod loader invokes your hook with an ILContext for ComputeDamage:

using System;
using MonoMod.Cil;
using MonoWeaver.Cecil;
using MonoWeaver.CFG;
using MonoWeaver.Patterns;

public static class DamagePatch
{
    public static void Patch(ILContext il)
    {
        var pattern = Cil.Value((int baseDamage, int bonus) =>
            baseDamage + bonus);

        il.Method.Match(pattern)
          .Single()
          .Transform(ModHooks.ClampDamage)
          .Apply(VerifyOptions.Mod);
    }
}

MonoWeaver handles MonoMod branch labels while applying the rewrite. Every label must already point to a valid target when Apply is called.

Offline DLL patch

Use this form when the output should be a patched assembly on disk:

using System;
using System.Linq;
using Mono.Cecil;
using MonoWeaver.Cecil;
using MonoWeaver.CFG;
using MonoWeaver.Patterns;

public static class DamagePatcher
{
    public static void Patch(string inputPath, string outputPath)
    {
        using var module = ModuleDefinition.ReadModule(inputPath);

        var method = module.Types
            .Single(type => type.FullName == "Game.Player")
            .Methods.Single(candidate => candidate.Name == "ComputeDamage");

        var pattern = Cil.Value((int baseDamage, int bonus) =>
            baseDamage + bonus);

        method.Match(pattern)
              .Single()
              .Transform(ModHooks.ClampDamage)
              .Apply(VerifyOptions.Full);

        module.Write(outputPath);
    }
}

Single() is deliberate: it fails when there is no match or when more than one place matches. For a mod hook, making the pattern more specific is safer than silently patching the first candidate.

For an offline patch, deploy the assembly that contains ModHooks with the patched game assembly. Instance delegates and closures are runtime-only because they refer to objects in the current process.

Choose the operation by intent

What the mod should do API
Run a callback before the matched code Before(...)
Run a callback after a value or action After(...)
Read the old value and return a new one Transform(...)
Read or log the old value without changing it Observe(...)
Skip the old code and provide a replacement Replace(...)
Remove a matched no-result action Remove()

Every operation creates a RewritePlan. Nothing changes until you call Apply(). Always verify: if the check fails, MonoWeaver restores the method and throws instead of leaving a half-applied edit. Use VerifyOptions.Mod for a runtime hook and VerifyOptions.Full for an offline patch.

Values, actions, and conditions have slightly different valid operations. In particular, a branch-based condition has no single After(...) point; use Transform, Observe, Replace, or Before instead.

Capturing one part of a larger match

The root match can be edited directly. When the hook should target one of the parameters, the lambda parameter is already the capture — read it back by parameter name:

var pattern = Cil.Value((int baseDamage, int bonus) => baseDamage + bonus);

var match = method.Match(pattern).Single();

match.Arg("baseDamage").Transform(ModHooks.ClampDamage)
                       .Apply(VerifyOptions.Full);

When the target is a compound sub-expression, declare that part as a standalone Cil.Value fragment, use it directly in the expression, and read it back through the same object (match[fragment]).

The matcher follows an unambiguous compiler-generated temporary by default. If several assignments could reach the same local read, it refuses to guess.

When game types are not referenced

If the mod project references the game assembly, the lambda form above is usually the easiest. If you cannot or do not want to load those types, describe them by assembly and type name:

var game = CilSymbols.In("GameAssembly");
var player = game.Type("Game.Player");
var getScore = player.InstanceMethod("GetScore", CilType.Int32);

var scorePattern = Cil.Value(
    P.Arg(0, player.Assignable())
     .Call(getScore));

Both forms produce the same kind of match result and use the same rewrite operations.

Documentation

Full documentation, in English and Simplified Chinese: https://pkuyo.github.io/MonoWeaver/en/

  • Using MonoMod — runtime integration through ILContext.
  • Your first hook — the complete offline patch flow, step by step.
  • Patterns by example — common game functions shown beside the pattern and the exact part it finds.
  • Rewrite operations — what Before, After, Transform, Observe, Replace, and Remove do per match kind.
  • Verification — recommended checks and plain-language troubleshooting.
  • Type matching — practical type comparisons for game classes, callbacks, and member access.

Build and test the repository

The whole solution follows the same CecilFlavor switch, so the tests run against either Cecil generation:

dotnet test MonoWeaver.slnx
dotnet test MonoWeaver.slnx -p:CecilFlavor=Latest

Build both packages locally into artifacts/nupkg/:

dotnet pack MonoWeaver/MonoWeaver.csproj -c Release -p:CecilFlavor=Cecil10 -p:Version=0.1.1
dotnet pack MonoWeaver/MonoWeaver.csproj -c Release -p:CecilFlavor=Latest -p:Version=0.1.1

The main projects in this repository are:

Project Purpose
MonoWeaver The library used by mods.
tests/MonoWeaver.PatternTests Matching, rewriting, delegate, and MonoMod compatibility tests.
tests/MonoWeaver.ILTests Edited-method checker tests.
tests/MonoWeaver.DocSamples Source of every code block in the docs; compiled, not run.
MonoWeaver.Fuzz Automated stress tests.
benchmarks/MonoWeaver.Benchmarks IL verification throughput, plus a patch-time comparison against MonoMod.
dotnet run -c Release --project benchmarks/MonoWeaver.Benchmarks -- --verify-only --max-method-us 50000
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0 86 8/29/2026
0.1.2 84 8/28/2026
0.1.1 87 8/26/2026
0.1.0 97 8/23/2026