-
Notifications
You must be signed in to change notification settings - Fork 0
Premonition Overview
Premonition is a library for specifying preload patches with attributes on types/methods in your mod's assembly(s). It has a few pre
To specify a patch, you first need to specify the assembly that is being patched with the [PremonitionAssembly(...)]
attribute. Inside the (...)
is where the assembly name (usually the filename of the dll
file without the .dll
) goes for your patch. This attribute can go either on the class or method that is the patch, and if it is on the class it is inherited for every method that is in said class.
Next, you must specify the type that is being patched with the [PremonitionType(...)]
attribute. Where this time inside the (...)
is the namespace qualified type name, so like SomeClass
in the namespace This.Is
would be specified as This.Is.Some.Class
. This can once again be placed on either the class or method, and has the same inheritance as the [PremonitionAssembly(...)]
attribute.
Then, you must specify the method being patched with the [PremonitionMethod(...)]
attribute. Inside the (...)
is the method name of the method being patched, a lot of the time nameof(...)
can be used here, but there are exceptions to that. There are also other attributes for specifying more specific types of methods, like constructors, getters, and setters, which are explained under the Target Method Attributes
section. Any of these attributes must be on the patch method specifically.
Finally, you must specify the type of patch, with [PremonitionPrefix]
, [PremonitionPostfix]
, or [PremonitionTrampoline]
on the patch method. These types are explained in the Patch Types
section.
Optionally, you can specify which overload of a method you want to patch with the [PremonitionArguments(...)]
attribute. This allows you to specify a list of namespace qualified type names that are the argument types of the method being patched, you may leave some out by using null
.
A full example of a premonition patch
[PremonitionAssembly("Assembly-CSharp")]
[PremonitionType("KSP.Game.CelestialBodyProvider")]
internal static class CelestialBodyProviderPatches
{
[PremonitionMethod("RegisterBodyFromData")]
[PremonitionPrefix]
public static void RegisterBodyFromData(CelestialBodyProvider __instance, CelestialBodyCore jsonData)
{
LogInfo($"[CelestialBodyProvider] Loading {jsonData.data.bodyName}");
}
}
A prefix patch patches the target method to inject a call to your function at the beginning of your method. You can also specify that the prefix patch will stop execution of the original method by returning a bool
value. But if you do so and the method is non-void, you must also add a out T __retVal
parameter where T
is the return type of the target method.
Example of a conditional prefix patch modifying the return value
[PremonitionMethod(nameof(StaticMethods.ReturnsTripleInput))]
[PremonitionPrefix]
public static bool ReturnsTripleInput(int input, out int __retVal)
{
__retVal = 0;
return input >= 3;
}
A postfix patch patches the target method to inject a call before the target method returns. Postfix patches can either return void
or T
where T
is the return type of the target method. Postfix methods can also optionally take the return value in as T __retVal
but this must be the first argument if they do.
Example of a postfix patch that is applied to a void method
[PremonitionMethod(nameof(StaticMethods.StaticVoidMethodThree))]
[PremonitionPostfix]
public static void StaticVoidMethodThree() => StaticMethods.StaticVoidMethodThreeDummyValue *= 2;
Example of a postfix patch that modifies the return value of the target method.
[PremonitionMethod(nameof(StaticMethods.ReturnsDoubleInput))]
[PremonitionPostfix]
public static int ReturnsDoubleInput(int __retVal) => __retVal * 2;
A trampoline patch is a patch that replaces the target methods body with a call to your method. Your method must return the same type as the target method for this to work.
Example of a trampoline patch.
[PremonitionMethod(nameof(StaticMethods.GenericValue))]
[PremonitionTrampoline]
public static T? GenericValue<T>(T from) where T : struct => from;
Matches a method with the name ...
Matches the getter for the property named ...
Matches the setter for the property named ...
Matches a constructor (.ctor
)
Matches a destructor (.dtor
)
Matches the getter for a []
property.
Matches the setter for a []
property.
Premonition when analyzing patch methods can recognize arguments with special names. And have them filled in with special properties
When patching instance methods __instance
is used to get this
In postfix patches, or conditional prefix patches __retVal
is the return value of the method
In the case that an argument is none of the above, then instead it is filled in with the argument of the same name from the target method