Jump to content

[LE] I have a script that works fine most of the time. On the 4th use, the script stops cold. (source gist included)


monsto

Recommended Posts

Here's the entire script source.
I posted this thing yesterday, thinking I could workaround this problem by just deleting the pedestal in a different manner... but since this script completely stops, it seems there's a larger problem that truly needs to be addressed.
This script is attached to a working in-game mannequin replacement.
  • Activate it to get a UIExtensions Listmenu
  • Menu choices are: Show inventory, Change mannequin (body type presets), toggle statue/moving, toggle pedestal tall/short, KILL (remove entirely).
Selecting "Change mannequin"
  • gives a UIExtensions Listmenu of preset names
  • disables original
  • places the new
  • transfers the inventory from orig to new
  • deletes original.
All mannequins have a pedestal. Above, "delete original" means I delete the pedestal, then delete self, then return.
The offending moment comes on line 209, deleting the pedestal before deleting self.
I did the registerforupdate to see if the update would run after the lockup. While "change 8" is never output to the console, "5sec update" never comes either... it seems that the script has completely just stopped running for whatever reason.
"Sometimes", the output stops at "change 7" but I can continue to activate/use the new mannequin. When I do this, "5sec update" plays, and the old pedestal stays there, dead.
"Sometimes", after the stop, I can spawn in new mannequins as if nothing is different,
"Sometimes", I can't activeate anything. Other mannequins, and a chest is all i have in the test cell, but neither of them are usable.
Secondarily, I have a completely separate script that I made that has a bunch of helper functions... things like press a key to toggle fly/clip mode, move selected references by a relative distance with a simple console command, etc... "sometimes" that helper script is completely gone after this mannequin script stops. As if it never ran in the first place.
notify and conmsg are defined #262. Tehy're basically just typesaver functions for output to console or debug.
This is pretty f*#@ing frustrating as the entire thing works like a charm and is about half the size of the original SPODUM. This one error makes it problematic, leaving dead pedestals everywhere, not to mention the script just outright dying.
I'm also open to some kind of workaround that avoids deleting the pedestal, or even doing some other method for mannequin change.

 

Link to comment
Share on other sites

  On 7/21/2019 at 1:08 AM, Reneer said:

There is a major lack of sanity checks in your code. In particular, you never check to see if "BUMPed" is actually filled in a lot of places. What is showing up in your Papyrus logs?

 

At the end, there's absolutely nothing relevant. Lemme generate a new one and make a gist.

 

BUMPed always works at the point of generation. It's property is an ingame miscitem (I should prob make it a static) and it 100% shows.

Link to comment
Share on other sites

  On 7/21/2019 at 1:08 AM, Reneer said:

There is a major lack of sanity checks in your code. In particular, you never check to see if "BUMPed" is actually filled in a lot of places. What is showing up in your Papyrus logs?

 

Here's the papyrus log.

 

It had previously been spammed by unrelated, unused scripts. I cleared them out just now and whittled the log from 50k lines to it's now svelte 2k lines, and I'm wondering if the entry at #1849 is relevant. It's 2 sec before the end of the file, and probably right on time as to when the stop at "change 7" occurs.

 

[edit] oh wait, no it's irrelevant. There's a bare delete in setpedestal(). onInit of a new mannequin, that delete runs before putting down a new pedestal. So that error occurs after the lockup.

Edited by monsto
Link to comment
Share on other sites

  On 7/21/2019 at 1:11 AM, monsto said:

At the end, there's absolutely nothing relevant. Lemme generate a new one and make a gist.

 

BUMPed always works at the point of generation. It's property is an ingame miscitem (I should prob make it a static) and it 100% shows.

You should probably call Disable() before you call Delete() on the Objectreference as per the notes in the Delete wiki page. Or maybe you can try switching to DeleteWhenAble to see if that helps.

Link to comment
Share on other sites

Your script is based on "aaSLuckMannHuman.psc" that was compiled in year 2012.

sLuckyD has made some mistakes caused by not knowing the weaknesses of Skyrim papyrus implementation.

 

1) Do not overwhelming the OnInit() event. Follow code snippet as sample:

 

  Reveal hidden contents

 

 

2) DO NEVER USE construct as follow in papyrus !!!

    WHILE !self.Is3DLoaded()
        Utility.wait(0.5)
    ENDWHILE

3) Whenever a script has placed an object or actor to Skyrim world you have to use something like this:

    BUMPed.Disable()
    BUMPed.Delete()
    BUMPed = None

to remove such kind of reference. Keep in mind the native function "Delete()" marks the reference only for delete, it never made them deleted.

 

4) Whenever you load a game the following vanilla script "PF_MannequinStay_000D7510" will be running, if not using a mod that changes the original package.

 

  Reveal hidden contents

 

 

adjusted script "aaBumHumanScript" a bit rewritten, not yet compiled or tested

  Reveal hidden contents

 

Edited by ReDragon2013
Link to comment
Share on other sites

Excellent notes and info.

 

It's annoying that standards that you mention (points 1, 2, 3) are only found by the handing down from one generation to the next. I've been piddling with papyrus for I dunno 3-4 years, and have never seen anything like this. It truly belongs on a ckwiki page, something like "Papyrus conventions for cleaner scripting".

 

For point 2 on your list, what's an alternative to that?

 

I'm also a fan of some of the 'programming habits' you've displayed. Comment lines for breaking up the menu elseifs (Elesif? Holy s***...), the comment "skse required", things like that.

 

I left the boilerplate decompiler heading as a credit to the original author and idea In the script. I think the only thing remaining from the original is a handful of lines, like maybe 20.

 

I see exactly what and why about overwhelming init(). I'm assuming that by using an update, it allows the task to be threaded and therefore reducing hitches and framerate drops. I may take that piece of advice when it's time to streamline, but it's working right now and I'm reluctant to mess with it.

 

I have no idea of what is the significance of, or when or how to use, fragment scripts. Is there a primer somewhere?

 

All in all I appreciate your very good papyrus response.

Edited by monsto
Link to comment
Share on other sites

  On 7/21/2019 at 9:04 AM, ReDragon2013 said:

2) DO NEVER USE construct as follow in papyrus !!!

    WHILE !self.Is3DLoaded()
        Utility.wait(0.5)
    ENDWHILE

 

The only problem with the above code is that the object might be disabled, its cell might be unloaded, etc (and thus the loop will keep on looping until the object's 3D is actually loaded). What would fix / mitigate this issue would be to have code like this:

float futuretime = Utility.GetCurrentRealTime() + 5.0
While (Self.Is3DLoaded() == false && Utility.GetCurrentRealTime() < futuretime)
; do nothing here, just loop
endWhile
if (Utility.GetCurrentRealTime() > futuretime && Self.Is3DLoaded() == false)
; bad, time to abort / retry.
endif
Link to comment
Share on other sites

  On 7/21/2019 at 8:49 PM, Reneer said:

 

  On 7/21/2019 at 9:04 AM, ReDragon2013 said:

2) DO NEVER USE construct as follow in papyrus !!!

    WHILE !self.Is3DLoaded()
        Utility.wait(0.5)
    ENDWHILE

 

The only problem with the above code is that the object might be disabled, its cell might be unloaded, etc (and thus the loop will keep on looping until the object's 3D is actually loaded). What would fix / mitigate this issue would be to have code like this:

float futuretime = Utility.GetCurrentRealTime() + 5.0
While (Self.Is3DLoaded() == false && Utility.GetCurrentRealTime() < futuretime)
; do nothing here, just loop
endWhile
if (Utility.GetCurrentRealTime() > futuretime && Self.Is3DLoaded() == false)
; bad, time to abort / retry.
endif

 

Alright so basically you're saying that it's risky, which I get.

 

Good thoughts. Thank you.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...