1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[vJASS] SyncInteger

Discussion in 'JASS Resources' started by TriggerHappy, Apr 30, 2016.

  1. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,665
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Updated. I tried base 20 but nothing changed.

    I noticed in the current implementation some 8-10 digit numbers work but most don't.

    For example 2147483647 will return 2147483520.

    Code (Text):

    1.0.5

    [SyncInteger]
    - Added RemoveSyncEvent
    - OnSyncInteger returns trigger condition (for use with RemoveSyncEvent)
    - Added IsPlayerIdSyncing
    - Other minor changes
     
     
  2. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Ok, time to debug then, i suppose the math fail somewhere.
     
  3. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,665
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
  4. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    I would think that you would want to synchronize 12 bits at a time using base 2. If a unit is present, it is a 1. If it isnt, it is a 0. Every unit would represent a position in a bitstring of 12 bits.


    Here is the idea. 13 units

    111111111111
    EOF

    You would synchronize from the highest bit down to the lowest bit. When a higher bit comes in, the next bitstring would be worked on. When EOF comes in, the stream would end. You would only select a unit if the 1 is present in the bitstring. 0's would be the absence of a synchronized unit. You could build the number by just adding the values together that the unit's represent. For example, if you were to synchronize the unit in the 3rd position, 2nd position, and 1st position, it would be 2^2 + 2^1 + 2^0, or 7.


    I am again 100% unsure of how you are currently doing it, but I saw a base 20 there and figured I might put in some feedback ^_^.

    For the file, you could use base 64 to rapidly combine the bits together.
     
  5. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    @TH : Yep i realized that also (after reading the very same thread yesterday), so instead of pow using an array of integer maybe ?
    This way no real will be involved.

    Also, what about making the base configurable with some constant integer ?
    I think it should be up to the user to decide how many dummies he wants, the more, the fastest sync you will get.
    Anyway that seems a library for a personal stuff, not in a public resource, right ?

    @Nestharus :
    You can sync as many booleans as you want if you convert it to integers.
    Because of jass integer range/implementation, 32 booleans seems fair enough.
    In fact i would ask TH to implement a such feature when there were no more bugs, you just ask before me.
     
    Last edited: May 10, 2016
  6. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    @Troll-Brain
    The idea was to synchronize integers using the method I described. Converting them to binary and synchronizing 12-bits at a time seems like the fastest way to sync integers using SelectUnit.

    edit
    Dr Super Good had a good idea. use 33 units, 32 for bits and 33rd for EOF. ALWAYS select 12 units, encoding as many 1's as you possibly can : ). 0's are passive, so this would allow you to encode up to 32 bits depending on the number of 1's ^_^.
     
    Last edited: May 10, 2016
  7. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Then maybe read the script.

    Less selection = faster sync.
    Higher base = less selections.
     
  8. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Ah, I see now.

    Thanks Troll-Brain.

    I would use base 2, 4, 8, 16, 32, or 64 as bits can be packed into those.


    So, from my understanding of the code then, SelectUnit is not asynchronous : ).
     
  9. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    I don't think the efficiency of the script or even logic really does matter here, since it's not in the same scale of the delay to sync anyway.
    The time to sync is about 0.2 s in a lan game with 10 selections (or something like that).
    I mean, the main problem is the time to sync. And we should focus on that first.

    I don't think TH has even tested on battle.net. (i had the idea way before and i didn't care to test)

    We could also imagine use these dummies for something else, instead of just wasting resources.

    Problem is that they need to be selectable, so the use is limited.
    Or maybe not that much, i've already read that some animations makes an unit not selectable (some property inside the model, but i don't have any knowledge about modeling)
    However i don't know if it's with the mouse AND with a trigger action.
    If it's only with the mouse, then it's perfect.

    EDIT :

    Well, it makes sense that you can select an unit in a local block and that the select event still fire on each computer.
    You do that every day with your mouse.
     
  10. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,665
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Not the current implementation but I have tested up to 64 selections and it was sent in under a second.

    I have tested with 2-5 players.

    Yes it is.

    -------

    I will be updating this soon enough
     
  11. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Ok, so I wrote a Network 2 using SelectUnit instead of a gamecache and decided to benchmark it as compared to the original Network.

    Keep in mind that original Network has the capacity to synchronize up to 32 bits of data at a time.


    The original network did 5000 synchronizations in 34.625 seconds, sending across 19.53125 kilobytes of data.

    The SelectUnit version did 5000 synchronizations in 122.25 seconds, sending across 3.662109375 kilobytes of data.


    I think that this is not the droid we are looking for : ).



    Now, how do these times change as more players go into the game? The SelectUnit may be more scalable. However, I still believe that if TriggerSyncReady is optimized in the gamecache algorithm, that the gamecache algorithm will crush the SelectUnit algorithm in all cases.



    SelectUnit allows 512 synchronizations before throttling occurs, at which point only 300 selections may occur every half a second.

    In comparison, gamecache allows 1536 synchronizations to occur immediately before throttling, at which point it is throttled to 512 synchronizations per second. This is with the current TriggerSyncReady spam.


    I would like to compare gamecache algorithm with optimized syncs to SelectUnit in a 12-player setting across 5000 synchronizations.
     
    Last edited: May 11, 2016
  12. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    6,791
    Resources:
    12
    Models:
    5
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    12
    All these values are completely theoretical and FAR OFF the reality when actually applied to maps.

    So far, I had WAY BETTER results with selectUnit in practical Bnet tests (6 players) than I ever had with Network.
     
  13. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Give me a moment because I have an interesting idea. I will look for testers later : ).

    edit
    IcemanBo, Killcide, 1 random pub, and I tested the new algorithm utilizing gamecache. Synchronizing 5000 values took 31 seconds. That is 19.53125 kilobytes of data : ).


    I believe this is far faster than anything you could hope for with SelectUnit ^_^.



    Rather than TriggerSyncReady spam, a periodic timer and polling was utilized.


    Need to test with 12 players.
     
    Last edited: May 11, 2016
  14. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,665
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Here is some basic test code for GC in case some are wondering.

    Code (vJASS):

    scope TestInt initializer Init

        globals
            private timer Timer=CreateTimer()
            private gamecache GC
        endglobals

        private function Check takes nothing returns nothing
            local integer v

            set v = GetStoredInteger(GC, "m", "1")

            if (v > 0) then
                // this runs for the first player before everyone else, may cause desync
                call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Took: " + R2S(TimerGetElapsed(Timer)) + " seconds, int was " + I2S(v))
                call PauseTimer(GetExpiredTimer())
            endif
        endfunction

        private function MapStart takes nothing returns nothing
            local integer i = 1
            local integer base = 0

            if (GetLocalPlayer() == Player(0)) then
                set base = 500
            endif

            call TimerStart(Timer, 512, false, null)

            call StoreInteger(GC, "m", "1", base)
            call SyncStoredInteger(GC, "m", "1")
        endfunction

        private function Init takes nothing returns nothing
            set GC=InitGameCache("test.w3v")
            call TimerStart(Timer, 1, false, function MapStart)
            call TimerStart(CreateTimer(), 0.01, true, function Check)
        endfunction

    endscope
     


    Depending on further tests the selection implementation may no longer be needed.

    EDIT: just posting my progress before I go to sleep

    I got syncing with GC to work (in LAN, 2 players, so who knows..), but it requires the SyncInteger library to notify when the GC sync is over for everyone.

    Here is some sloppy test code

    Code (vJASS):

    scope TestInt2 initializer Init // requires SyncInteger

        globals
            private timer Timer=CreateTimer()
            private gamecache GC
            private boolean SyncComplete = false
            private boolean SawMsg=false
            private constant integer COUNT = 500
            private integer Progress
        endglobals

        private function GCComplete takes nothing returns boolean
            set SyncComplete = (GetSyncedInteger() == 1)
            return false
        endfunction

        private function Check takes nothing returns nothing
            local integer v //= GetStoredInteger(GC, "0", I2S(1))
            local integer i = 0
            local integer c = 0

            //check
            set i = 1
            set v = 1
            loop
                exitwhen i > COUNT or v == 0
                if (GetStoredInteger(GC, "0", I2S(i)) != i) then
                    //call BJDebugMsg("Fail @ " + I2S(i))
                    set v = 0
                else
                    set c = c + 1
                endif
                set i = i + 1
            endloop

            if (v > 0) then

                set v = GetStoredInteger(GC, "0", I2S(1))

                if (not SawMsg) then
                    call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "LOCAL: " + R2S(TimerGetElapsed(Timer)) + " seconds, int was " + I2S(v))
                    set SawMsg=true
                endif

                if (GetLocalPlayer() == Player(1)) then
                    set i = 1
                endif
                call SyncInteger(1, i)
            endif

            // this runs synchronously for everyone (:
            if (SyncComplete) then
                call PauseTimer(GetExpiredTimer())

                call BJDebugMsg("SyncComplete")
                call BJDebugMsg("Took: " + R2S(TimerGetElapsed(Timer)) + " seconds to sync " + I2S(COUNT) + " integers\nFirst: " + I2S(GetStoredInteger(GC, "0", "1")) + "\nLast: " + I2S(GetStoredInteger(GC, "0", I2S(COUNT))))
            elseif (c != Progress) then
                call BJDebugMsg(I2S(c) + "/" + I2S(COUNT))
                set Progress=c
            endif
        endfunction

        private function MapStart takes nothing returns nothing
            local integer i = 1
            local integer base = 0
            local player p = Player(0)
            local string mkey = I2S(GetPlayerId(p))

            call TimerStart(Timer, 99999, false, null)

            loop
                exitwhen i > COUNT
                if (GetLocalPlayer() == p) then
                    call StoreInteger(GC, mkey, I2S(i), base + i)
                    call SyncStoredInteger(GC, mkey, I2S(i))
                endif
                set i = i + 1
            endloop
        endfunction

        private function Init takes nothing returns nothing
            set GC=InitGameCache("test.w3v")
            call TimerStart(Timer, 1, false, function MapStart)
            call TimerStart(CreateTimer(), 0.01, true, function Check)
            call OnSyncInteger(function GCComplete)
        endfunction

    endscope
     


    EDIT2: working on a pub library
     
    Last edited: May 12, 2016
  15. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    I know the truth.

    You have changed the code, just because you couldn't handle the math bug :xxd:
     
  16. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,665
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    haha except the new library requires the old one

    I still need to fix it

    EDIT:

    Current implementation with GC. I will hopefully be adding .addStr and .addUnit as well.

    Code (vJASS):

    //callback handled internally for now

    local SyncData d = SyncData.create(Player(0))
    local integer i = 1

    loop
        exitwhen i > 1000
        call d.addInt(i)
        set i = i + 1
    endloop

    call d.start()
     
     
    Last edited: May 13, 2016
  17. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    what is GC supposed to mean? :D cause only think I can think of is Garbage collector, but that makes no sense in this case
     
  18. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Game cache, obvioulsy
     
  19. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,665
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Updated.

    Code (Text):

    v1.0.6

    - Base is configurable
    - Minor optimizations
     
    I removed SyncString since my new implementation with GC is much better. Now a player can sync multiple strings at the same time, and there's improved performance.

    Check out the new library !

    https://www.hiveworkshop.com/posts/2824912/
     
  20. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Seems to work like a charm :

    Code (vJASS):
    library SyncInteger initializer Init uses optional UnitDex, optional GroupUtils
    /***************************************************************
    *
    *   v1.0.6, by TriggerHappy
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *
    *   This library allows you to send integers to all other players.
    *
    *   _________________________________________________________________________
    *   1. Installation
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *   Copy the script to your map and save it (requires JassHelper *or* JNGP)
    *   _________________________________________________________________________
    *   2. How it works
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       1. Creates 12 dummy units and assigns 10 of them an integer from 0-9.
    *          The 11th dummy is used to signal when the sequence of numbers is over.
    *          The 12h signifies a negative number.
    *
    *       2. Breaks down the number you want to sync to one or more base 10 integers,
    *          then selects each unit assoicated with that integer.
    *
    *       4. The selection event fires for all players when the selection has been sycned
    *
    *       5. The ID of the selected unit is one of the base 10 numbers. The current
    *          total (starts at 0) is multiplied by 10 and the latest synced integer is
    *          added to that. The process will repeat until it selects the 11th dummy,
    *          and the total is our result.
    *   _________________________________________________________________________
    *   3. Proper Usage
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       - Avoid the SyncSelections native. It may cause the
    *         thread to hang or make some units un-able to move.
    *
    *       - Dummies must be select-able (no locust)
    *
    *       - Run the script in debug mode while testing
    *   _________________________________________________________________________
    *   4. Function API
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       function SyncInteger takes integer playerId, integer number returns boolean
    *
    *       function GetSyncedInteger takes nothing returns integer
    *       function GetSyncedPlayer takes nothing returns player
    *       function GetSyncedPlayerId takes nothing returns integer
    *       function IsPlayerSyncing takes player p returns boolean
    *       function IsSyncEnabled takes nothing returns boolean
    *       function SyncIntegerToggle takes boolean flag returns nothing
    *       function SyncIntegerEnable takes nothing returns nothing
    *       function SyncIntegerDisable takes nothing returns nothing
    *
    *       function OnSyncInteger takes code func returns triggercondition
    *       function RemoveSyncEvent takes triggercondition action returns nothing
    *       function TriggerRegisterSyncEvent takes trigger t, integer eventtype returns nothing
    *
    *       function SyncInitialize takes nothing returns nothing
    *       function SyncTerminate takes boolean destroyEvent returns nothing
    *
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *   -http://www.hiveworkshop.com/forums/submissions-414/syncinteger-syncstring-278674/
    *
    */
     
            globals
                // calls SyncInitialize automatically
                private constant boolean AUTO_INIT          = true
               
                // owner of the dummy units
                private constant player DUMMY_PLAYER        = Player(PLAYER_NEUTRAL_PASSIVE)
               
                // dummy can *not* have locust (must be selectabe)
                // basically anything should work (like 'hfoo')
                private constant integer DUMMY_ID           = 'hfoo' // XE_DUMMY_UNITID
               
                // dummy ghost ability
                private constant integer DUMMY_ABILITY      = 'Aeth'

                // debug mode
                private constant boolean ALLOW_DEBUGGING    = true
               
                // higher == more dummies but faster
                private constant integer BASE               = 10

                // don't need to change this
                private constant integer DUMMY_COUNT        = BASE+2
               
                // endconfig
                constant integer EVENT_SYNC_INTEGER = 1
               
                private trigger OnSelectTrigger = CreateTrigger()
                private trigger EventTrig       = CreateTrigger()
                private real FireEvent          = 0
               
                private group SelectionGroup

                private integer array SyncData
                private integer LastPlayer
                private integer LastSync
                private unit array SyncIntegerDummy
                private integer array Power
            endglobals
           
            function GetSyncedInteger takes nothing returns integer
                return LastSync
            endfunction
           
            function GetSyncedPlayer takes nothing returns player
                return Player(LastPlayer)
            endfunction
           
            function GetSyncedPlayerId takes nothing returns integer
                return LastPlayer
            endfunction
           
            function IsPlayerSyncing takes player p returns boolean
                return (SyncData[GetPlayerId(p)] != -1)
            endfunction
           
            function IsPlayerIdSyncing takes integer pid returns boolean
                return (SyncData[pid] != -1)
            endfunction

            function IsSyncEnabled takes nothing returns boolean
                return IsTriggerEnabled(OnSelectTrigger)
            endfunction
           
            function SyncIntegerEnable takes nothing returns nothing
                call EnableTrigger(OnSelectTrigger)
            endfunction    
           
            function SyncIntegerDisable takes nothing returns nothing
                call DisableTrigger(OnSelectTrigger)
            endfunction    
           
            function SyncIntegerToggle takes boolean flag returns nothing
                if (flag) then
                    call EnableTrigger(OnSelectTrigger)
                else
                    call DisableTrigger(OnSelectTrigger)
                endif
            endfunction
           
            function OnSyncInteger takes code func returns triggercondition
                return TriggerAddCondition(EventTrig, Filter(func))
            endfunction

            function RemoveSyncEvent takes triggercondition action returns nothing
               call TriggerRemoveCondition(EventTrig, action)
            endfunction
           
            function TriggerRegisterSyncEvent takes trigger t, integer eventtype returns nothing
                call TriggerRegisterVariableEvent(t, SCOPE_PREFIX + "FireEvent", EQUAL, eventtype)
            endfunction
           
            function SyncInteger takes integer playerId, integer number returns boolean
                local integer x = number
                local integer i = 0
                local integer d = BASE
                local integer n = 0
                local player p
                local unit u

                static if (ALLOW_DEBUGGING and DEBUG_MODE) then
                    if (OnSelectTrigger == null) then
                        call BJDebugMsg(SCOPE_PREFIX + "SyncInteger: OnSelectTrigger is destroyed")
                    endif
                   
                    if (not IsSyncEnabled()) then
                        call BJDebugMsg(SCOPE_PREFIX + "SyncInteger: OnSelectTrigger is disabled")
                    endif
                endif
               
                if (not IsSyncEnabled()) then
                    return false
                endif
               
                if (number < 0) then
                    set d = DUMMY_COUNT-1
                    set number = number * -1
                endif
           
                set p = Player(playerId)
               
                loop
                    set x = x/(BASE)
                    exitwhen x==0
                    set i=i+1
                endloop
               
                // de-select one unit in case the players selection is full
                call GroupEnumUnitsSelected(SelectionGroup, p, null)
                set u = FirstOfGroup(SelectionGroup)

                if (GetLocalPlayer() == p) then
                    call SelectUnit(u, false)
                endif
               

                loop
                    set n = Power[i]
                    set x = number/n
                   
                    if (GetLocalPlayer() == p) then
                        call SelectUnit(SyncIntegerDummy[x], true)
                        call SelectUnit(SyncIntegerDummy[x], false)
                    endif
                   
                    set number = number-x*n

                    exitwhen i == 0
                   
                    set i = i - 1
                endloop
           
                if (GetLocalPlayer() == p) then
                    call SelectUnit(SyncIntegerDummy[d], true)
                    call SelectUnit(SyncIntegerDummy[d], false)
                    call SelectUnit(u, true)
                endif

                set u = null

                return true
            endfunction
           
            //this cleans up all dummies and triggers created by the system
            function SyncTerminate takes boolean destroyEvents returns nothing
                local integer i = 0
               
                if (destroyEvents) then
                    call DestroyTrigger(OnSelectTrigger)
                    call DestroyTrigger(EventTrig)
                    static if (LIBRARY_SyncString) then
                        call DestroyTrigger(SyncString_EventTrig)
                    endif
                else
                    call SyncIntegerDisable()
                endif
               
                static if (LIBRARY_UnitDex) then
                    set UnitDex.Enabled = false
                endif
               
                loop
                    exitwhen i >= DUMMY_COUNT
                    call RemoveUnit(SyncIntegerDummy[i])
                    set SyncIntegerDummy[i] = null
                    set i = i + 1
                endloop
               
                static if (LIBRARY_UnitDex) then
                    set UnitDex.Enabled = true
                endif
            endfunction
           
            function SyncInitialize takes nothing returns nothing
                local integer i = 0
               
                static if (ALLOW_DEBUGGING and DEBUG_MODE) then
                    if (OnSelectTrigger == null) then
                        call BJDebugMsg(SCOPE_PREFIX + "SyncInitialize: OnSelectTrigger is null and has no events attached to it")
                    endif
                endif
               
                static if (LIBRARY_UnitDex) then
                    set UnitDex.Enabled = false
                endif
               
                loop
                    exitwhen i >= DUMMY_COUNT
                    set SyncIntegerDummy[i]=CreateUnit(DUMMY_PLAYER, DUMMY_ID, 1000000, 1000000, i)
                   
                    static if (ALLOW_DEBUGGING and DEBUG_MODE) then
                        if (i == 0) then // display once
                            if (SyncIntegerDummy[i] == null) then
                                call BJDebugMsg(SCOPE_PREFIX + "SyncInitialize: Dummy unit is null (check DUMMY_ID)")
                            endif
                       
                            if (GetUnitAbilityLevel(SyncIntegerDummy[i], 'Aloc') > 0) then
                                call BJDebugMsg(SCOPE_PREFIX + "SyncInitialize: Dummy units must be selectable (detected locust)")
                                call UnitRemoveAbility(SyncIntegerDummy[i], 'Aloc')
                            endif
                        endif
                    endif
                   
                    call SetUnitUserData(SyncIntegerDummy[i], i)
                    call UnitAddAbility(SyncIntegerDummy[i], DUMMY_ABILITY)
                    call PauseUnit(SyncIntegerDummy[i], true)
                    set i = i + 1
                endloop
               
                static if (LIBRARY_UnitDex) then
                    set UnitDex.Enabled = true
                endif
               
                if (GetExpiredTimer() != null) then
                    call DestroyTimer(GetExpiredTimer())
                endif
            endfunction
           
            private function OnSelect takes nothing returns boolean
                local unit u        = GetTriggerUnit()
                local player p      = GetTriggerPlayer()
                local integer id    = GetPlayerId(p)
                local integer index = GetUnitUserData(u)
                local boolean isNeg = (SyncIntegerDummy[DUMMY_COUNT-1] == u)

                if (SyncIntegerDummy[index] != u) then
                    set u = null
                    return false
                endif

                static if (ALLOW_DEBUGGING and DEBUG_MODE) then
                    if (OnSelectTrigger == null) then
                        call BJDebugMsg(SCOPE_PREFIX + "SyncInteger: OnSelectTrigger is null")
                    endif
                endif
               
                if (isNeg) then
                    set SyncData[id] = SyncData[id]*-1
                endif

                if (isNeg or SyncIntegerDummy[DUMMY_COUNT-2] == u) then
                    set LastPlayer   = id
                    set LastSync     = SyncData[id]
                    set SyncData[id] = -1

                    // run "events"
                    set FireEvent = EVENT_SYNC_INTEGER
                    call TriggerEvaluate(EventTrig)
                    set FireEvent = 0
                else
                    if (SyncData[id]==-1)then
                        set SyncData[id]=0
                    endif
                    set SyncData[id] = SyncData[id] * BASE + index
                endif
               
                set u = null
               
                return false
            endfunction
           
            // this function is meant to be used syncstring
            public function FireEvents takes real eventtype returns nothing
                set FireEvent = eventtype
                call TriggerEvaluate(EventTrig)
                set FireEvent = 0
            endfunction
       
            //===========================================================================
            private function Init takes nothing returns nothing
                local integer i = 0
                local integer j
               
                loop
                    call TriggerRegisterPlayerUnitEvent(OnSelectTrigger, Player(i), EVENT_PLAYER_UNIT_SELECTED, null)
                   
                    set SyncData[i] = -1
                   
                    set i = i + 1
                    exitwhen i==bj_MAX_PLAYER_SLOTS
                endloop

                call TriggerAddCondition(OnSelectTrigger, Filter(function OnSelect))
               
                static if (AUTO_INIT) then
                    call TimerStart(CreateTimer(), 0, false, function SyncInitialize)
                endif
               
                static if (LIBRARY_GroupUtils) then
                    set SelectionGroup=ENUM_GROUP
                else
                    set SelectionGroup=CreateGroup()
                endif
                set i=0
                set j=1
                loop
                exitwhen i==32
                    set Power[i]=j
                    set j = j*BASE
                    set i=i+1
                endloop
            endfunction
           
            static if (ALLOW_DEBUGGING and DEBUG_MODE) then
                private function SyncSelectionsHook takes nothing returns nothing
                    call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, SCOPE_PREFIX + "SyncSelectionsHook: Detected SyncSelections (can cause issues)")
                endfunction
               
                hook SyncSelections SyncSelectionsHook
            endif

    endlibrary


    So reals are indeed the devil here.