[B18] [KV] Mending ChangeDresser Patch [ModSync]

Started by Kiame, November 13, 2017, 10:13:54 PM

Previous topic - Next topic

Kiame

Quote from: ShadowXIII on November 24, 2017, 03:23:52 PM
Today I got following error while right clicking onto the mending bench with a selected colonist.

Method not found: 'ChangeDresser.WorldComp.get_DressersToUse'...

When I have build a mending bench, some jobs won't be done by the colonists. For example my crafter won't cut stones anymore. After deconstruct the bench, the jobs will be executed again.

It looks like you do not have ChangeDresser - https://ludeon.com/forums/index.php?topic=35632.0 - installed.
If you're playing B18, get the B18 verison
If you're playing A17, make sure you're using the newer version "Change Dresser - BETA" is the name in the mod manager in game.

ShadowXIII

Quote from: Kiame on November 24, 2017, 04:02:19 PM
It looks like you do not have ChangeDresser - https://ludeon.com/forums/index.php?topic=35632.0 - installed.
If you're playing B18, get the B18 verison
If you're playing A17, make sure you're using the newer version "Change Dresser - BETA" is the name in the mod manager in game.

I am playing B18 at the moment and I already have installed the B18 Version from Steam Workshop.

This one:

http://steamcommunity.com/sharedfiles/filedetails/?id=1180720623&searchtext=change+dresser


Kiame

Thanks for reporting the issue ShadowXIII

Looks like when B18 came out as the official version a cross-link i was using got broken.

Direct download and steam uploaded with the fix


Nightinggale

I have been wondering about this mod and how it conditionally adds harmony patching. I tried to get ModCheck to load a DLL file conditionally and failed to get it working reliably. Still I don't like patch mods and while conditional xml patching works fine with ModCheck, I'm still on the lookout for conditional harmony patching. I have finally come up with an idea, which I think will do the trick.

protected bool isModLoaded(string name)
{
        return ModsConfig.ActiveModsInLoadOrder.Any(m => m.Name == name);
}

This method is included in ModCheck 1.6 and does precisely what you would think. It checks if a mod is loaded and it compares against the name tag in About.xml. Since it relies on vanilla code only, it should be safe to copy between mods. I imagine it could be used something like this.
static Main()
{
        if (isModLoaded("Mending"))
        {
                var harmony = HarmonyInstance.Create("com.mendingchangedresserpatch.rimworld.mod");
                // rest of the harmony code
        }
}

Now when the DLL is loaded, if Mending is loaded, it will do the same as the existing DLL. If Mending is not loaded, the static constructor will do absolutely nothing. In other words it should be safe to add this to change dresser and it should work even if Mending isn't loaded. You can't merge the DLL files though as it will use all the harmony code in the DLL and as such makes it problematic to conditionally load just some of it. However there is no issues involved in adding multiple DLLs to a single mod. They will be loaded alphabetically.

Now the really big question is if it works. I haven't actually tested it, but based on my experience in this area, I see no reason why it shouldn't work.
ModCheck - boost your patch loading times and include patchmods in your main mod.

Kiame

#20
Thanks I'll try that. If it works I'll do that for Weapon Storage as well.

I wonder if I can get a way to only load certain methods with harmony and just wrap it into one mod. Would be wonderful not to have these patches  ;D

Quick correction for the code
protected bool isModLoaded(string name)
{
        return ModsConfig.ActiveModsInLoadOrder.Any(m => m.Name.Equals(name));
}


Equals instead of ==

Kiame

I think i know how to do it w/o a 'patch mod'. For Change Dresser and Weapon Storage it'd be a huge pain as I'd have to specifiy each method to patch...


// Patch all methods:
var harmony = HarmonyInstance.Create("com.changedresser.rimworld.mod");
harmony.Patch(...);
...
harmony.Patch(...);

if (isModLoaded("Mending"))
{
        // 'Mending' specific patches
        harmony.Patch(...);
}

Nightinggale

Quote from: Kiame on May 31, 2018, 09:10:20 PM
I think i know how to do it w/o a 'patch mod'. For Change Dresser and Weapon Storage it'd be a huge pain as I'd have to specifiy each method to patch...
That's why I proposed one DLL file for patches to always apply and one for conditional patching. That way it is possible to maintain the simple approach to Harmony inside each DLL file and keep it in the "apply all patches from this DLL file" approach.
ModCheck - boost your patch loading times and include patchmods in your main mod.

Kiame

Quote from: Nightinggale on May 31, 2018, 09:16:25 PM
Quote from: Kiame on May 31, 2018, 09:10:20 PM
I think i know how to do it w/o a 'patch mod'. For Change Dresser and Weapon Storage it'd be a huge pain as I'd have to specifiy each method to patch...
That's why I proposed one DLL file for patches to always apply and one for conditional patching. That way it is possible to maintain the simple approach to Harmony inside each DLL file and keep it in the "apply all patches from this DLL file" approach.

/Lightbulb

Interesting... going to try that when I have a minute to try  ;D

Nightinggale

#24
Quote from: Kiame on May 31, 2018, 07:21:02 PMQuick correction for the code
protected bool isModLoaded(string name)
{
        return ModsConfig.ActiveModsInLoadOrder.Any(m => m.Name.Equals(name));
}


Equals instead of ==
I started researching this and it turns out that == vs .Equals is a hot topic online. Some says it's the same, some says it isn't and some says while it generally isn't, it is when working on two strings. Some says .Equals is slower due to added overhead and some don't. In other words the internet fails to provide a proper answer to this question.

I decided to call both in a loop. While the speed is a bit unstable (expected), all both mostly stayed below 33 ms, hit 32.4 ms multiple times, but none of them went below 32.4. If there is a performance difference, it's hidden in the noise and I'm not going to spend ages analyzing the noise to detect such a minor difference. I added a Dictionary<string, int> and called using ContainsKey. Not counting dictionary building, it managed to do the same in around 6.7 ms. However I suspect the building of the Dictionary takes way longer than a single call, meaning it's an approach for frequent calls.

The next question is what the functional difference is. In this case, apparently nothing. The == is part of the stable release since 1.0 (released on Halloween last year), is well tested and nobody have reported any issues.


One interesting observation I made here is that sometimes a test hit 42 ms. Not 44 or 37, but 42 every single time. I suspect this is the lag from moving the process from one CPU core to another. In case you don't know, modern CPUs move 100% loaded threads around frequently to spread the heat production. Say you have a cooling, which can handle 100W. That's 25W/core if they are all loaded. However if only one is loaded, you can use 60*1+5*3 = 75 W. The problem here is that the cooler can't cool 60W from one core, but inserting cooling periods and you get (60+5+5+5)/4 ≈ 19 W on average. More power allows higher clock speed. In my case single core can work 0.5 GHz faster than when using all cores, which still performs better overall even though it suffers from pauses while changing core. It's one of those issues you have to be aware of while benchmarking and one reason why getting reliable readings can be tricky.

This is getting a bit off topic regarding ChangeDresser and more about how to code (including ChangeDresser), but whatever. If the DLL patching idea works, this thread and patch mod will no longer be needed anyway.
ModCheck - boost your patch loading times and include patchmods in your main mod.