• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!

[JASS] Race conditions in JASS?

Status
Not open for further replies.
Level 15
Joined
Aug 7, 2013
Messages
1,337
Hi,

If I understand correctly Warcraft 3 only has a single thread that runs at a time. So every single call is ordered, in that there are no two calls that run at the exact same time, and that each call needs to wait for the one before it to finish before it is executed?

Now suppose I have a (global) boolean variable. I have two separate triggers which do the exact same thing to that boolean: they set it to true only if it is false.

These two triggers also fire on the same exact event, e.g. a player unit picks up any item. Here is some example code:

JASS:
function trig1Actions takes nothing returns false
  if myBool == False then
     set myBool == True
     call DisplayTimedTextToPlayer(Player(0), 0, 0, 5, "trig1 entered CS!"
  endif
endfunction

private function init takes nothing returns nothing
  local trigger t = CreateTrigger()
  call TriggerRegisterPlayerUnitEventSimple(t, Player(0), EVENT_PLAYER_UNIT_PICKUP_ITEM)
  call TriggerAddCondition(trig1Actions, Condition(function trig1Actions))
  set t = null
endfunction
...

function trig2Actions takes nothing returns false
  if myBool == False then
     set myBool == True
     call DisplayTimedTextToPlayer(Player(0), 0, 0, 5, "trig2 entered CS!")
  endif
endfunction

private function init takes nothing returns nothing
  local trigger t = CreateTrigger()
  call TriggerRegisterPlayerUnitEventSimple(t, Player(0), EVENT_PLAYER_UNIT_PICKUP_ITEM)
  call TriggerAddCondition(trig1Actions, Condition(function trig2Actions))
  set t = null
endfunction

As you can see, they have the exact same event conditions, but obviously different actions. So if a unit picks up an item, what will happen? Will I get both display text messages, or either only from trig1 or trig2? Will it be 'random' to me which one happens to set the flag to true first?
 
It depends on which trigger had the event registered first. It is not random--it'll have the same order each time the event occurs in-game. Here is some sample code:
JASS:
globals
	trigger array t
endglobals

library A initializer Init
	public function Cond takes nothing returns boolean
		call BJDebugMsg("A")
		return false 
	endfunction

	private function Init takes nothing returns nothing 
		set t[0] = CreateTrigger()
		call TriggerRegisterPlayerEventEndCinematic(t[0], Player(0))
	endfunction
endlibrary

library B initializer Init
	public function Cond takes nothing returns boolean
		call BJDebugMsg("B")
		return false 
	endfunction

	private function Init takes nothing returns nothing 
		set t[1] = CreateTrigger()
		call TriggerRegisterPlayerEventEndCinematic(t[1], Player(0))
	endfunction
endlibrary

library C initializer Init
	public function Cond takes nothing returns boolean
		call BJDebugMsg("C")
		return false 
	endfunction

	private function Init takes nothing returns nothing 
		set t[2] = CreateTrigger()
		call TriggerRegisterPlayerEventEndCinematic(t[2], Player(0))
	endfunction
endlibrary

library D initializer Init requires A, B, C
	private function OnStart takes nothing returns nothing 
		call TriggerAddCondition(t[2], Condition(function C_Cond))
		call TriggerAddCondition(t[2], Condition(function C_Cond))
		call TriggerAddCondition(t[1], Condition(function B_Cond))
		call TriggerAddCondition(t[0], Condition(function A_Cond))
	endfunction

	private function Init takes nothing returns nothing 
		call TimerStart(CreateTimer(), 0, false, function OnStart)
	endfunction
endlibrary

When the player presses ESC, it'll display "A", "B", "C", "C" in that order. Note how the conditions are actually registered "C", "C", "B", "A". You can rearrange it as much as you'd like, but the order will remain the same. It all depends on which trigger registered the event first. So the question is, how do you know which is registered first?

By looking at the source code (the outputwar3map.j, or you can just force an error like this), you can determine the order of initialization. For the example above:
JASS:
function main takes nothing returns nothing
    call SetCameraBounds(- 3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), - 3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), - 3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), - 3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM))
    call SetDayNightModels("Environment\\DNC\\DNCLordaeron\\DNCLordaeronTerrain\\DNCLordaeronTerrain.mdl", "Environment\\DNC\\DNCLordaeron\\DNCLordaeronUnit\\DNCLordaeronUnit.mdl")
    call NewSoundEnvironment("Default")
    call SetAmbientDaySound("LordaeronSummerDay")
    call SetAmbientNightSound("LordaeronSummerNight")
    call SetMapMusic("Music", true, 0)
    call InitBlizzard()

// initializers
call ExecuteFunc("A__Init")
call ExecuteFunc("B__Init")
call ExecuteFunc("C__Init")
call ExecuteFunc("D__Init")

    call InitGlobals()
    call InitCustomTriggers()

endfunction

As such, the order will be A, B, C when the player presses ESC. What determines the order? A number of things. First of all, there is an order of initializations (module initializers are run first, then struct intializers, then library initializers, etc.)
See: http://www.hiveworkshop.com/forums/...80/jpag-jass-proper-application-guide-204383/
After that, then it is determined by where it ends up being placed in the resulting war3map.j (which has other factors... it isn't always the order it is in the code) and by what it requires (libraries that require other libraries are placed at the bottom after all libraries that don't have requirements).

Good luck!
 
Status
Not open for further replies.
Top