expired6978 / F4SEPlugins

26 stars 13 forks source link

Papyrus SetMorph() + UpdateMorphs() not working on base game NPCs #3

Open EgoBallistic opened 3 years ago

EgoBallistic commented 3 years ago

Calling SetMorph() followed by UpdateMorphs() from Papyrus on an actor whose ActorBase is defined in Fallout4.esm almost always fails. The same script called on NPCs defined in mod .esps works every time. The issue appears to be a race condition of some sort.

I was troubleshooting an issue with AAF where morphs appeared to only work intermittently. I eventually discovered that the bug only affects base game NPCs. I created a standalone test script to ensure this wasn't caused by a bug in AAF, and confirmed that it is easily reproducible just by wandering around Diamond City and calling SetMorph/UpdateMorphs on residents and security guards. I tried different combinations of morphs on male and female actors, all with the same results.

I then tried various settings in F4EE.ini to see if I could work around it. Setting uMaxCache to 0 prevented the problem from occurring, although it's horrible for performance. After a lot of slogging through the code, I realized the reason this works is that it causes the thread to wait on I/O as each .tri file is loaded.

I was able to build F4EE, and put a delay in InstallArmorAddon():

void InstallArmorAddon(TESForm * form, NiAVObject * object, UInt32 slotIndex)
{
    Actor * actor = DYNAMIC_CAST(form, TESForm, Actor);
    if(actor) {
        if (g_bEnableBodyMorphs)
        {
            std::this_thread::sleep_for(std::chrono::microseconds(1));
            g_bodyMorphInterface.ApplyMorphsToShapes(actor, object);
        }

This cures the problem and SetMorph() works 100% of the time now. Making the thread sleep will work as long as it occurs somewhere between start of InstallArmorAddon and the call to g_renderManager CreateBSGeometryData in BodyMorphInterface::ApplyMorphsToShape.

I am guessing here that something else is going on in the body update process, and making the InstallArmorAddon thread give up the CPU allows whatever that is to continue so that CreateBSGeometryData works properly. It's even stranger that this only applies to NPCs from Fallout4.esm, but there it is. This may also apply to the DLC NPCs, I have not tested that.