[LIB] Harmony v1.2.0.1

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

Previous topic - Next topic

Brrainz

Quote from: Robloxsina66 on March 20, 2017, 07:59:43 AM
i forgot how do you install harmony on rimworld?
To avoid a situation where mods are dependent on another mod and cannot be released because that mod is not updated quickly enough, Harmony does not work like that. Instead it is a simple library that each developer bundles with their mod. Harmony itself is not RimWorld specific and needs not to stay in sync with RimWorld changes.

Brrainz

Quote from: kanid99 on March 18, 2017, 12:31:52 PM
I could be wrong - but it appears that with your recent update, mods that are a mix of those who have and have not taken the update breaks drawing in the game. Im getting a ton of errors like this after Call of Cthulu updated with Harmony 1.0.9 and clearly other mods in my list use Harmony
Harmony 1.0.9 broke the original promise of being compatible across different mods and Harmony versions. It was a bug that was necessary to fix. But as of 1.0.9 the risk that this happens again is very low.

This means that we need to keep nagging everyone to update to 1.0.9 to archive stability. This is unfortunate but a side effect of introducing a complicated tool like Harmony. I still think that it serves more value than this single incident costs and so far 1.0.9 works flawless - even across different platforms.

My apologies

Robloxsina66

Quote from: pardeike on March 20, 2017, 11:31:39 AM
Quote from: Robloxsina66 on March 20, 2017, 07:59:43 AM
i forgot how do you install harmony on rimworld?
To avoid a situation where mods are dependent on another mod and cannot be released because that mod is not updated quickly enough, Harmony does not work like that. Instead it is a simple library that each developer bundles with their mod. Harmony itself is not RimWorld specific and needs not to stay in sync with RimWorld changes.


ah you gotta put the Harmony stuff in the mod then to work?


Brrainz



oreganor

Hi, pardeike, first thanks for creating Harmony, my small project couldn't have even started without something like Harmony around to be sure that in the future it will "gracefully collide" against other mods.

If you had the time could you check why I'm getting this totally unexpected error?:

Exception in Tick (pawn=Irgo, job=AttackMelee A=Thing_Mamuffalo337661, CurToil=2): System.Reflection.TargetParameterCountException: parameters do not match signature
at System.Reflection.MonoMethod.Invoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) <0x001c2>
at System.Reflection.MethodBase.Invoke (object,object[]) <0x00025>
at Harmony.Traverse.GetValue () <0x00100>
at Harmony.Traverse.Resolve () <0x00024>
at Harmony.Traverse.Field (string) <0x0001b>
at MeleeParry.MeleeParryPatch.Prefix (RimWorld.Verb_MeleeAttack,bool&) <0x0043f>
at (wrapper dynamic-method) RimWorld.Verb_MeleeAttack.TryCastShot_Patch1 (object) <0x0002b>
at Verse.Verb.TryCastNextBurstShot () <0x0001a>
at Verse.Verb.WarmupComplete () <0x00028>
at Verse.Verb.TryStartCastOn (Verse.LocalTargetInfo,bool,bool) <0x0050c>
at RimWorld.Pawn_MeleeVerbs.TryMeleeAttack (Verse.Thing,Verse.Verb,bool) <0x001dc>
at Verse.AI.JobDriver_AttackMelee/<MakeNewToils>c__Iterator194.<>m__664 () <0x00080>
at Verse.AI.Toils_Combat/<FollowAndMeleeAttack>c__AnonStorey44E.<>m__6B6 () <0x0022a>
at Verse.AI.JobDriver.DriverTick () <0x00322>

Verse.Log:Error(String)
Verse.AI.JobDriver:DriverTick()
Verse.AI.Pawn_JobTracker:JobTrackerTick()
Verse.Pawn:Tick()
Verse.TickList:Tick()
Verse.TickManager:DoSingleTick()
Verse.TickManager:TickManagerUpdate()
Verse.Game:Update()
Verse.Root_Play:Update()


I traced the error to the following line. I added context related to the operation but the commented line is the one causing the error (The rest of Traverse operations work flawlessly):



    [HarmonyPatch(typeof(Verb_MeleeAttack))]
    [HarmonyPatch("TryCastShot")]
    public static class MeleeParryPatch
    {

        public static bool Prefix(Verb_MeleeAttack __instance, ref bool __result)
        {
            var verbMA = Traverse.Create(__instance);
            LocalTargetInfo target = verbMA.Field("currentTarget").GetValue<LocalTargetInfo>();
            Thing thing = target.Thing;

            // The following is the line triggering the error
            bool surprise = verbMA.Field("surpriseAttack").GetValue<bool>();




Meanwhile, on a previous test, that Prefixed other method OF THE SAME CLASS, using the same ways to access THE SAME FIELD nothing failed and everything was working according to design in-game. Here is an extract of the patch for the other.



    [HarmonyPatch(typeof(Verb_MeleeAttack))]
    [HarmonyPatch("GetHitChance")]
    public static class MeleeParryPatch
    {
        public static bool Prefix(Verb_MeleeAttack __instance, ref float __result, LocalTargetInfo target)
        {
            var verbMA = Traverse.Create(__instance);
            __result = verbMA.Field("DefaultHitChance").GetValue<float>(); // Default chances if anything else fails to evaluate
            if (verbMA.Field("surpriseAttack").GetValue<bool>() || verbMA.Method("IsTargetImmobile", target).GetValue<bool>()){



For reference this are the relevant class definitions, according to ILSpy:


public class Verb_MeleeAttack : Verb
{
                private const float DefaultHitChance = 0.6f;

protected override bool TryCastShot()
{
                         (...)
}

private float GetHitChance(LocalTargetInfo target)
{
                       (...)
}


And the relevant fields on the definition of the base class Verb:


public abstract class Verb : ILoadReferenceable, IExposable
{
protected LocalTargetInfo currentTarget = null;

protected bool surpriseAttack;



Both sniplets of code aren't compiled together (I reused the same class name but disabled ALL THE CODE and cleaned the ENTIRE project of any remaining data)... I'm at the "learning what can be done" phase and all this are temporal tests on how the final solution can be really implemented (I wanted to also take the chance to practice with your Library).

I'm puzzled because ONLY the access to the "surpriseAttack" field is failing while the rest of Traverse operations work without issues... The only remarkable difference I find is the type of methods I'm patching and that the problematic one is an override and also lacks arguments, compared to the "good one" were the same traverse operations work without a hitch.

Any ideas? Extra tests I can try?

EDIT: Silly me, forgot the most important info, this is using Harmony 1.0.9 on VS 2017.

EDIT2: Replacing all Traverse calls by direct Reflections using GetField & GetMethod, made everything working back again... So there is something I must be doing wrong while using your Traverse tool.
Contributions:
Melee Skill Rebalance

Brrainz


oreganor

Thanks for taking the time to look into it. I will publish my mod before the weekend (Need feedback) but will hold on the Sources until I can offer a full integration with the Traverse tool, ATM my code is a mess :).
Contributions:
Melee Skill Rebalance

Brrainz

Quote from: oreganor on March 23, 2017, 04:17:41 AM
Thanks for taking the time to look into it. I will publish my mod before the weekend (Need feedback) but will hold on the Sources until I can offer a full integration with the Traverse tool, ATM my code is a mess :).
could you isolate the problem into a separate project or file - that would help me tremendously

oreganor

Sure... This evening when I reach home I will create the test project that triggers the error, pack it and send you a google drive link on a PM.
Contributions:
Melee Skill Rebalance

Brrainz

Quote from: oreganor on March 23, 2017, 05:42:26 AM
Sure... This evening when I reach home I will create the test project that triggers the error, pack it and send you a google drive link on a PM.
Awesome

Brrainz

Quote from: oreganor on March 23, 2017, 05:42:26 AM
Sure... This evening when I reach home I will create the test project that triggers the error, pack it and send you a google drive link on a PM.
Thank you. I have successfully tested with your test project and found two bugs in Harmony that otherwise would have not been spotted. I have updated the github master with the fix: https://github.com/pardeike/Harmony/releases/tag/v1.0.9.1

One change to your code though:

var DamageInfosToApply = Traverse.Create(vMA).Method("DamageInfosToApply", new Type[] { typeof(LocalTargetInfo) });
foreach (DamageInfo current in (DamageInfosToApply.GetValue<IEnumerable<DamageInfo>>(target))) { }

Method("DamageInfosToApply") returns null because Method() needs you to specify the method exactly. That method has one parameter of type LocalTargetInfo, which I added in the code above. Also, I move the traverse instance out of the loop code just for good measures.

Please let me know if helps you.
Andreas

oreganor

Woah! All Traverse calls working flawlessly.

Thanks a lot, specially for helping at the correct syntax for those calls to private methods. Glad I could help you at squashing some bugs.

Time for a cleanup on my mod code to publish also some decent sources.
Contributions:
Melee Skill Rebalance

Brrainz


Greep

Hey, sorry for the noobishness, working on something pretty simple and running into issues.  This is my attempt to change the formula for skill levels.  "Test Boot" is logged at startup, but never in game "Test Patch".  I also previously tried a prefix version with no results.

namespace GeneralBalanceAdvanced
{
    [StaticConstructorOnStartup]
    class HarmonyBalanceBooter
    {
        static HarmonyBalanceBooter()
        {
            var harmony = HarmonyInstance.Create("com.greep.balance.rimworld.mod.release");
            harmony.PatchAll(Assembly.GetExecutingAssembly());
            Log.Warning("Test Boot");
        }
    }
}

namespace GeneralBalanceAdvanced
{
    [HarmonyPatch(typeof(SkillRecord))]
    [HarmonyPatch("XpRequiredToLevelUpFrom")]
   class SkillCostPatcher
    {
        public static void Postfix(SkillRecord __instance, ref float __result, ref int startingLevel)
        {
            Log.Warning("Test Patch");
            int val = startingLevel;
            __result = val * val * 30 + val * 110 + 350;
        }
    }
}

I could just be misunderstanding rimworlds code there, because this logs and does work as expected e.g.

    [HarmonyPatch(typeof(SkillRecord))]
    [HarmonyPatch("Interval")]
    public static class SkillIntervalPatcher
    {
        public static void Prefix(SkillRecord __instance)
        {
            __instance.Learn(-1f);
            Log.Warning("Test Patch 2");
        }
    }
1.0 Mods: Raid size limiter:
https://ludeon.com/forums/index.php?topic=42721.0

MineTortoise:
https://ludeon.com/forums/index.php?topic=42792.0
HELLO!

(WIPish)Strategy Mode: The experienced player's "vanilla"
https://ludeon.com/forums/index.php?topic=43044.0