Centrifuge icon indicating copy to clipboard operation
Centrifuge copied to clipboard

Silent catch-all in RuntimePatcher.AutoPatch

Open trigger-segfault opened this issue 2 years ago • 0 comments

Describe the Issue

Use of the function RuntimePatcher.AutoPatch() is a debugging nightmare for anyone developing mods without realizing what's inside. The silent catch-all (shown below) without logging makes it impossible to track where patch errors are originating from.

https://github.com/Centrifuge-Modding-Framework/Centrifuge/blob/2485670337515b69216e2dd2828ee0fc5b3bd85b/Reactor.API/Runtime/Patching/RuntimePatcher.cs#L43-L53

Possible Solutions

Add Logging

The catch block should at least be changed to below to match the RuntimePatcher.RunTranspilers() function. This way failures can be logged and viewed.

catch (Exception e)
{
    Log.Exception(e);
}

Add Throwing

But ideally, it should be possible to call the RuntimePatcher functions and allow exceptions to be thrown and handled by the caller.

This could be achieved by overloading the functions with the argument bool throwErrors, where the default behavior of false would ensure backwards compatibility with mods that have faulty patches, but ultimately still function as expected.

There are two ways to solve this:

  1. One way would be to duplicate both function, the new one with extra handling.
  2. The other would be to create a private sub-function where we pass the assembly, since we still need to be able to use Assembly.GetCallingAssembly().
  3. The solution can't be to overload the only function as AutoPatch(bool throwErrors = false), because that would break binary compatibility with mods using a newer dll version.

Duplicate both functions

Code example
public static void AutoPatch()
{
    try
    {
        HarmonyInstance.PatchAll(Assembly.GetCallingAssembly());
    }
    catch (Exception e)
    {
        Log.Exception(e);
    }
}

public static void AutoPatch(bool throwErrors)
{
    try
    {
        HarmonyInstance.PatchAll(Assembly.GetCallingAssembly());
    }
    catch (Exception e)
    {
        if (throwErrors)
        {
            throw;
        }
        else
        {
            Log.Exception(e);
        }
    }
}

Pass calling assembly to private function

Code example
private static void AutoPatch(Assembly asm, bool throwErrors)
{
    try
    {
        HarmonyInstance.PatchAll(asm);
    }
    catch (Exception e)
    {
        if (throwErrors)
        {
            throw;
        }
        else
        {
            Log.Exception(e);
        }
    }
}

public static void AutoPatch()
{
    AutoPatch(Assembly.GetCallingAssembly(), false);
}

public static void AutoPatch(bool throwErrors)
{
    AutoPatch(Assembly.GetCallingAssembly(), throwErrors);
}

trigger-segfault avatar Jul 01 '22 18:07 trigger-segfault