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,671
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Recommended: Sync Library

    Demo Map:
    Codeless Save and Load (Multiplayer)

    Core System
    Code (vJASS):
    library SyncInteger uses optional UnitDex /*or any unit indexer*/, optional GroupUtils, optional xebasic, optional PlayerUtils
    /***************************************************************
    *
    *   v1.2.1, by TriggerHappy
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *
    *   This library allows you to send integers to all other players through
    *   unit selections.
    *
    *   _________________________________________________________________________
    *   1. Installation
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *   Copy the script to your map and save it (requires JassHelper *or* JNGP)
    *   _________________________________________________________________________
    *   2. How it works
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       1. Creates {DUMMY_COUNT} units and assigns {BASE} of them an integer from 0-{BASE}.
    *          The 2nd to last dummy is used to signal when the sequence of numbers is over and
    *          the last dummy signifies a negative number.
    *
    *       2. Breaks down the number you want to sync to one or more {BASE} integers,
    *          then selects each dummy 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} numbers. The current
    *          total (starts at 0) is multiplied by {BASE} and the latest synced integer is
    *          added to that. The process will repeat until it selects the 2nd to last dummy,
    *          and the total is our result.
    *   _________________________________________________________________________
    *   3. Proper Usage
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       - Dummies must be select-able (no locust)
    *
    *       - Run the script in debug mode while testing
    *
    *   _________________________________________________________________________
    *   4. Struct API
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       struct SelectionSync
    *        
    *           static method create takes nothing returns thistype
    *
    *           method syncValue takes player p, integer number returns boolean
    *           method destroy takes nothing returns nothing
    *   _________________________________________________________________________
    *   5. Function API
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       function SyncInteger takes player p, integer number returns boolean
    *
    *       function GetSyncedInteger takes nothing returns integer
    *       function GetSyncedInstance takes nothing returns integer
    *       function GetSyncedPlayer takes nothing returns player
    *       function GetSyncedPlayerId takes nothing returns integer
    *       function IsPlayerSyncing takes player p returns boolean
    *       function IsPlayerIdSyncing takes integer pid 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 filterfunc func returns triggercondition
    *       function RemoveSyncEvent takes triggercondition action returns nothing
    *       function TriggerRegisterSyncEvent takes trigger t, integer eventtype returns nothing
    *
    *       function SyncTerminate takes boolean destroyEvent returns nothing
    *
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *   -http://www.hiveworkshop.com/threads/syncinteger.278674/
    *
    */

            globals
                // create a struct instance for global use
                public constant boolean DEFAULT_INSTANCE   = true
               
                // owner of the dummy units
                public constant player DUMMY_PLAYER        = Player(PLAYER_NEUTRAL_PASSIVE)
                // Dummy can *not* have locust and must be selectabe.
                public constant integer DUMMY_ID           = 'hfoo'
                // allow debug messages (also requries JassHelper Debug Mode)
                public constant boolean ALLOW_DEBUGGING    = true
                // higher == more dummies but less selections (faster)
                public constant integer BASE               = 10
               
                // two higher than BASE (jasshelper doesn't allow BASE + 2)
                public constant integer DUMMY_COUNT        = 12
               
                // endconfig
                constant integer EVENT_SYNC_INTEGER = 1
               
                public integer DefaultInstance = 0
               
                private trigger OnSelectTrigger = CreateTrigger()
                private trigger EventTrig       = CreateTrigger()
                private real FireEvent          = 0
                private group SelectionGroup
                private integer LastPlayer
                private integer LastSync
                private integer LastInstance
                private player LocalPlayer
                private integer array ActiveSyncs
               
                private real DUMMY_X = 0
                private real DUMMY_Y = 0
               
                private integer array DummyInstance
            endglobals
            function GetSyncedInteger takes nothing returns integer
                return LastSync
            endfunction
            function GetSyncedPlayer takes nothing returns player
                return Player(LastPlayer)
            endfunction
           
            function GetSyncedInstance takes nothing returns integer
                return LastInstance
            endfunction
       
            function GetSyncedPlayerId takes nothing returns integer
                return LastPlayer
            endfunction
           
            function IsPlayerIdSyncing takes integer pid returns boolean
                return ActiveSyncs[pid] > 0
            endfunction
           
            function IsPlayerSyncing takes player p returns boolean
                return ActiveSyncs[GetPlayerId(p)] > 0
            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 filterfunc func returns triggercondition
                return TriggerAddCondition(EventTrig, 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
       
            public function FireEvents takes real eventtype returns nothing
                set FireEvent = eventtype
                set FireEvent = 0
            endfunction
           
            // This function is called when a unit is selected.
            private function OnSelect takes nothing returns boolean
                local unit u        = GetTriggerUnit()
                local player p      = GetTriggerPlayer()
                local integer id    = GetPlayerId(p)
                local integer udata = GetUnitUserData(u)
                local SelectionSync this = DummyInstance[udata]
                local boolean isNeg = (this.Dummy[DUMMY_COUNT-1] == u)
                local integer index = this.DummyID[udata] - 1
               
                if (this <= 0) then
                    return false
                endif
               
                if (isNeg) then
                    set this.SyncingValue[id] = this.SyncingValue[id]*-1
                endif
                if (isNeg or this.Dummy[DUMMY_COUNT-2] == u) then
                    set ActiveSyncs[id] = ActiveSyncs[id] - 1
                   
                    // The number is finished syncing, fire the events
                    set LastPlayer   = id
                    set LastSync     = this.SyncingValue[id]
                    set LastInstance = this
                    set FireEvent    = EVENT_SYNC_INTEGER
                   
                    call TriggerEvaluate(EventTrig)
                   
                    // Reset variables
                    set FireEvent = 0
                    set this.SyncingValue[id] = -1
                else
               
                    if (this.SyncingValue[id] == -1) then
                        set this.SyncingValue[id] = 0
                    endif
                   
                    // Build the number we are trying to sync
                    set this.SyncingValue[id] = this.SyncingValue[id] * BASE + index
                endif
       
                set u = null
       
                return false
            endfunction
           
            private keyword SyncIntegerInit
           
            // This struct allows us to dynamically create a group of units
            // which we can use to synchronize our integer through unit selections.
            struct SelectionSync
               
                public unit array Dummy[DUMMY_COUNT]
                public integer array DummyID[DUMMY_COUNT]
                public integer array SyncingValue[12]
               
                public static method debugger takes boolean b, string s returns nothing
                    static if (ALLOW_DEBUGGING and DEBUG_MODE) then
                        if (b) then
                            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "|c00FF0000" + SCOPE_PREFIX + s + "|r")
                        endif
                    endif
                endmethod
           
                static method create takes nothing returns thistype
                    local thistype this = thistype.allocate()
                    local integer i = 0
                    local integer uid
                   
                    debug call .debugger(OnSelectTrigger == null, "[SelectionSync.create()] OnSelectTrigger is null and has no events attached to it.")
                    debug call .debugger(this.Dummy[0] != null, "[SelectionSync.create()] Dummy not null!")
                   
                    loop
                        exitwhen i >= DUMMY_COUNT
                       
                        set this.Dummy[i] = CreateUnit(DUMMY_PLAYER, DUMMY_ID, DUMMY_X, DUMMY_Y, i)
               
                        set uid = GetUnitUserData(this.Dummy[i])
                     
                        if (uid == 0) then
                            set uid = ( (this-1) * DUMMY_COUNT ) + (i + 1)
                            call SetUnitUserData(this.Dummy[i], uid)
                        endif
                     
                        debug call .debugger((i == 0) and (this.Dummy[i] == null), "[SelectionSync.create()] Dummy unit is null (check DUMMY_ID).")
                        debug call .debugger((i == 0) and (GetUnitAbilityLevel(this.Dummy[i], 'Aloc') > 0), "[SelectionSync.create()] Dummy units must be selectable (detected locust).")
                     
                        set this.DummyID[uid] = i + 1
                        set DummyInstance[uid] = this
                       
                        // Make dummy only selectable through triggers
                        call UnitAddAbility(this.Dummy[i], 'Amrf')
                        call SetUnitFlyHeight(this.Dummy[i], 5000, 0)
                        call UnitAddAbility(this.Dummy[i], 'Aeth')
                        call SetUnitScale(this.Dummy[i], 0, 0, 0)
                        call PauseUnit(this.Dummy[i], true)
                       
                        // Hide health bar
                        call UnitAddAbility(this.Dummy[i], 'Aloc')
                        call ShowUnit(this.Dummy[i], false)
                        call UnitRemoveAbility(this.Dummy[i], 'Aloc')
                        call ShowUnit(this.Dummy[i], true)
                       
                        set i = i + 1
                    endloop
                   
                    return this
                endmethod
               
                method syncValue takes player p, integer number returns boolean
                    local integer x = number
                    local integer i = 0
                    local integer d = BASE
                    local integer j = 0
                    local integer n = 0
                    local integer l = 0
                    local integer playerId = GetPlayerId(p)
                    local unit u
                    local unit last
                    debug call .debugger(OnSelectTrigger == null, "[SelectionSync.syncValue()] OnSelectTrigger is destroyed.")
                    debug call .debugger(IsSyncEnabled() == false, "[SelectionSync.syncValue()] OnSelectTrigger is disabled.")
           
                    if (not IsSyncEnabled()) then
                        return false
                    endif
                   
                    set ActiveSyncs[playerId] = ActiveSyncs[playerId] + 1
                    // Check if the number is negative
                    if (number < 0) then
                        set d = DUMMY_COUNT-1
                        set number = number * -1
                    endif
         
                    loop
                        set x = x/(BASE)
                        exitwhen x==0
                        set i=i+1
                    endloop
           
                    // Count how many units are selected
                    call GroupEnumUnitsSelected(SelectionGroup, p, null)
                    set bj_groupCountUnits = 0
                    set u = FirstOfGroup(SelectionGroup)
                    loop
                        exitwhen u == null
                        set last = u
                        call GroupRemoveUnit(SelectionGroup, u)
                        set bj_groupCountUnits = bj_groupCountUnits + 1
                        set u = FirstOfGroup(SelectionGroup)
                    endloop
               
                    // If the queue is full, de-select the last unit which
                    // will allow us to select a dummy, and hopefully
                    // avoid a flickering effect.
                    if (bj_groupCountUnits >= 12 and LocalPlayer == p) then
                        call SelectUnit(last, false)
                    endif
                    set j=R2I(Pow(BASE, i))
                    loop
                        set n = j
                        set x = number/n
                        set j = j / BASE
                   
                        if (LocalPlayer == p) then
                            call SelectUnit(this.Dummy[x], true)
                            call SelectUnit(this.Dummy[x], false)
                        endif
               
                        set number = number-x*n
                        exitwhen i == 0
               
                        set i = i - 1
                    endloop
         
                    if (LocalPlayer == p) then
                        call SelectUnit(this.Dummy[d], true)
                        call SelectUnit(this.Dummy[d], false)
                     
                        if (bj_groupCountUnits >= 12) then
                            call SelectUnit(last, true)
                        endif
                    endif
                    set u = null
                    set last = null
                   
                    return true
                endmethod
               
                method destroy takes nothing returns nothing
                    local integer i = 0
         
                    loop
                        exitwhen i >= DUMMY_COUNT
                        call RemoveUnit(this.Dummy[i])
                        set this.Dummy[i] = null
                        set i = i + 1
                    endloop
                endmethod
               
                implement SyncIntegerInit
            endstruct
           
            function SyncInteger takes player p, integer number returns boolean
                debug call SelectionSync.debugger(DefaultInstance == 0, "[SyncInteger()] DefaultInstance is not initialized (make sure DEFAULT_INSTANCE is true")
               
                return SelectionSync(DefaultInstance).syncValue(p, number)
            endfunction
           
            function SyncTerminate takes boolean destroyEvents returns boolean
                local integer i = 0
               
                if (OnSelectTrigger == null and EventTrig == null) then
                    return false
                endif
               
                if (destroyEvents) then
                    call DestroyTrigger(OnSelectTrigger)
                    call DestroyTrigger(EventTrig)
                    set OnSelectTrigger = null
                    set EventTrig = null
               
                    static if not LIBRARY_GroupUtils then
                        call DestroyGroup(SelectionGroup)
                        set SelectionGroup = null
                    endif
                else
                    call SyncIntegerDisable()
                endif
               
                if (DefaultInstance > 0) then
                    call SelectionSync(DefaultInstance).destroy()
                endif
               
                return true
            endfunction
            //===========================================================================
            private module SyncIntegerInit
                private static method onInit takes nothing returns nothing
                    local integer i = 0
                    local integer j
                   
                    loop
                        call TriggerRegisterPlayerUnitEvent(OnSelectTrigger, Player(i), EVENT_PLAYER_UNIT_SELECTED, null)
             
                        set i = i + 1
                        exitwhen i==bj_MAX_PLAYER_SLOTS
                    endloop
                    call TriggerAddCondition(OnSelectTrigger, Filter(function OnSelect))
         
                    static if (LIBRARY_GroupUtils) then
                        set SelectionGroup=ENUM_GROUP
                    else
                        set SelectionGroup=CreateGroup()
                    endif
                    static if (LIBRARY_PlayerUtils) then
                        set LocalPlayer=User.Local
                    else
                        set LocalPlayer=GetLocalPlayer()
                    endif
               
                    set DUMMY_X = GetCameraBoundMaxX() + 2000
                    set DUMMY_Y = GetCameraBoundMaxY() + 2000
                   
                    static if (DEFAULT_INSTANCE) then
                        set DefaultInstance = SelectionSync.create()
                    endif
                endmethod
            endmodule
           
    endlibrary
     
    Last edited: Aug 3, 2018
  2. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    6,789
    Resources:
    12
    Models:
    5
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    12
    There is some minor inconvenience in this thing; since the only reason you have the optional xeBasic requirement is for the invisible dummy unit.
    Why not just have an object merger call creating the required dummy and call it a day?

    Besides; the way you implemented it, if the user doesn't have xeBasic, the user will get an unknown variable syntax error at the globals section.
     
  3. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Cool another sync system but with asynchronous execution.
    I think you should point out in the documentation that it's not technically syncing a 32-bit integer. It would be great if it can sync 32 -bit integer by splitting the 32 bits into 4 bits and synching them one at a time (which was originally my plan for my sync resource but too lazy to do + I have no wc3/jngp at the moment). Then combine them at the end. And that way, the no. of dummy units is constant (16 if 4 bits)
    What's different here is you're using selection events which would prevent looping through each dummy. However I thought selection events were delayed? I remember trying to make a unit unselectable using selection event and there I saw I delay (I'm not 100% sure, that was a long time ago)
     
  4. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,671
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I don't really think it matters.

    In fact I'm not even exactly sure how it works behind the scenes.


    In my tests
    IsUnitSelected
    was async so it returns false for all other players immediately after calling
    SelectUnit


    The delay in the selection event seems to be about the same as the delay in a point order. I have only tested this with 5 players, though (only 2 on b.net).

    It's possible all selection events are sent/received in a group so calling
    SelectUnit
    doesn't fire multiple requests.

    I think it might be the same thing for
    ForceUIKey
    and the ESC key event, but I have more tests to do.

    The editor has to be restarted with a object merger call and I want to avoid that especially since most people already have a dummy in their map.

    I could just remove 'Aloc' if xebasic isn't defined.

    true next version will avoid that
     
  5. HappyTauren

    HappyTauren

    Joined:
    Nov 3, 2006
    Messages:
    8,413
    Resources:
    87
    Models:
    61
    Icons:
    23
    Packs:
    1
    Tutorials:
    2
    Resources:
    87
    One of the reasons I never wanted to create a continuous MP RPG was that save/load codes always seemed kinda amateurish to me, this seems great though.
     
  6. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,671
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Updated. I will be adding a timeout feature but that will require preloading timers or data attaching (timerutils).

    Code (Text):

    v1.0.1

    [SyncString]
    - SyncString no longer needs to populate every string to the maximum length. This is a huge performance increase for users who know how long their string is going to be.
    - Other minor improvements

    [SyncInteger]
    - SyncInitialize function added to allow users to (re)initialize the system whenever they want
    - SyncTerminate now takes paramter "destroyEvents" which will destroy the triggers instead of disabling them (which makes the system un-usable from then on)
    - SyncTerminate now removes handles from the SyncString library if it's present
    - Added AUTO_INIT constant
    - XE_DUMMY_UNITID no longer hardcoded into config
    - Many improvements to debugging
    - SyncSelections is now detected in debug mode and the system will warn you *not* to use it (very useful to GUI users since a couple functions that deal with selected units use that)
    - Improved variable names
     
     
  7. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Ok, since nobody has understood or read what i've written about that, a code should be better :

    Code (vJASS):
    library Sync initializer Init

        globals
            timer Tim = CreateTimer()
            real T1
            real T2
            private unit End_sync
            private group Units = CreateGroup()
            private integer array Last_sync
            private unit array U
        endglobals
       
        function SyncInteger takes integer sync returns nothing
            local integer x = sync
            local integer i=0
            set Last_sync[0]=0
            set Last_sync[1]=0
            set T1 = TimerGetElapsed(Tim)
            loop
                set x = x/10
                exitwhen x==0
                set i=i+1
            endloop
            loop
                set x = R2I(sync/Pow(10,i))
                call SelectUnit(U[x],true)
                call SelectUnit(U[x],false)
                //call BJDebugMsg(I2S(x))
                set sync = R2I(sync-x*Pow(10,i))
                exitwhen i==0
                set i=i-1
            endloop
            call SelectUnit(End_sync,true)
            call SelectUnit(End_sync,false)
        endfunction
       
        private function OnSelect takes nothing returns boolean
            local unit u = GetTriggerUnit()
            local integer id = GetPlayerId(GetTriggerPlayer())
            if not IsUnitInGroup(u,Units) then
                return false
            endif
            if u == End_sync then
                set T2 = TimerGetElapsed(Tim)
                call BJDebugMsg("end of sync : "+R2S(T2-T1) + " Player(" + I2S(id) + ") == " + I2S(Last_sync[id]))
                return false
            endif
            set Last_sync[id]=Last_sync[id]*10+GetUnitUserData(u)
            return false
        endfunction
       
        private function OnDeselect takes nothing returns boolean
            return false
        endfunction
       
        private function Init takes nothing returns nothing
            local trigger trig = CreateTrigger()
            local integer i = -1
            local integer data
            call TriggerRegisterPlayerSelectionEventBJ(trig,Player(0),true)
            call TriggerRegisterPlayerSelectionEventBJ(trig,Player(1),true)
            call TriggerAddCondition(trig,function OnSelect)
            set trig = CreateTrigger()
            call TriggerRegisterPlayerSelectionEventBJ(trig,Player(0),false)
            call TriggerRegisterPlayerSelectionEventBJ(trig,Player(1),false)
            call TriggerAddCondition(trig,function OnDeselect)
            call TimerStart(Tim,3600,false,null)
            loop
            exitwhen i == 10
            set i=i+1
                set U[i] = CreateUnit(Player(3),'hfoo',0,0,0)
                call SetUnitUserData(U[i],i)
                call GroupAddUnit(Units,U[i])
            endloop
            set End_sync = CreateUnit(Player(3),'hfoo',0,0,0)
            call GroupAddUnit(Units,End_sync)
           
        endfunction
       
    endlibrary
    .

    Code (vJASS):
    scope Test initializer Init

        private function Cond takes nothing returns boolean
            local integer i = S2I(GetEventPlayerChatString())
            call BJDebugMsg(R2S(TimerGetElapsed(Tim)))
            if GetLocalPlayer() == Player(1) then
                set i = 9
            endif
            call SyncInteger(i)
            return false
        endfunction
       
        private function Init takes nothing returns nothing
            local trigger trig = CreateTrigger()
            call TriggerRegisterPlayerChatEvent(trig,Player(0),"",false)
            call TriggerAddCondition(trig,Filter(function Cond))
        endfunction
    endscope

     
    Ofc there it's only a proof of concept, it doesn't handle negative numbers, and so one.
    But i don't know if it's really reliable, seems so, if you don't have to do more than 9 selections, included the end_sync unit, and then the result start to glitch.
    I didn't investigate more about that but i was not because of overflow (well, not really tested this option in fact), maybe a bug in my code or a selection hardcoded limitation.
     
  8. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,671
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Your implementation is probably better since it doesn't need to preload as many dummies. I have already implemented it in the next version :thumbs_up:

    Also,
    SelectUnit(u,true)
    would fail if the player's selection was full, correct? If so I was considering adding a constant to allow between
    ClearSelection
    and simply selecting/unselecting the unit, but then people's implementations would be different (some handling reselection, some not).

    EDIT: Figure I will just post the current version.

    Updated, 1.0.2. need to update documentation / changelog
     
    Last edited: May 3, 2016
  9. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    A player selection can be full ?
    I used the 10 base, simply because well, that's the one we use every day but if there is this 9 selection limitation you should use a bigger one to reduce the number of selections.
     
  10. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,839
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    I think you can only select 12 units at a time. afaik
     
  11. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    With the "pick every unit action" or whatever it spells, i suppose, but i don't think there is a limit with the single action SelectUnit.
    But meh, these kind of tests are up to the resource maker :thumbs_up:

    Seriously ClearSelection makes it very lame imho, especially because i suppose it is noticeable in game.
     
  12. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,671
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    It's not noticeable if you handle the re-selection, but yes it sucks I'll look into it more.

    EDIT:

    Updated, along with documentation.

    Code (Text):

    1.0.3

    [SyncInteger]
    - Replaced ClearSelection with SelectUnit(u, false)
    - SyncInteger handles re-selection on it's own now

    1.0.2

    [SyncInteger]
    - Dummy count reduced
    - System now breaks down a number and sends it in pieces
     
     
    Last edited: May 5, 2016
  13. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    I still highly doubt there is a practical limit number selection.
    Even if there is, i don't think that the actual code will handle that.

    My theory is that unit selections/deselections are delayed because they are handled each X seconds (or something like that). Ofc i can be wrong that's just a theory without any tests.
    So, when you will order several selections at a time and the player selection is full-1, then you will still reach the limit.

    You confirm that this random selection/deselection of unit is not noticeable ?
    For example if you spam orders with actual selected units.

    Why not just use FirstOfGroup instead of random unit ?
    It's no like you use every index of your unit array variable anyway.

    Also, it seems that if you select/deselect several times the same unit without any delay, like example you try to sync the value 1111, it takes less time than let's say 1234.
    I say that because for negative numbers you could use also SyncIntegerDummy[10]

    Very important, as said with my code the result was not correct if i tried to sync a number with more than 8 digits, like let's say 123456789, have you tested it ?
     
  14. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,671
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    What do you mean? If you try to select a unit when the queue is full it fails.

    The delay is fairly small and I've explained it more in my other thread. I've sent 32 integers in under a second with a 4 player map.

    But yes it's possible it's sent every X seconds with each selection in a group/list.

    The selections are done locally so yes there's nothing noticeable. Unless I'm missing something?

    Dumb mistake, I was updating this kind of late last night.

    Yeah I could use another dummy to signal a negative number.

    I guess I missed that. No I haven't tested it but regardless this implementation is better.

    If it fails at 8 digits and I can't fix it then I'll create a disclaimer and a debug message.

    Can always send huge numbers via a higher base string.

    EDIT: Some 8 digit numbers worked but higher ones didn't. Is that due to some inaccuracy in converting the base?
     
  15. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Let's say the limit is 32, the player has actually 32 unit selected.
    You remove one selection -> 31
    But you try to select 8 units -> 39

    Imagine you click as a pro gamer, many many mouse clicks and eventually keyboard in one second for a group of units selected.
    What i mean is that : the unit you select/deselect is always selected from the point of view of player ?
    All orders will be received ? None will fail because the unit was not selected during a short interval of time ?

    I mean for negative numbers you could use this very same unit at the begin of the sync.
    Because you will use it also at the end of sync, and that should mean the total sync should be faster than if you used a dedicaced unit for that (if what i've said is correct)

    Or just increase the base (more than hexadecimal) until you don't need more than 8 digit to cover all integers (if it doesn't need that much dummies, i have not calculated it)
    Or maybe better add constants to let the user choice ?

    EDIT : Maybe, as said i have not investigated about the bug, gogo debug text messages :thumbs_up:
     
  16. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,846
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    32bit int can actually store 10 numbers.

    But then you would need to select certain units up to 9 times, if the number is like 999999 you need literally 6*9 selections, if you want 10 dummy units.
     
  17. TriggerHappy

    TriggerHappy

    Code Moderator

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

    Code (Text):

    1.0.4

    [SyncInteger]
    - Support for negative numbers
    - Added IsPlayerSyncing
    - Added GetSyncedPlayerId
    - Minor improvements and fixes
     
     
  18. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Hmm no, in the worst case you will need 10 selections for each digit, one for negative and one for end of sync.
    So 12 for the lowest negative integer number : -2147483648

    If there is this 9 selection limitation, then we need to solve that :

    x⁽⁷⁾ -1 >= 2147483648.

    So we need a 22 base at least.
     
  19. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,839
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    Can't we use something, like converting the integer to an unsigned one, do the selection thingy, then convert it back?
     
  20. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    Yes, my bad, TriggerHappy already do that.
    So if there is this 9 selection limitation, the lowest base which can cover the full jass integer range is 20.
    20^8 - 1 > biggest jass integer (2147483648)