CelesteMod.Roslyn.ModLifecycleAttributes 1.0.0

dotnet add package CelesteMod.Roslyn.ModLifecycleAttributes --version 1.0.0
                    
NuGet\Install-Package CelesteMod.Roslyn.ModLifecycleAttributes -Version 1.0.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="CelesteMod.Roslyn.ModLifecycleAttributes" Version="1.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CelesteMod.Roslyn.ModLifecycleAttributes" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="CelesteMod.Roslyn.ModLifecycleAttributes">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
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 CelesteMod.Roslyn.ModLifecycleAttributes --version 1.0.0
                    
#r "nuget: CelesteMod.Roslyn.ModLifecycleAttributes, 1.0.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 CelesteMod.Roslyn.ModLifecycleAttributes@1.0.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=CelesteMod.Roslyn.ModLifecycleAttributes&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=CelesteMod.Roslyn.ModLifecycleAttributes&version=1.0.0
                    
Install as a Cake Tool

ModLifecycleAttributes

A C# source generator for Celeste mods that automatically generates consolidated lifecycle method invocations based on attributes.

Problem

As mods grow larger, more and more work is being done in the module's Load(), Initialize(), LoadContent(), and Unload() lifecycle methods, making it difficult to navigate.

Modders often solve this by creating static methods in each class that needs to do work during these lifecycle methods (most commonly Load() and Unload()) and calling them from the module respectively.
Some modders instead choose to create [Load]/[Unload] attributes, annotate the methods with them and use reflection to call them, instead of listing each invocation manually.

Both approaches have their benefits and drawbacks: reflection is slower but more convenient, while manually listing each call is faster but more verbose.

Solution

Since attribute information is present at compile-time, we can leverage source generation to inspect the project and generate the method calls for us.
This gets us the best of both worlds: convenience and performance.

The generator provides [OnLoad], [OnInitialize], [OnLoadContent], and [OnUnload] attributes for users to mark their methods with.
At compile-time, it will generate a LifecycleMethods class in your project's root namespace, with methods that aggregate the invocations of the annotated methods.

The only thing left for you to do is to call the generated methods from your module.

The annotated methods must be static and accessible from the generated class.

Example

Suppose that you have a Load() method that you need to call on your module's Load():

namespace Celeste.Mod.MyFluffyMod;

public static class FluffManager
{
    internal static void Load()
    {
        // ...
    }
}

Annotate your method with [OnLoad] from Celeste.Mod.Roslyn.ModLifecycleAttributes:

using Celeste.Mod.Roslyn.ModLifecycleAttributes;

namespace Celeste.Mod.MyFluffyMod;

public static class FluffManager
{
    // Mark this method to be called on your module's Load()
    [OnLoad]
    internal static void Load()
    {
        // ...
    }
}

The generator will generate a LifecycleMethods class with an OnLoad() method in your mod's root namespace - Celeste.Mod.MyFluffyMod in this case. Call it in your module's Load() method:

namespace Celeste.Mod.MyFluffyMod;

public class MyFluffyModule : EverestModule
{
    public override void Load()
    {
        LifecycleMethods.OnLoad();
    }
}

Now FluffManager.Load() will be called when your mod module is loaded.

Inspecting the generated OnLoad() method, you will see this:

// <auto-generated />

namespace Celeste.Mod.MyFluffyMod;

internal static partial class LifecycleMethods
{
    /// <summary>
    ///   Run all methods marked with <see cref="Celeste.Mod.Roslyn.ModLifecycleAttributes.OnLoadAttribute"/>.
    ///   This method should be called in your module's <c>Load()</c> method.
    /// </summary>
    public static void OnLoad()
    {
        global::Celeste.Mod.MyFluffyMod.FluffManager.Load();
    }
}

Referencing

Add the CelesteMod.Roslyn.ModLifecycleAttributes NuGet package to your csproj like so:

<ItemGroup>
    <PackageReference Include="CelesteMod.Roslyn.ModLifecycleAttributes" Version="*" />
</ItemGroup>

After restoring the solution, the attributes should be available in your project.
Annotating at least one method with them will make the corresponding LifecycleMethods method available.

Building

Clone the solution and build the ModLifecycleAttributes project. Prefer Release mode as it's more optimized.

Building in Release mode also generates a NuGet package.

Before publishing to NuGet, remember to bump the package version in the .csproj file according to SemVer and update CHANGELOG.md.

Testing the NuGet Package

Packages pushed to https://nuget.org/ are immutable, meaning any mistake you make there will be permanent.
Fortunately, you can test the NuGet package locally before pushing it for real.

First, open the package and inspect it. .nupkg files are .zip files in disguise.
Make sure only the necessary files are present.

Second, create a NuGet package source that points to some folder if you don't already have one.
You will be able to safely push the package there for testing and have other projects be able to reference it, so long that source is enabled.

dotnet nuget add source --name "Local NuGet Packages" "path/to/package/source/folder"

Then, push the package to the test source.

dotnet nuget push "path/to/package.nupkg" --source "Local NuGet Packages"

Now, open a project you want to test the NuGet package in.
Reference the package in its .csproj like below, replacing the version with the one you just built.

<ItemGroup>
  <PackageReference Include="CelesteMod.Roslyn.ModLifecycleAttributes" Version="x.y.z" />
</ItemGroup>

Using a version already present in NuGet may use the published package instead of your test package.
Remember to bump the version before building.

Restore the solution for the package to be available.
Now you can test and make sure the package works as expected.

If you make changes to the package and push it again, you will need to clear the NuGet cache.
Your IDE may interfere here, so close it before clearing the cache.

dotnet nuget locals all --clear

Once you feel you're ready to publish, you can disable the local source without deleting it.
You may enable it later by rerunning the command below, replacing disable with enable.

dotnet nuget disable source "Local NuGet Packages"
There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

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
1.0.0 153 4/18/2026