Difference between revisions of "Creating Multithreaded Skyrim Mods Part 3 - Callbacks"

Updated Thread section.
imported>Chesko
(Added Futures tutorial as a template; will edit to fit Callbacks.)
imported>Chesko
(Updated Thread section.)
Line 47: Line 47:
<source lang="papyrus">
<source lang="papyrus">
scriptname GuardPlacementThread extends Quest hidden
scriptname GuardPlacementThread extends Quest hidden
 
;Thread control variables
;Thread variables
ObjectReference future
bool thread_queued = false
bool thread_queued = false
 
;Variables you need to get things done go here  
;Variables you need to get things done go here  
ActorBase theGuard
ActorBase theGuard
Static theMarker
Static theMarker
 
;Thread queuing and set-up
;Thread queuing and set-up
ObjectReference function get_async(Activator akFuture, ObjectReference akFutureAnchor, ActorBase akGuard, Static akXMarker)
ObjectReference function get_async(ActorBase akGuard, Static akXMarker)
        ;Let the Thread Manager know that this thread is busy
    ;Let the Thread Manager know that this thread is busy
        thread_queued = true
    thread_queued = true
        ;Store our passed-in parameters to member variables
    ;Store our passed-in parameters to member variables
theGuard = akGuard
theGuard = akGuard
theMarker = akXMarker
theMarker = akXMarker
;Create the Future that will contain our result
future = akFutureAnchor.PlaceAtMe(akFuture)
return future
endFunction
endFunction
 
;Allows the Thread Manager to determine if this thread is available
;Allows the Thread Manager to determine if this thread is available
bool function queued()
bool function queued()
return thread_queued
return thread_queued
endFunction
endFunction
 
;For Thread Manager troubleshooting.
bool function has_future(ObjectReference akFuture)
    if akFuture == future
        return true
    else
        return false
    endif
endFunction
 
;For Thread Manager troubleshooting.
;For Thread Manager troubleshooting.
bool function force_unlock()
bool function force_unlock()
Line 91: Line 77:
     return true
     return true
endFunction
endFunction
 
;The actual set of code we want to multithread.
;The actual set of code we want to multithread.
Event OnGuardPlacement()
Event OnGuardPlacement()
if thread_queued
if thread_queued
;OK, let's get some work done!
;OK, let's get some work done!
ObjectReference tempMarker = Game.GetPlayer().PlaceAtMe(theMarker) ;We could have passed PlayerRef in as a get_async() parameter, too
ObjectReference tempMarker = Game.GetPlayer().PlaceAtMe(theMarker)
MoveGuardMarkerNearPlayer(tempMarker)
MoveGuardMarkerNearPlayer(tempMarker)
ObjectReference result = tempMarker.PlaceAtMe(theGuard)
ObjectReference result = tempMarker.PlaceAtMe(theGuard)
                tempMarker.Disable()
tempMarker.Disable()
                tempMarker.Delete()
tempMarker.Delete()
 
                ;OK, we're done - let's pass the result back to the future
        ;OK, we're done - raise event to return results
                ;UNCOMMENT THIS after compiling GuardPlacementFuture
RaiseEvent_GuardPlacementCallback(result)
;(future as GuardPlacementFuture).result = result
 
        ;Set all variables back to default
                ;Set all variables back to default
clear_thread_vars()
clear_thread_vars()
 
                ;Make the thread available to the Thread Manager again
        ;Make the thread available to the Thread Manager again
thread_queued = false
thread_queued = false
endif
endif
endEvent
endEvent
 
;Another function that does things we want to multithread.
;Called from Event OnGuardPlacement
function MoveGuardMarkerNearPlayer(ObjectReference akMarker)
function MoveGuardMarkerNearPlayer(ObjectReference akMarker)
;Expensive SetPosition, GetPosition, FindNearestRef, etc calls here (illustration only)
;Move the marker away from the player a random distance and direction in 75.0 game unit increments
endFunction
Actor player = Game.GetPlayer()
 
Float A = player.GetAngleZ() + (Utility.RandomInt(1, 24) * 15.0)
Float YDist = math.Sin(A)
Float XDist = math.Cos(A)
XDist *= (Utility.RandomInt(1, 5) * 75.0)
YDist *= (Utility.RandomInt(1, 5) * 75.0)
akMarker.MoveTo(player, XDist, YDist)
EndFunction
function clear_thread_vars()
function clear_thread_vars()
;Reset all thread variables to default state
;Reset all thread variables to default state
theGuard = None
theGuard = None
theMarker = None
theMarker = None
endFunction
;Create the callback
function RaiseEvent_GuardPlacementCallback(ObjectReference akGuard)
    int handle = ModEvent.Create("MyMod_GuardPlacementCallback")
    if handle
    ModEvent.PushForm(handle, akGuard as Form)
        ModEvent.Send(handle)
    else
        ;pass
    endif
endFunction
endFunction
</source>
</source>
Line 130: Line 133:
As you can see, our thread does a few important things:
As you can see, our thread does a few important things:
* It has a <code>get_async()</code> function, which takes in all of the parameters necessary to do the work we need to perform.
* It has a <code>get_async()</code> function, which takes in all of the parameters necessary to do the work we need to perform.
* <code>get_async()</code> creates a <code>Future</code> which will eventually make its way back to the script that called our Thread Manager function.


* <code>Event OnGuardPlacement()</code> will fire if the Thread Manager raises the event.
* <code>Event OnGuardPlacement()</code> will fire if the Thread Manager raises the event.


* The thread returns its results back to the Future it created.
* The thread "calls back" to any scripts that have registered for our callback event.


* It clears all of the member variables using <code>clear_thread_vars()</code>.
* It clears all of the member variables using <code>clear_thread_vars()</code>.
Anonymous user