Jump to content

[SOLVED] Need help with Papyrus Stages, multithreading and preventing multiples of events slipping into eachother


Recommended Posts

The issue has been solved, if you encountered the same problem, please refer to my second post's "Edit" section for the solution! Cheers!

Hello, everyone, I've been working on procedural generation for my project, which works great up until one point, and that is having multiples of objects with a script attached firing the OnLoad event, slipping into each other, changing variables in the middle of one of them running, resulting in terrifying stuff (vehicle generations slipping into furniture generation events, resulting in vehicles spawned where furniture should have and vice versa). 

I read that all of this has to do with Papyrus' threading, stages and other ways to "lock" a script, and while I've been reading up on it a bunch, I'm still complete and utter 0 in this field.

Below I will attach all the scripts I have, with a description of what I use them for, but my main issue/goal is to set them up in a way that each of the objects runs and finished the event before the next one starts to.

Here's the main "Generator" script I have (which utilizes Data Structures framework):
 

  Reveal hidden contents

 

And here is the SWT:Libraries:Generation:Generate script that is called in the script above:

 

  Reveal hidden contents

 

I'm also attaching both of these as .psc files for anyone wanting quick access to them. Once I figure out a way to overcome this obstacle with some way to "lock" the script for each seperate object, everything else is set and done to begin work on much bigger things. This is the only thing holding my project back at the moment, so I'm thankfull for any help in advance!

 

As a little preview that will hopefully peak more interest to this post, here's a GIF of the much advanced generation in progress (including smart surface population wip):

 

  Reveal hidden contents

 

DSWTGenerateDynamic.pscFetching info... Generate.pscFetching info...

Edited by DeadlyStr1ke
Issue was solved, solution included
Link to comment
Share on other sites

hm... Not sure that I understand the problem correctly, but you can try to make your Dynamic function non-global.

The reason why scripts need to be "locked" - sync access to script's variables. Global functions do not use script's variables, so it is possible that calls of global function does not "lock" script.

 

I know that global native functions doesn't lock the script where they are declared, but I'm not sure about just global functions.

Edited by DlinnyLag
Link to comment
Share on other sites

Hey @DlinnyLag, thank you for the reply! I'll try out your suggestion, maybe that'll do the trick.

Gonna try to explain what is happening with an example:

So, I have a room, in which I have 3 activator objects, using the same script that is attached to their forms (DSWTGenerateDynamic), one is a table, that spawns a random version of table, taken from the array in the script property, second is a chair, that does the same, third is a couch, same process. Sometimes, when they run simultaneously, the second or third activator can change the variables before the first one finishes generating, for example

ObjectReference generatedStaticDynamic = sourceGenerator.PlaceAtMe(poolStaticDynamics[Utility.RandomInt(0,poolStaticDynamicsSize)])

changing this variable for the table "generator" to be either a chair or a couch, thus leaking into it's thread before it can finish.

What I was looking for is for a way to completely "lock" the thread, so that no matter what my table objects calls inside the OnLoad event, the chair and couch wont run until the table is finished with the thread. So, force them to que and not slip in during calls. I read on States, checked out bool locks, but haven't had any success in implementing them, might be that I am understanding them process wrong. I'll try one more way I think it can work after I try your suggestion.

Edit: I got it working with the use of a GlobalVariable, since neither states, nor bools appear to be thread-safe for such things.

This is the code I used to lock up the script until each individual reference is done with it:

 

    ; Wait until the global lock is free
    While DSWT_GlobalVar_isDynamicLocked.GetValueInt() == 1
        Utility.Wait(1.0)
    EndWhile
    ; Lock the global variable
    DSWT_GlobalVar_isDynamicLocked.SetValue(1)

 

Edited by DeadlyStr1ke
Link to comment
Share on other sites

  On 7/8/2024 at 9:16 PM, DeadlyStr1ke said:

I got it working with the use of a GlobalVariable, since neither states, nor bools appear to be thread-safe for such things.
This is the code I used to lock up the script until each individual reference is done with it:

Expand  

Your solution still allow race condition.

You can use the fact that calls of DS:*Set.Add/Remove and DS:*Dict*.Add/Remove are atomic and returns result of operation and build your own mutex.

An example, how it can be done:

Keyword someKey

int someNumber = 10 const

bool function AcquireLock()
   return DS:IntSet.Add(someKey, someNumber) ; returns true if value was added to the set, otherwise - false
endfunction

function ReleaseLock()
  DS:IntSet.Remove(someKey, someNumber)
endfunction

function CriticalSection()
   while !AcquireLock()
      Utility.Wait(1) ; 
   endwhile

   ; Do the stuff 
   ....

   ReleaseLock()
endfunction

 

 

 

  • Like 1
Link to comment
Share on other sites

  • Recently Browsing   0 members

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