Skip to content

Repository files navigation

ModInteropImportGenerator

Generates MonoMod.ModInterop imports based on a given method signature and name.

Terminology

TermDefinition
DependencyAn assembly that includes one or more export classes.
Export classA class annotated with [ModExportName] that contains one or more export methods.
Export nameThe name passed to the [ModExportName] annotation.
Export methodA public static method inside an export class.
Import classA class annotated with [GenerateImports] that contains one or more import methods.
Import nameThe name passed to the [GenerateImports] annotation.
Import methodA public static partial method inside an import class that does not have a method body.
ImportThe process of assigning the correct export method implementation to import methods of a given import class, based on the import name as well as the import method name and signature.

Demonstration

Assume the following export class:

[ModExportName("CommunalHelper.DashStates")]publicstaticclassDashStates{
#region DreamTunnel
publicstaticintGetDreamTunnelDashState(){returnSt.DreamTunnelDash;}publicstaticboolHasDreamTunnelDash(){returnDreamTunnelDash.DreamTunnelDashCount>0;}publicstaticintGetDreamTunnelDashCount(){returnDreamTunnelDash.DreamTunnelDashCount;}publicstaticComponentDreamTunnelInteraction(Action<Player>onPlayerEnter,Action<Player>onPlayerExit){returnnewDreamTunnelInteraction(onPlayerEnter,onPlayerExit);}
#endregion
#region Seeker
publicstaticboolHasSeekerDash(){returnSeekerDash.HasSeekerDash;}publicstaticboolIsSeekerDashAttacking(){returnSeekerDash.SeekerAttacking;}
#endregion
}

Normally, to import it, you would define your import class like so, and call typeof(DashStates).ModInterop();:

[ModImportName("CommunalHelper.DashStates")]publicstaticclassDashStates{publicstaticFunc<int>GetDreamTunnelDashState;publicstaticFunc<bool>HasDreamTunnelDash;publicstaticFunc<int>GetTunnelDashCount;publicstaticFunc<Action<Player>,Action<Player>,Component>DreamTunnelInteraction;publicstaticFunc<bool>HasSeekerDash;publicstaticFunc<bool>IsSeekerDashAttacking;}

This form can be confusing for new code modders and may sometimes be difficult to read.

However, with this source generator you can now copy-paste the method signatures and mark them as partial! The source generator will figure out the rest.

[GenerateImports("CommunalHelper.DashStates")]publicstaticpartialclassDashStates{publicstaticpartialintGetDreamTunnelDashState();publicstaticpartialboolHasDreamTunnelDash();publicstaticpartialintGetDreamTunnelDashCount();publicstaticpartialComponentDreamTunnelInteraction(Action<Player>onPlayerEnter,Action<Player>onPlayerExit);publicstaticpartialboolHasSeekerDash();publicstaticpartialboolIsSeekerDashAttacking();}
  • The definition is more readable (literally just a function)
  • You don't have to convert function signatures into Func<...>s or Action<...>s, or define your own delegate types
  • You get parameter names as a bonus
  • You don't have to constantly slap an ?.Invoke(...) on the imported methods if the dependency is optional

Referencing

Add the ModInteropImportGenerator NuGet package to your csproj like so:

<ItemGroup>
<PackageReferenceInclude="ModInteropImportGenerator"Version="*" />
</ItemGroup>

Usage

Importing methods is done in a very similar fashion to how it was previously done with [ModImportName] and typeof(...).ModInterop();.

Define a public static partial class and give it the [GenerateImports] attribute. Make sure the import name matches the export name you're interested in.

// DashStates.csusingModInteropImportGenerator;[GenerateImports("CommunalHelper.DashStates")]publicstaticpartialclassDashStates{}

Next, define import methods that have the same name and signature as the export methods that you're interested in.
The easiest way would be to copy-paste the export method definitions, leave out the method bodies and mark them as partial(and of course add the semicolon at the end).

// DashStates.csusingModInteropImportGenerator;[GenerateImports("CommunalHelper.DashStates")]publicstaticpartialclassDashStates{publicstaticpartialintGetDreamTunnelDashState();publicstaticpartialboolHasDreamTunnelDash();publicstaticpartialintGetDreamTunnelDashCount();publicstaticpartialComponentDreamTunnelInteraction(Action<Player>onPlayerEnter,Action<Player>onPlayerExit);publicstaticpartialboolHasSeekerDash();publicstaticpartialboolIsSeekerDashAttacking();}

Finally, call the Load() method on the import class. This method is automatically generated by the source generator.

// YourModModule.cspublicoverridevoidLoad(){DashStates.Load();// ...}

You may now call the import methods.
By default, the source generator will treat the import class as an optional dependency. If the dependency is not present at the time Load() is called, the import methods will throw an exception when called.

To safely call an import method, you must check if the IsImportedbool property is true.

if(DashStates.IsImported)DashStates.GetDreamTunnelDashState();

You can tell the source generator to treat the import class as a required dependency by setting the RequiredDependency property to true. This will throw an exception directly in Load() if any methods fail to import.

// DashStates.csusingModInteropImportGenerator;[GenerateImports("CommunalHelper.DashStates",RequiredDependency=true)]publicstaticpartialclassDashStates{// ...}

You can see the state of the import in the ImportState property of the import class.

Building

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

About

A source generator for MonoMod.ModInterop imports, letting the users define imports using the regular method definition syntax.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages