Jump to content

[LE] Throttling OnHit scripts


lesi123

Recommended Posts

I'm currently stress testing a script rewrite of my old combat mod and, in the beginning, if I engaged in fights with many NPCs or entered a situation with a lot of OnHit events flying around (dear god, the multi dragons fights...), Papyrus would start to churn out stack dumps. I rewrote my scripts again using states using the "Stopping a Function From Running Again Before Finishing" method detailed here: http://www.cipscis.com/skyrim/tutorials/states.aspx and made a Ready state and Busy state.

 

It helped tremendously! However, I originally had the OnHit event check a few condition then go to a custom function block depending on what conditions were met. Now I'm not so sure if those function blocks should be in the Ready state or if they are fine outside.

 

In essence, the code currently looks like this:

 

State Ready
Event OnHit
GoToState ("Busy") ;don't run this OnHit event again until it's finished checking conditions
if Blah == 1
Function1()
elseif Blah == 2
Function2()
endif
GoToState("Ready") ;finished checking conditions and sent to function blocks, OnHit can be called again
EndEvent
EndState
State Busy
Event OnHit
; nothing
EndEvent
EndState
Function1()
if Foo == 1
Bar1.cast(PlayerRef)
elseif Foo == 2
Bar2.cast(PlayerRef)
endif
EndFunction
Function2()
if Doo == 1
Dad1.cast(PlayerRef)
elseif Doo == 2
Dad2.cast(PlayerRef)
endif
EndFunction

 

 

Any advice on where I should be placing these functions blocks? Do I even need them?...

 

Thank you for taking the time to read this and any help you could provide!

Edited by lesi123
Link to comment
Share on other sites

  • Replies 67
  • Created
  • Last Reply

Top Posters In This Topic

I'm unsure what you mean be declaring the functions at the top, you mean as in writing them above the OnHit event?

 

I'm honestly not sure if it's switching back to the Ready state before the functions are finished running in the current structure. I only know that the "bottleneck" condition checks are but wasn't sure if it was going to be a problem written the way it is or if I had to shove those functions into the Ready state too to avoid the functions running during or after the script gets sent back to Ready.

Link to comment
Share on other sites

Int QF = 0 ; <- flag

 

Event OnInit()
GoToState("Done")
EndEventv

 

Event OnHit()

If(QF==(0)) ; <- is ready

QF = 1 ; <- set busy

If(Foo==(1))

self.Function1()

ElseIf(Foo==(2))

self.Function2()

EndIf

QF = 0 ; <- reset ready

EndIf

EndEvent

 

Function1()

;bla bla bla

EndFunction

 

Function2()

;bla bla bla

EndFunction

 

State Done
; do nothing
EndState
;--------------------

This is weird ...

 

ScriptName LongFunctionScript extends ObjectReference

Function LongFunction()
GoToState("Busy") ; Don't run this function again until it's finished

; Do something that takes a long time

GoToState("Waiting") ; The function has finished, so it can be called again
EndFunction

State Busy
Function LongFunction()
; Do nothing
EndFunction
EndState

Edited by NexusComa
Link to comment
Share on other sites

Int QF = 0 ; <- flag

SmkViper, one of the developers responsible for Papyrus, has said that using states allows the game to optimize scripts. And that using flags should only be done in the rare cases where states can't accomplish what you want. (Even then a combination of states and flag variables would be the right solution.)

 

While using states appears equivalent to what you could accomplish more easily using a variable as a flag there are extremely important optimizations that you can't implement using flag variables. Especially for OnHit and inventory events the flag variable method is a terrible idea.

 

The state solution prevents LongFunction from even getting called when the Busy state is active. Using a flag variable still results in LongFunction getting called and the if statement running every time with only the body of the if getting skipped.

 

For something like the OnHit or inventory events (which can get called hundreds of times per second) that difference really matters.

ScriptName LongFunctionScript extends ObjectReference
{This is the optimal Papyrus way to ensure multiple copies of a function or event don't run at the same time.}
 
Function LongFunction()
    GoToState("Busy")
    ; Do something that takes a long time
    GoToState("")
EndFunction
 
State Busy
    Function LongFunction()
    EndFunction
EndState
ScriptName LongFunctionScript extends ObjectReference
{This is a bad idea. Always use the state system above instead!}  
string busy = false;

Function LongFunction()
    if !busy
        busy = true
        ; Do something that takes a long time
        busy = false
    endif
EndFunction
Link to comment
Share on other sites

I'm unsure what you mean be declaring the functions at the top, you mean as in writing them above the OnHit event?

 

I'm honestly not sure if it's switching back to the Ready state before the functions are finished running in the current structure. I only know that the "bottleneck" condition checks are but wasn't sure if it was going to be a problem written the way it is or if I had to shove those functions into the Ready state too to avoid the functions running during or after the script gets sent back to Ready.

When I said top level I meant outside of any declared state just like in the code you posted above. The only thing that needs to be protected is the OnHit event itself and you're doing it right.

 

When you call Function1, Function2, etc. from inside the OnHit event the control is transferred to those functions and that instance of OnHit is on hold until those functions return, so the state change at the bottom of OnHit won't happen until after those other functions return.

Link to comment
Share on other sites

Programming without flags .... Madness!

The default Skyrim scripts are filled with flags of every type. Even the Global's are used as flags.

 

The optimizing comes into play as the current state is loaded on startup and ready to go in its entirety.

Buffered if you will ... This had been completely considered and fully optimized by using the GotoState("Done").

 

The only time this method would run into problems with an OnHit event would be if the script used the wait command

someplace within the function. Then it could miss a hit. Other than that it could take 101 hits a second and never miss.

 

Considering we can't really see the script and have no clue to what it's doing to respond at all is kind of hit and miss.

I suggest some testing to see how/if this will work for you in this case.

 

 

[ If you have a way to write this script simply post it for his consideration ... why feel the need to troll others responses ]

Edited by NexusComa
Link to comment
Share on other sites

Programming without flags .... Madness!

The default Skyrim scripts are filled with flags of every type. Even the Global's are used as flags.

 

The optimizing comes into play as the current state is loaded on startup and ready to go in its entirety.

Buffered if you will ... This had been completely considered and fully optimized by using the GotoState("Done").

 

The only time this method would run into problems with an OnHit event would be if the script used the wait command

someplace within the function. Then it could miss a hit. Other than that it could take 101 hits a second and never miss.

 

Considering we can't really see the script and have no clue to what it's doing to respond at all is kind of hit and miss.

I suggest some testing to see how/if this will work for you in this case.

 

 

[ If you have a way to write this script simply post it for his consideration ... why feel the need to troll others responses ]

I am not trolling your response, the original poster already has a working script and was simply asking if it was really the correct solution to the problem. And as I've stated repeatedly now, it is. I've cited my source: a developer of the scripting language.

 

You also provided the same basic state information at the bottom of your first posting, but called it weird. Since you also posted code which does not solve the problem of the scripting system getting overwhelmed by too many calls to OnHit I felt the need to clarify that the flag variable scheme something that SmkViper said should not be used as a general solution when states will do the job.

 

Flag variables are indeed used in many of the game's scripts, but not for the sort of optimization which is the topic of this thread.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...