Jump to content

[LE] How to avoid papyrus stack dumps when moving lots of items in/out of inventory quickly


Recommended Posts

Hello,

 

Unfortunately it seems a project of mine is going downhill into the situation, where it will have to do that: Move lots of unique ObjectReferences out of, then into the player inventory in a quick succession. As far as I know, this is a golden recipe for triggering possible papyrus stack dumps if there are lots of other scripts present in the game without the necessary inventory filters.

 

So the scenario is that the ObjectReferences are sitting in an array on a script, and they have to be moved out of the inventory, (the script the does some stuff to them, namely puts them into aliases), and then they must be moved back into the player inventory, silently.

 

The theoretical limit of the objects is 128 (length limit of the array). I don't expect to have this many ObjectReferences in an actual game, but theoretically it's possible that the script will have to move up to 128 ObjectReferences out of, then into the player inventory. Successively, quickly.

 

 

How is it possible to do this safely? Are there some sort of precaution techniques that can do this in a safe way, preventing a possible stack dump? (I mean on THIS end of the code, that's doing the moving in and out thing. NOT on the end of the other scripts that failed to use inventory filtering to reduce their load.)

 

Thanks

Link to comment
Share on other sites

My Inventory Management System mod (SSE version) does a lot of mass transfer back and forth. The way I avoided as many stack dumps as possible was two fold. The first thing I did was utilize SKSE's added Mod Events feature. The script triggering the transfer would first send a mod event to the aliases running the OnItemAdded and OnItemRemoved events. Those scripts would receive the mod event and remove all inventory filters and add back a filter for a certain single item. When the transferring was complete, a separate mod event would be sent which would then add back the inventory filters for the lists of items. The second thing was designating a count limit of 31. If a list contained 31 or less items, the list would be transferred in one go. If the list was greater, I'd compare the quantity of items in the container being transferred from against the index size of the current list. Whichever was smaller, I'd cycle through one at a time and transfer if said item matched the criteria.

 

Since you are doing something to each object, if done sequentially rather than all at once it will spread the processing out and help eliminate the stack dumps. Downside is that it may take longer than desired.

Edited by IsharaMeradin
Link to comment
Share on other sites

Thanks very much :smile:

 

I'm afraid I do things a bit differently. My mod doesn't contain any OnItemAdded and OnItemRemoved events by the way, so there's nothing I can do about those - I'm just worried what I might trigger by doing by stuff if someone does have such events without filters in their game.

 

Doing the moving with a limit (why 31 though? Just curious. I get it it's a prime, but any other reason?) seems like a reasonable idea. However, the doing something to the objects is done by the script that's also doing the move in/out.

Let me clarify:

 

Each ObjectReference has a script attached, that has this property:

ObjectReference Property OwningContainer  Auto

The script uses states and OnContainerChanged events to store what container the reference is currently in (NONE if dropped in the world).

 

The move in/out of owning container happens as a part of saved game reload maintenance. Alias renamings of these objects are lost on a save reload because their underlying Message title that was renamed with SKSE's SetName() is lost. Which means the items all return to the Message's original CK name - and thus the items in inventories get stacked since they get the same name (and are also the same base objects).

Unfortunately, doing just the renaming-back maintenance does not unstack the items. (Instead the last item's name is shown as the stack name). They need to be moved out of inventory, and back in so that they are again shown as separate items and not as a stack.

 

So the maintenance script does something like this:

; objects that need maintenance are sitting in an array like this:
ObjectReference[] SentRefs
int sentRefsLen    ; their current number, guaranteed to be less than 128


function RenamingPartOfTheMaintenance()
    ObjectReference OwningContainers = new ObjectReference[128]
    int i = 0
    while(i < sentRefslen) )   ; iterate over all currently in-game items
        ItemScriptName itemscript = SentRefs[i] as ItemScriptName
        OwningContainers[i] = itemscript.OwningContainer            ; get owning container from their script

        ; if this is not NONE, the item is in some inventory, and needs to be moved out then back in to avoid stacking there
        if(OwningContainers[i])
            SentRefs[i].Moveto( someXMarkerInAJunkyardWorldspace)
        endif
 
        ; clear renaming alias here
 
        ; update underlying Message name here
 
        i += 1
    endwhile
 
    i = 0
    while(i < sentRefsLen)
 
        ; force ref back into renamer alias to update name here
 
        if(OwningContainers[i])
            ; it was moved out previously, now to put it back in (silently)
            OwningContainers[i].AddItem( SentRefs[i], 1, true) 
        endif
    endwhile
endfunction

(A note: I'm not renaming my items with SetDisplayItem() because they are books and that breaks their text replacement. See this thread and this thread. I'm afraid this is the only way to reliably rename successively spawned book references with text replacement.)

 

 

Most of the ObjectReferences are likely in the player inventory. So would it help if the loops only went until something like 31, and then went to sleep for a few seconds by registering for a single update, to allow other stalled papyrus stacks to get out of there, and then continuing there?

Link to comment
Share on other sites

Why 31? In the unmodified game, that is the total number of alchemy ingredients and the smallest list. And whenever there were less than that in a container and take all was used (even without deactivating my mod's event filters) there were no stack dumps appearing. Seemed to be a logical cut off point before having to take a slower one by one approach. Unfortunately, it seems like my approaches will not work with your specific scenario.

 

You may be fine as it seems that your maintenance function already handles one item at a time. There is a difference between cycling an array / list and transferring all at once. It is the latter that contributes to the stack dumps, the former takes up more time.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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