- Joined
- Jan 17, 2010
- Messages
- 149
Posting this here in case its useful for someone.
A simple library to synchronize boolean values (heavily inspired-by/based-on TH's [vJASS] - SyncInteger & all the research done around this topic). I decided to code this because BlzTriggerRegisterPlayerSyncEvent/BlzSendSyncData/BlzGetTriggerSyncData were too cumbersome and syncinteger+sync too bloated for synchronizing tiny values. Also this works in all versions of of Wc3 so no dependency 1.31+.
This library should only be used for tiny value synchronizations (flags, smallints, etc), for local values, for stuff such as config done at map initialization.
Unlike TH's library, this one only works for booleans (although you can pack integers into boolean chains, its not recommended with this library), it does not create dummy units unless synchronization is requested, cleans after itself after usage is over, and is overall more compact (library is barebones because there's no need for string/int sync).
In addition, the API provides an immediate callback for convenience:
Warning: SyncBoolean IS NOT safe in GetLocalPlayer() blocks
A simple library to synchronize boolean values (heavily inspired-by/based-on TH's [vJASS] - SyncInteger & all the research done around this topic). I decided to code this because BlzTriggerRegisterPlayerSyncEvent/BlzSendSyncData/BlzGetTriggerSyncData were too cumbersome and syncinteger+sync too bloated for synchronizing tiny values. Also this works in all versions of of Wc3 so no dependency 1.31+.
This library should only be used for tiny value synchronizations (flags, smallints, etc), for local values, for stuff such as config done at map initialization.
Unlike TH's library, this one only works for booleans (although you can pack integers into boolean chains, its not recommended with this library), it does not create dummy units unless synchronization is requested, cleans after itself after usage is over, and is overall more compact (library is barebones because there's no need for string/int sync).
In addition, the API provides an immediate callback for convenience:
JASS:
// for example
function test takes nothing returns nothing
if GetSyncedBoolean() == true then
call BJDebugMsg(GetPlayerName(GetTriggerPlayer()) + ": yes")
else
call BJDebugMsg(GetPlayerName(GetTriggerPlayer()) + ": no")
endif
endmethod
//...
function bleh takes nothing returns nothing
local boolean b = false
if GetLocalPlayer() == Player(0) then
set b = true
endif
call SyncBoolean(Player(0), b, function test)
endfunction
Warning: SyncBoolean IS NOT safe in GetLocalPlayer() blocks
JASS:
// bad
if GetLocalPlayer() == Player(0) then
// desync
call SyncBoolean(Player(0), b, function ...)
endif
// Good
call SyncBoolean(Player(0), b, function ...)
JASS:
library BooleanSync requires optional TimerUtils
private struct BooleanSync extends array
// must be a unit with vision (~100 sight range) so players/triggers can select it
private static constant integer DUMMY_UID = 'nrat' // in this instance, its a wc3 rat (350 sight range)
// area in the map that's out of the way for any play
private static real DUMMY_X
private static real DUMMY_Y
// dummy unit handles
private static unit dummy1
private static unit dummy2
private static unit dummy_helper
// callback stack
private static trigger array callbacks
private static integer stack
// utility handles
private static integer deleteTick
private static timer deleteTimer
private static trigger selectTrigger
private static group selectionGroup
// event response
readonly static boolean synced
static hashtable callbacksHT
static method Code2Trigger takes code c returns trigger
local integer cfid
if c == null then
return null
endif
set cfid = GetHandleId(Condition(c))
if not HaveSavedHandle(callbacksHT, cfid, cfid) then
call SaveTriggerHandle(callbacksHT, cfid, cfid, CreateTrigger())
call TriggerAddCondition(LoadTriggerHandle(callbacksHT, cfid, cfid), Condition(c))
endif
return LoadTriggerHandle(callbacksHT, cfid, cfid)
endmethod
private static method CreateDummy takes nothing returns unit
local unit u
local integer i = 0
// create dummy unit
set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_UID, DUMMY_X, DUMMY_Y, 0)
// Make dummy only selectable through triggers
call UnitAddAbility(u, 'Amrf')
call SetUnitFlyHeight(u, 5000, 0)
call UnitAddAbility(u, 'Aeth')
call SetUnitScale(u, 0, 0, 0)
call PauseUnit(u, true)
// Hide health bar
call UnitAddAbility(u , 'Aloc')
call ShowUnit(u, false)
call UnitRemoveAbility(u , 'Aloc')
call ShowUnit(u, true)
// ensure it doesn't die
call SetUnitInvulnerable(u, true)
// share vision
loop
call UnitShareVision(u, Player(i), true)
set i = i + 1
exitwhen i >= bj_MAX_PLAYER_SLOTS
endloop
set dummy_helper = u
set u = null
return dummy_helper
endmethod
private static method onSelect takes nothing returns boolean
if GetTriggerUnit() == dummy1 or GetTriggerUnit() == dummy2 then
if GetTriggerUnit() == dummy1 then
set synced = true
else
set synced = false
endif
if callbacks[stack] != null then
call TriggerEvaluate(callbacks[stack])
set callbacks[stack] = null
endif
set stack = stack - 1
if stack < 0 then
// shield from array negative index crash
set stack = 0
endif
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local integer i = 0
static if LIBRARY_TimerUtils then
set deleteTimer = null
else
set deleteTimer = CreateTimer()
endif
set selectTrigger = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(selectTrigger, Player(i), EVENT_PLAYER_UNIT_SELECTED, null)
set i = i + 1
exitwhen i >= bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(selectTrigger, Filter(function thistype.onSelect))
call DisableTrigger(selectTrigger)
set DUMMY_X = GetRectMinX(GetWorldBounds()) + 1000
set DUMMY_Y = GetRectMaxY(GetWorldBounds()) - 1000
set dummy1 = null
set dummy2 = null
set synced = false
set selectionGroup = CreateGroup()
set stack = 0
set deleteTick = 0
set callbacksHT = InitHashtable()
endmethod
private static method deleteThis takes nothing returns nothing
set deleteTick = deleteTick - 1
if deleteTick <= 0 then
static if LIBRARY_TimerUtils then
call ReleaseTimer(GetExpiredTimer())
set deleteTimer = null
endif
call DisableTrigger(selectTrigger)
if dummy1 != null then
call RemoveUnit(dummy1)
set dummy1 = null
endif
if dummy2 != null then
call RemoveUnit(dummy2)
set dummy2 = null
endif
endif
endmethod
static method syncB takes player p, boolean b, code callback returns nothing
local unit u
local unit last
local integer count
if p == null then
set p = GetLocalPlayer()
endif
call EnableTrigger(selectTrigger)
if dummy1 == null then
set dummy1 = CreateDummy()
endif
if dummy2 == null then
set dummy2 = CreateDummy()
endif
if GetLocalPlayer() == p then
call GroupEnumUnitsSelected(selectionGroup, p, null)
set count = 0
set u = FirstOfGroup(selectionGroup)
loop
exitwhen u == null
set last = u
call GroupRemoveUnit(selectionGroup, u)
set count = count + 1
set u = FirstOfGroup(selectionGroup)
endloop
if count >= 12 then
call SelectUnit(last, false)
endif
if b then
call SelectUnit(dummy1, true)
call SelectUnit(dummy1, false)
else
call SelectUnit(dummy2, true)
call SelectUnit(dummy2, false)
endif
if count >= 12 then
call SelectUnit(last, true)
endif
endif
set stack = stack + 1
set callbacks[stack] = thistype.Code2Trigger(callback)
if deleteTick <= 0 then
static if LIBRARY_TimerUtils then
if deleteTimer == null then
set deleteTimer = NewTimer()
endif
call TimerStart(deleteTimer, 1, true, function thistype.deleteThis)
else
call TimerStart(deleteTimer, 1, true, function thistype.deleteThis)
endif
endif
set deleteTick = 5
set u = null
set last = null
endmethod
endstruct
function GetSyncedBoolean takes nothing returns boolean
return BooleanSync.synced
endfunction
function SyncBoolean takes player p, boolean b, code callback returns nothing
call BooleanSync.syncB(p, b, callback)
endfunction
endlibrary
Last edited: