[LIB] Harmony v1.2.0.1

Started by Brrainz, January 13, 2017, 04:59:21 PM

Previous topic - Next topic

jecrell

I've started actively using this in my mods in internal builds.
Magnificent job.
...Psst. Still there? If you'd like to support
me and my works, do check out my Patreon.
Someday, I could work for RimWorld full time!

https://www.patreon.com/jecrell

jecrell

Any clue how to use Harmony to handle "out" variables?

Let's say I want to modify....

Verse.Pawn_HealthTracker
public void PreApplyDamage(DamageInfo dinfo, out bool absorbed)

...
with a Prefix.
My goal is to have it return the absorbed as "true" before the main method and prevent the main method from occurring.

How could I do that?
Thanks!
...Psst. Still there? If you'd like to support
me and my works, do check out my Patreon.
Someday, I could work for RimWorld full time!

https://www.patreon.com/jecrell

Brrainz

Quote from: jecrell on February 10, 2017, 12:37:14 PM
Any clue how to use Harmony to handle "out" variables?

Let's say I want to modify....

Verse.Pawn_HealthTracker
public void PreApplyDamage(DamageInfo dinfo, out bool absorbed)

...
with a Prefix.
My goal is to have it return the absorbed as "true" before the main method and prevent the main method from occurring.

How could I do that?
Thanks!
Here is how you do it: the prefix can return a boolean that controls if the original is run. So define your prefix like this:


static bool Prefix(ref bool absorbed)
{
    absorbed = true;
    return false;
}

jecrell

Sadly, the result is an error.


Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for CompDeflector.HarmonyCompDeflector ---> System.Exception: Parameter "absorbed" not found in method Void PreApplyDamage(DamageInfo, Boolean ByRef)
  at Harmony.MethodPatcher.EmitCallParameter (System.Reflection.Emit.ILGenerator il, System.Reflection.MethodBase original, System.Reflection.MethodInfo patch, System.Collections.Generic.Dictionary`2 variables, Boolean noOut) [0x00000] in <filename unknown>:0
  at Harmony.MethodPatcher+<>c__DisplayClass7_0.<AddPrefixes>b__0 (System.Reflection.MethodInfo fix) [0x00000] in <filename unknown>:0
  at System.Collections.Generic.List`1[System.Reflection.MethodInfo].ForEach (System.Action`1 action) [0x00000] in <filename unknown>:0
  at Harmony.MethodPatcher.AddPrefixes (System.Reflection.Emit.ILGenerator il, System.Reflection.MethodBase original, System.Collections.Generic.List`1 prefixes, System.Collections.Generic.Dictionary`2 variables, Label label) [0x00000] in <filename unknown>:0
  at Harmony.MethodPatcher.CreatePatchedMethod (System.Reflection.MethodBase original, System.Collections.Generic.List`1 prefixes, System.Collections.Generic.List`1 postfixes, System.Collections.Generic.List`1 processors) [0x00000] in <filename unknown>:0
  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo) [0x00000] in <filename unknown>:0
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0
  at Harmony.HarmonyInstance.Patch (System.Reflection.MethodBase original, Harmony.HarmonyMethod prefix, Harmony.HarmonyMethod postfix, Harmony.HarmonyProcessor infix) [0x00000] in <filename unknown>:0
  at CompDeflector.HarmonyCompDeflector..cctor () [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
  at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0
  at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0
  at Verse.PlayDataLoader.<DoPlayLoad>m__6F8 () [0x00000] in <filename unknown>:0
  at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0


This is what I'm trying to do~~



namespace CompDeflector
{
    [StaticConstructorOnStartup]
    static class HarmonyCompDeflector
    {
        static HarmonyCompDeflector()
        {
            HarmonyInstance harmony = HarmonyInstance.Create("rimworld.jecrell.comps.deflector");

            harmony.Patch(typeof(Pawn_HealthTracker).GetMethod("PreApplyDamage"), new HarmonyMethod(typeof(HarmonyCompDeflector).GetMethod("PreApplyDamagePostFix")), null);

        }



        //=================================== COMPDEFLECTOR

        public static bool PreApplyDamagePostFix(Pawn_HealthTracker __instance, DamageInfo dinfo, ref bool absorbed)
        {
           .......(Code snipped, let me know if you need to see it) absorbed = true;  return false;
        }


I've cut it off there, because I'm not sure you need to see the whole method.

Also, the original method's parameters etc look like....

Verse.Pawn_HealthTracker
public void PreApplyDamage(DamageInfo dinfo, out bool absorbed)
...Psst. Still there? If you'd like to support
me and my works, do check out my Patreon.
Someday, I could work for RimWorld full time!

https://www.patreon.com/jecrell

Brrainz


RawCode

little note:

ref and out are special case of syntax sugar and result in exactly same native code
(ref variable) and (& variable) provide equal results.
ref byte and byte* are also equals on postjit level

only possible difference is using ref on managed types, because using & on managed type is not allowed by default compiler (but perfectly possible with hacks)

Brrainz

Quote from: RawCode on February 13, 2017, 11:36:00 PM
little note:

ref and out are special case of syntax sugar and result in exactly same native code
(ref variable) and (& variable) provide equal results.
ref byte and byte* are also equals on postjit level

only possible difference is using ref on managed types, because using & on managed type is not allowed by default compiler (but perfectly possible with hacks)
Yeah, I saw that when comparing IL code for the different cases. BTW, Zhentars problem was actually a stupid bug related to using the wrong parameter index. Fixed in 1.0.7 which has no release yet but is up on master repo. Harmony now can handle any combination of normal/ref/out parameters. And compiler wise out parameters actually make a differences regarding the need to assign values to them so I had to take care of that too.

darkitten

hello i am curious if this error means that linux mint cinnamon + latest mono ..is not compatable because unlike ubuntu it has some sort of unity bug? should i chase that or is this harmony code issue? (Filename: /home/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)

Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/mono.dll
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/libmono.dll.so
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/./mono.dll
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/./libmono.dll.so
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/mono.dll
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/mono
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/libmono.so
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/./mono
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/./libmono.so
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/mono
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/libmono.dll
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/libmono.dll.so
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/./libmono.dll
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/./libmono.dll.so
Fallback handler could not load library /home/leaf/.local/share/Steam/steamapps/common/RimWorld/RimWorldLinux_Data/Mono/x86_64/libmono.dll
Exception from long event: System.EntryPointNotFoundException: mono_domain_get
  at (wrapper managed-to-native) Harmony.ILCopying.MonoInternals:mono_domain_get_other ()
  at Harmony.ILCopying.MonoInternals.mono_domain_get () [0x00000] in <filename unknown>:0
  at Harmony.ILCopying.MonoInternals.GetCodeInfo (System.Reflection.MethodBase method, System.Int64& start) [0x00000] in <filename unknown>:0
  at Harmony.ILCopying.Memory.GetMethodStart (System.Reflection.MethodBase method) [0x00000] in <filename unknown>:0
  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo) [0x00000] in <filename unknown>:0
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0
  at Harmony.HarmonyInstance.Patch (System.Reflection.MethodBase original, Harmony.HarmonyMethod prefix, Harmony.HarmonyMethod postfix, Harmony.HarmonyProcessor infix) [0x00000] in <filename unknown>:0
  at PrisonerRansom.ReplacementCode+<>c.<.cctor>b__0_0 () [0x00000] in <filename unknown>:0
  at Verse.LongEventHandler.UpdateCurrentSynchronousEvent (System.Boolean& sceneChanged) [0x00000] in <filename unknown>:0

sorry and thank you

Brrainz

If you figure out how to DLLImport the mono lib on your setup, I can fix that in the code. This line is causing it:


[DllImport("__Internal", EntryPoint = "mono_domain_get")]


No idea what it should be for your Linux.

darkitten

http://www.mono-project.com/docs/advanced/pinvoke/dllnotfoundexception/

^^ this is it an i think you probably know all this so anyway ill find the dll file an tell you what i can

RawCode

libmono.so is proper name for linux.

and mac too, but mac have somewhat strange rules on library loading.

Brrainz

Quote from: RawCode on February 21, 2017, 05:00:20 AM
libmono.so is proper name for linux.

and mac too, but mac have somewhat strange rules on library loading.
There are quite a few cases where "libmono.so" as a name does not work. So the real problem is how to choose between the different names. Also, since Harmony is not RimWorld specific, names that contain specific paths are not an option.

RawCode

using System;
using System.Runtime.InteropServices;

namespace rcrml
{
public unsafe class _Native
{
static public PLATFORM CURRENT_PLATFORM;
public enum PLATFORM
{
EXT,W32,M64,L64
}


//mono_domain_get

[DllImport("__Internal",
EntryPoint="mono_domain_get")]
extern static private void* mono_domain_get__EXT ();
[DllImport("mono.dll",
EntryPoint="mono_domain_get")]
extern static private void* mono_domain_get__W32 ();
[DllImport("libmono.so",
EntryPoint="mono_domain_get")]
extern static private void* mono_domain_get__M64 ();
[DllImport("libmono.so",
EntryPoint="mono_domain_get")]
extern static private void* mono_domain_get__L64 ();

//mono_jit_info_table_find

//void* equal to "native integer", it follows word size based on platform used automatically
public struct _MonoJitInfo
{
//should work on x64 due to variable size of void*
public RuntimeMethodHandle method; //should work properly because IntPtr host void* intenrnally
public void* next_jit_code_hash;
public IntPtr code_start; //same void* under wrappers
public uint  unwind_info;
public int    code_size;
public void* __rest_is_omitted;
//struct is longer actually, but rest of fields does not matter
}

[DllImport("__Internal",
EntryPoint="mono_jit_info_table_find")]
extern static private _MonoJitInfo* mono_jit_info_table_find__EXT (void* domain,void* function);
[DllImport("mono.dll",
EntryPoint="mono_jit_info_table_find")]
extern static private _MonoJitInfo* mono_jit_info_table_find__W32 (void* domain,void* function);
[DllImport("libmono.so",
EntryPoint="mono_jit_info_table_find")]
extern static private _MonoJitInfo* mono_jit_info_table_find__M64 (void* domain,void* function);
[DllImport("libmono.so",
EntryPoint="mono_jit_info_table_find")]
extern static private _MonoJitInfo* mono_jit_info_table_find__L64 (void* domain,void* function);

//mono_code_manager_new

[DllImport("__Internal",
EntryPoint="mono_code_manager_new")]
extern static private void* mono_code_manager_new__EXT ();
[DllImport("mono.dll",
EntryPoint="mono_code_manager_new")]
extern static private void* mono_code_manager_new__W32 ();
[DllImport("libmono.so",
EntryPoint="mono_code_manager_new")]
extern static private void* mono_code_manager_new__M64 ();
[DllImport("libmono.so",
EntryPoint="mono_code_manager_new")]
extern static private void* mono_code_manager_new__L64 ();

//mono_code_manager_reserve

[DllImport("__Internal",
EntryPoint="mono_code_manager_reserve")]
extern static private void* mono_code_manager_reserve__EXT (void* MonoCodeManager, int size);
[DllImport("mono.dll",
EntryPoint="mono_code_manager_reserve")]
extern static private void* mono_code_manager_reserve__W32 (void* MonoCodeManager, int size);
[DllImport("libmono.so",
EntryPoint="mono_code_manager_reserve")]
extern static private void* mono_code_manager_reserve__M64 (void* MonoCodeManager, int size);
[DllImport("libmono.so",
EntryPoint="mono_code_manager_reserve")]
extern static private void* mono_code_manager_reserve__L64 (void* MonoCodeManager, int size);


static public void* mono_code_manager_new (void* MonoCodeManager, int size)
{
if (CURRENT_PLATFORM == PLATFORM.EXT)
return mono_code_manager_reserve__EXT(MonoCodeManager, size);

return mono_code_manager_reserve__W32(MonoCodeManager, size);
}

static public void* mono_code_manager_new ()
{
if (CURRENT_PLATFORM == PLATFORM.EXT)
return mono_code_manager_new__EXT();

return mono_code_manager_new__W32();
}

static public void* mono_domain_get ()
{
if (CURRENT_PLATFORM == PLATFORM.EXT)
return mono_domain_get__EXT();

return mono_domain_get__W32();
}

static public _MonoJitInfo* mono_jit_info_table_find (void* domain,void* function)
{
if (CURRENT_PLATFORM == PLATFORM.EXT)
return mono_jit_info_table_find__EXT(domain,function);

return mono_jit_info_table_find__W32(domain,function);
}

static _Native()
{
string s = Environment.CommandLine;
//last char of command line string
s = s.Substring (s.Length - 1);

switch (s)
{
case "e": //exE
CURRENT_PLATFORM = PLATFORM.W32;
return;
case "l": //dlL
CURRENT_PLATFORM = PLATFORM.EXT;
return;

//C for mac
//4 for linux
}
}
}
}

Brrainz


Brrainz

Harmony v1.0.8 released. Lots of bug fixes and now supports constructor patching too: https://github.com/pardeike/Harmony/releases/tag/v1.0.8

/Andreas