• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[System] Integer sync tool

Level 13
Joined
Sep 13, 2010
Messages
550
Yes, this is that what the title also says, an independent synchronization library for syncing integers. You also could see one that is made by Nestharus and also he used in his new File I/O.

JASS:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                             //
//     SYNCHRONIZATION LIBRARY                                                                                                 //
//         by Geries, also special thanks to Magtheridon96, without him I far not be finished.                                 //
//                                                                                                                             //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                             //
//     What is this?                                                                                                           //
//         This is a system which is made to synchronize local integers with other players at 100% success and that high       //
//         as we can reach within warcraft. Most users won't find any way to use this, but others who know preloaders and also //
//         know it's possibilities may find this useful. I don't say that this is the best or there will noone make better but //
//         right now this part of warcraft is unknown to almost every modder. I don't know if local file saving will have a    //
//         future, but if it will, then this can be something from the whole thing can get started.                            //
//                                                                                                                             //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                             /                                                               //
//     Pros: - Pretty fast, 2500 integers in 26.8 seconds      /     *Cons: - May make your scripts extremely laggy during a   //
//             online. This is more than Nestharus' max        /             synchronization. If you adjust the configuration  //
//             speed. ;p                                       /             reals you can make sync process slower, but       //
//           - No data loss because it does synchronization    /             you can keep your scripts under control. This     //
//             in more steps.                                  /             also highly reduces the chance of a disconnect.   //
//           - Everything can be converted into integers.*     /           - If a player loose connection for a while at the   //
//           - You can have multiple sync tables to use t      /             data verification part it has a high chance for   //
//             a time.                                         /             disconnection. You can reduce this threat with    //
//           - You can store unlimited integers.               /             adjusting intervals, especially the Verification  //
//                                                             /             timeout. It will make sync slower but also safer. //
//     *if you have a good string hash conversion stuff then   /                                                               //
//      you can store more than 4 characters into one integer  /     *We only saw that only a single event has few second      //
//      Nes: As I remember your record was somewhere at 250    /      delay during sync. You can guess what can happen to a    //
//      character per seconds. Now I have more than 370 :P     /      keyboard movement system...                              //
//                                                             /                                                               //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                               /                                             //
//     API:                                                                      /     Use:                                    //
//                                                                               /                                             //
//     static method init takes nothing returns sync                             / local sync mysync = sync.init( )            //
//     - Create an empty sync table.                                             /                                             //
//                                                                               /                                             //
//     method addVal takes string storeCode , integer storedVar returns nothing  / call mysync.AddVal( "MyVar" , 12345678 )    //
//     - Store your value into sync table. Using storeCode you will be able to   /                   OR                        //
//       retrieve your value after sync. You can only store value before         / set mysync["MyVar"] = 12345678              //
//       synchronizating.                                                        /                                             //
//                                                                               /                                             //
//     method readVal takes string storeCode returns integer                     / local integer i = mysync.readVal( "MyVar" ) //
//     - Retrieving your value after synchronizating. Only work after            /                   OR                        //
//       synchronizating!                                                        / local integer i = mysync["MyVar"]           //
//                                                                               /                                             //
//     method startSync takes player varOwner , trigger whenRdy returns nothing  / call mysync.startSync( Player( 0 ) , Trig ) //
//     - Start the synchronization. ALL OTHER PLAYER WILL HAVE SAME VALUES       /                                             //
//       AFTER SYNCHRONIZATION WITH THE PLAYER YOU SET! Set up a trigger which   /                                             //
//       will be executed when synchronization is complete and you can read your /                                             //
//       data. This process cannot be undone and can't be stopped!               /                                             //
//                                                                               /                                             //
//     method flush takes nothing returns nothing                                / call mysync.flush( )                        //
//     - Destroys sync table pernamently.                                        /                                             //
//                                                                               /                                             //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library SyncLib2

    // Configurations - you can make experimental varies of configures and maybe you can make it even more faster

    globals
        // After sending final verification data, the system basically do nothing, just let's time for the 
        // verification data to be synced. If a player can't broadcast the verification data in time will
        // get disconnected unless other player has the same data and was able to sync it succesfully. 
        // If you care more about safety just adjust this value
        private constant real Verification_Timeout = 4.5
        
        // Time delay between two data verification period. Changing only this will likely not affect anything.
        private constant real Verification_Period = 0.001
        
        // Time delay between two sync period. Reducing this value will result less succesful sync periods. 
        // This does not cause any threat.
        private constant real Sync_Period = 0.05
        
        // Sync table at sync is divided into smaller parts. These parts get synced at every sync period with
        // more or less success. Changing this value may result different speeds. This does not cause any threat.
        private constant integer Sync_Loop_Size = 50

        // Time between synchronization and the start of verification. Reducing this value will result
        // less succesful sync periods. This does not cause any threat.
        private constant real Sync_Timeout = 1.5
        
        // Enabling host var update will cause that after every sync turn all of the synchronizating player's
        // value stored will be updated. I noticed once that while synchronizating the sync player's data stored
        // in the gamecache just got zerofied. So because of this I put this option here. It takes pretty much performance
        // so just disable if you want to.
        private constant boolean Enable_Updating_Host_Var = true
    endglobals

    struct sync extends array

        private gamecache SyncA
        private integer SyncC
        private player SyncPlayer
        private trigger SyncTrigger
        private integer SyncPhase

        private static boolean Error
        private static player Player
        private static gamecache Cache

        private static integer LofS = 0
        private static integer FofS = 0
        private static integer LofR = 0
        private static sync array Schedule_A
        private static sync ON = 0
        private static integer SyncStatus
        private static constant hashtable VarStorage = InitHashtable( )
        
        // ENGINE
        
        private static method TSSD takes nothing returns nothing
            call TriggerSyncStart( )
        endmethod
        
        private static method TSRD takes nothing returns nothing
            call TriggerSyncReady( )
        endmethod
    
        private static method finalv takes nothing returns nothing
            if .Error then
                call StoreBoolean( .Cache , "A" , "0" , true )
                call TriggerSleepAction( 0.0 )
                call SyncStoredBoolean( .Cache , "A" , "0" )
            else
                call FlushStoredBoolean( .Cache , "A" , "0" )
                call TriggerSleepAction( 0.0 )
            endif
        endmethod
    
        private static method verify takes nothing returns nothing
            local integer i
            local boolean b = false
            if GetLocalPlayer( ) != .Player then
                if .SyncStatus < Sync_Loop_Size then 
                    set i = 0
                else
                    set i = .SyncStatus - Sync_Loop_Size
                endif
                loop
                    set i = i + 1
                    if not HaveStoredInteger( .Cache , "0" , I2S( i ) ) then
                        call StoreBoolean( .Cache , I2S( ( .SyncStatus - .ON.SyncC ) / Sync_Loop_Size ) , "0" , true )
                        set b = true
                        set .Error = true
                        exitwhen true
                    endif
                    exitwhen i == .SyncStatus
                endloop
            endif
            call ExecuteFunc( sync.TSSD.name )
            if b then
                call SyncStoredBoolean( .Cache , "A" , I2S( ( .SyncStatus - .ON.SyncC ) / Sync_Loop_Size ) )
            endif
            call ExecuteFunc( sync.TSRD.name )
        endmethod
        
        private static method update takes nothing returns nothing
            local integer i = .SyncStatus + 1000
            if GetLocalPlayer( ) == .Player then
                loop
                    set .SyncStatus = .SyncStatus + 1
                    call StoreInteger( .Cache , "0" , I2S( .SyncStatus ) , LoadInteger( .VarStorage , .ON , .SyncStatus ) )
                    exitwhen .SyncStatus == i or .SyncStatus == .ON.SyncC
                endloop
            else
                set .SyncStatus = .SyncStatus + 1000
                if .SyncStatus > .ON.SyncC then
                    set .SyncStatus = .ON.SyncC
                endif
            endif
            if .SyncStatus != .ON.SyncC then
                call ExecuteFunc( sync.update.name )
            endif
        endmethod
    
        private static method exec takes nothing returns nothing
            local integer i
            if .SyncStatus < Sync_Loop_Size then 
                set i = 0
            else
                set i = .SyncStatus - Sync_Loop_Size
            endif
            if GetLocalPlayer( ) == .Player then
                loop
                    set i = i + 1
                    call SyncStoredInteger( .Cache , "0" , I2S( i ) )
                    exitwhen i == .SyncStatus
                endloop
            else
                call FlushStoredBoolean( .Cache , "A" , I2S( ( .SyncStatus - .ON.SyncC ) / Sync_Loop_Size ) )
            endif
        endmethod
        
        private static method engine takes nothing returns nothing
            local integer i
            if .ON.SyncPhase == 2 then
            
                if .SyncStatus <= 0 then
                    if GetPlayerSlotState( .Player ) == PLAYER_SLOT_STATE_PLAYING then
                        set .ON.SyncPhase = 3
                        static if Enable_Updating_Host_Var then
                            call ExecuteFunc( sync.update.name )
                        else
                            set .SyncStatus = .ON.SyncC
                        endif
                        set .Error = false
                        call TimerStart( GetExpiredTimer( ) , Sync_Timeout , false , function sync.engine )
                    else
                        set .ON.SyncPhase = 6
                        call TimerStart( GetExpiredTimer( ) , 0.0 , false , function sync.engine )
                    endif
                    return
                else
                    call TimerStart( GetExpiredTimer( ) , Sync_Period , false , function sync.engine )
                endif
                call ExecuteFunc( sync.TSSD.name )
                call ExecuteFunc( sync.exec.name )
                call ExecuteFunc( sync.TSRD.name )
                set .SyncStatus = .SyncStatus - Sync_Loop_Size
                
            elseif .ON.SyncPhase == 3 then
                
                if .SyncStatus <= 0 then
                    if GetPlayerSlotState( .Player ) == PLAYER_SLOT_STATE_PLAYING then
                        set .ON.SyncPhase = 4
                        call ExecuteFunc( sync.TSSD.name )
                        call ExecuteFunc( sync.finalv.name )
                        call ExecuteFunc( sync.TSRD.name )
                        call TimerStart( GetExpiredTimer( ) , Verification_Timeout , false , function sync.engine )
                        set .Error = false
                    else
                        set .ON.SyncPhase = 6
                        call TimerStart( GetExpiredTimer( ) , 0.0 , false , function sync.engine )
                    endif
                    return
                else
                    call TimerStart( GetExpiredTimer( ) , Verification_Period , false , function sync.engine )
                endif
                call ExecuteFunc( sync.verify.name )
                set .SyncStatus = .SyncStatus - Sync_Loop_Size

            elseif .ON.SyncPhase == 4 then
            
                if not .Error then
                    set .Error = GetStoredBoolean( .Cache , "A" , "0" )
                    set .SyncStatus = .ON.SyncC
                    if not .Error then
                        set .ON.SyncPhase = 5
                        call TriggerExecute( .ON.SyncTrigger )
                        if .FofS != 0 then
                            set .ON = .Schedule_A[.FofS]
                            set .FofS = .FofS + 1
                            if .FofS > .LofS then
                                if LofR != 0 then
                                    set .FofS = 1
                                    set .LofS = .LofR
                                    set .LofR = 0
                                else
                                    set .FofS = 0 
                                    set .LofS = 0
                                endif
                            endif
                            set .SyncStatus = .ON.SyncC
                            set .Player = .ON.SyncPlayer
                            set .Cache = .ON.SyncA
                            call TimerStart( GetExpiredTimer( ) , 0.0 , false , function sync.engine )
                        else
                            call DestroyTimer( GetExpiredTimer( ) )
                            set .ON = 0
                        endif
                        return
                    endif
                endif
                if GetStoredBoolean( .Cache , "A" , I2S( ( .SyncStatus - .ON.SyncC ) / Sync_Loop_Size ) ) then
                    call ExecuteFunc( sync.TSSD.name )
                    call ExecuteFunc( sync.exec.name )
                    call ExecuteFunc( sync.TSRD.name )
                    call TimerStart( GetExpiredTimer( ) , Sync_Period , false , function sync.engine )
                else
                    call TimerStart( GetExpiredTimer( ) , 0.0 , false , function sync.engine )
                endif
                set .SyncStatus = .SyncStatus - Sync_Loop_Size
                if .SyncStatus <= 0 then
                    if GetPlayerSlotState( .Player ) == PLAYER_SLOT_STATE_PLAYING then
                        set .ON.SyncPhase = 3
                        static if Enable_Updating_Host_Var then
                            call ExecuteFunc( sync.update.name )
                        else
                            set .SyncStatus = .ON.SyncC
                        endif
                        set .Error = false
                        call FlushStoredMission( .Cache , "A" )
                        call TimerStart( GetExpiredTimer( ) , Sync_Timeout , false , function sync.engine )
                    else
                        set .ON.SyncPhase = 6
                        call TimerStart( GetExpiredTimer( ) , 0.0 , false , function sync.engine )
                    endif
                endif
                        
            elseif .ON.SyncPhase == 7 then
            
                call FlushGameCache( .Cache )
                call TimerStart( GetExpiredTimer( ) , 1.5 , false , function sync.engine )
                set .ON.SyncPhase = 8
            
            elseif .ON.SyncPhase == 8 then

                set .ON.SyncPhase = 0
                set .ON.SyncPlayer = null
                set .ON.SyncTrigger = null
                set .ON.SyncC = 0
                set .ON.SyncA = null
                set i = LoadInteger( .VarStorage , -1 , 0 ) + 1
                call SaveInteger( .VarStorage , -1 , 0 , i )
                call SaveInteger( .VarStorage , -1 , i , .ON )
                if .FofS != 0 then
                    set .ON = .Schedule_A[.FofS]
                    set .FofS = .FofS + 1
                    if .FofS > .LofS then
                        if LofR != 0 then
                            set .FofS = 1
                            set .LofS = .LofR
                            set .LofR = 0
                        else
                            set .FofS = 0 
                            set .LofS = 0
                        endif
                    endif
                    set .SyncStatus = .ON.SyncC
                    set .Player = .ON.SyncPlayer
                    set .Cache = .ON.SyncA
                    call TimerStart( GetExpiredTimer( ) , 0.0 , false , function sync.engine )
                else
                    call DestroyTimer( GetExpiredTimer( ) )
                    set .ON = 0
                endif

            elseif .ON.SyncPhase == 6 then
            
                call FlushGameCache( .Cache )
                call TimerStart( GetExpiredTimer( ) , 1.5 , false , function sync.engine )
                call FlushChildHashtable( .VarStorage , .ON )
                set .ON.SyncPhase = 9
            
            elseif .ON.SyncPhase == 9 then
            
                set .ON.SyncA = InitGameCache( "Sync" + I2S( .ON ) )
                set .ON.SyncPhase = 5
                call TriggerExecute( .ON.SyncTrigger )
                if .FofS != 0 then
                    set .ON = .Schedule_A[.FofS]
                    set .FofS = .FofS + 1
                    if .FofS > .LofS then
                        if LofR != 0 then
                            set .FofS = 1
                            set .LofS = .LofR
                            set .LofR = 0
                        else
                            set .FofS = 0 
                            set .LofS = 0
                        endif
                    endif
                    set .SyncStatus = .ON.SyncC
                    set .Player = .ON.SyncPlayer
                    set .Cache = .ON.SyncA
                    call TimerStart( GetExpiredTimer( ) , 0.0 , false , function sync.engine )
                else
                    call DestroyTimer( GetExpiredTimer( ) )
                    set .ON = 0
                endif
                
            endif
        endmethod
        
        // ENDENGINE
        
        method operator []= takes string storeCode , integer storedVar returns nothing
            debug if this.SyncPhase != 0 then
                if this.SyncPhase == 1 then
                    set storeCode = storeCode + "x"
                    if GetStoredInteger( this.SyncA , storeCode , "0" ) == 0 then
                        set this.SyncC = this.SyncC + 1
                        call SaveInteger( .VarStorage , this , this.SyncC , storedVar )
                        call StoreInteger( this.SyncA , storeCode , "0" , this.SyncC )
                        if storedVar != 0 then
                            call StoreInteger( this.SyncA , "0" , I2S( this.SyncC ) , storedVar )
                        endif
                    debug else
                        debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: StoreCode already used.|r" )
                    endif
                debug else
                    debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: You can't store new value to this table.|r" )
                endif
            debug else
                debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: Sync table does not exist|r" )
            debug endif
        endmethod
        
        method operator [] takes string storeCode returns integer
            debug if this.SyncPhase != 0 then
                if this.SyncPhase == 5 then
                    if GetLocalPlayer( ) != this.SyncPlayer then
                        return GetStoredInteger( this.SyncA , "0" , I2S( GetStoredInteger( SyncA , storeCode + "x" , "0" ) ) )
                    else
                        return LoadInteger( .VarStorage , this , GetStoredInteger( this.SyncA , storeCode + "x" , "0" ) )
                    endif
                debug else
                    debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: You can't read table now.|r" )
                endif
            debug else
                debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: Sync table does not exist|r" )
            debug endif
            return 0
        endmethod

        method startSync takes player varOwner , trigger executeOnFinish returns nothing
            debug if this.SyncPhase != 0 then
                if this.SyncPhase == 1 then
                    if GetPlayerSlotState( varOwner ) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController( varOwner ) == MAP_CONTROL_USER then
                        set this.SyncPhase = 2
                        set this.SyncPlayer = varOwner
                    else
                        set this.SyncPhase = 6
                    endif
                    set this.SyncTrigger = executeOnFinish
                    if .ON == 0 then
                        set .ON = this
                        set .SyncStatus = this.SyncC
                        set .Player = varOwner
                        set .Cache = this.SyncA
                        call TimerStart( CreateTimer( ) , 0.0 , false , function sync.engine )
                    else
                        if .LofR > 0 then
                            set .LofR = .LofR + 1
                            set .Schedule_A[.LofR] = this
                        else
                            if LofS != 8191 then
                                if .LofS == 0 then
                                    set .FofS = 1
                                endif
                                set .LofS = .LofS + 1
                                set .Schedule_A[.LofS] = this
                            else
                                set .LofR = .LofR + 1
                                set .Schedule_A[.LofR] = this
                            endif
                        endif
                    endif
                debug else
                    debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: Can't clear sync table now.|r" )
                endif
            debug else
                debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: Sync table does not exist|r" )
            debug endif
        endmethod
        
        method addVal takes string storeCode , integer storedVar returns nothing
            debug if this.SyncPhase != 0 then
                if this.SyncPhase == 1 then
                    set storeCode = storeCode + "x"
                    if GetStoredInteger( this.SyncA , storeCode , "0" ) == 0 then
                        set this.SyncC = this.SyncC + 1
                        call SaveInteger( .VarStorage , this , this.SyncC , storedVar )
                        call StoreInteger( this.SyncA , storeCode , "0" , this.SyncC )
                        if storedVar != 0 then
                            call StoreInteger( this.SyncA , "0" , I2S( this.SyncC ) , storedVar )
                        endif
                    debug else
                        debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: StoreCode already used.|r" )
                    endif
                debug else
                    debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: You can't store new value to this table.|r" )
                endif
            debug else
                debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: Sync table does not exist|r" )
            debug endif
        endmethod
        
        method readVal takes string storeCode returns integer
            debug if this.SyncPhase != 0 then
                if this.SyncPhase == 5 then
                    if GetLocalPlayer( ) != this.SyncPlayer then
                        return GetStoredInteger( this.SyncA , "0" , I2S( GetStoredInteger( SyncA , storeCode + "x" , "0" ) ) )
                    else
                        return LoadInteger( .VarStorage , this , GetStoredInteger( this.SyncA , storeCode + "x" , "0" ) )
                    endif
                debug else
                    debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: You can't read table now.|r" )
                endif
            debug else
                debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: Sync table does not exist|r" )
            debug endif
            return 0
        endmethod
    
        method flush takes nothing returns nothing
            debug if this.SyncPhase != 0 then
                if this.SyncPhase == 1 or this.SyncPhase == 5 then
                    set this.SyncPhase = 7
                    if .ON == 0 then
                        set .ON = this
                        set .Cache = this.SyncA
                        call TimerStart( CreateTimer( ) , 0.0 , false , function sync.engine )
                    else
                        if .LofR > 0 then
                            set .LofR = .LofR + 1
                            set .Schedule_A[.LofR] = this
                        else
                            if LofS != 8191 then
                                if .LofS == 0 then
                                    set .FofS = 1
                                endif
                                set .LofS = .LofS + 1
                                set .Schedule_A[.LofS] = this
                            else
                                set .LofR = .LofR + 1
                                set .Schedule_A[.LofR] = this
                            endif
                        endif
                    endif
                debug else
                    debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: Can't clear sync table now.|r" )
                endif
            debug else
                debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: Sync table does not exist|r" )
            debug endif
        endmethod
        
        static method init takes nothing returns sync
            local sync i = LoadInteger( .VarStorage , -1 , 0 )
            if i > 0 then
                call SaveInteger( .VarStorage , -1 , 0 , i - 1 )
                set i = LoadInteger( .VarStorage , -1 , i )
            else
                set i = LoadInteger( .VarStorage , -1 , -1 ) + 1
                if i != 8192 then
                    call SaveInteger( .VarStorage , -1 , -1 , i )
                else
                    debug call DisplayTimedTextToPlayer( GetLocalPlayer( ) , 0 , 0 , 60 , "|cffff0000[Synchronization]: You reached sync table limit.|r" )
                    return 0
                endif
            endif
            set i.SyncA = InitGameCache( "Sync" + I2S( i ) )
            set i.SyncC = 0
            set i.SyncPhase = 1
            return i
        endmethod
        
    endstruct
    
endlibrary
Everything that you might read is in the documentation, I way not like repeating myself :p And before you start suggesting to put comments everywhere I must note I will likely not do that, if you want to know anything about it just ask me here.

YES, It works online, multiplayer tested. If you got desync problems, and also followed the steps to debug it but you still have problem please contact me here or in PM/VM.

Update:
1.01 -> Setting Queue to "FiFo"

Once we made 1250 character per second test with no loss, except Magtheridon got disconnected :D
 

Attachments

  • GsyncLib.w3x
    28.5 KB · Views: 97
Last edited:
Hi there just want to say this looks interesting though I don't know why it needs to be so complex just to sync something. I guess this should show you how much I still must learn

Well, the native used to sync strings in Gamecaches doesn't work, so we need a system like this to do the syncing.

One option is to take a string, assume it's an integer of base 98 (All the alphabet, the space character,
all the numberals, the symbols, and \n, \b, \t. Then you would take that integer and convert it to base2,
thus ending up with a huge integer. You would sync booleans (1s and 0s) to other players so that their
computers would do the base conversion back to base98 and get the string.

Of course, this just might be incredibly slow :p
 
Level 13
Joined
Sep 13, 2010
Messages
550
Hi there just want to say this looks interesting though I don't know why it needs to be so complex just to sync something. I guess this should show you how much I still must learn

The reason for having such difficult code is that, sync natives are not instant, cannot be trusted, especially at syncing numerous data. And you know if a player have only an integer missing that can ruin the entire game. If you need to sync only one or ten value it may not require such library, but you know systems/scopes/libraries here made to not apply it for only a few data, for hundreds instead. And if you are trying to sync up few hundred/thousand then you may have to check if everything is synced and then sync again which failed. At the end you need to make somewhat system like this. However I am still really interested in an easy way to sync data quickly and easily if there is any.

We always learn new things.

It can be tested, believe in probabilities and make something like 10000 serious tests.
Or 1000 times less if you have a life, but the result would be pointless because not enough accurate.

It takes same time. If there is any difference then that is minor and unnoticeable.
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Once we made 1250 character per second test with no loss, except Magtheridon got disconnected :D

if he got disconnected, then the code lagged behind. You gotta fix that =). I know I fixed desyncs in mine.

Also, keep in mind that my testing was dealing with WAY more than 2600 integers. I was working with 20,000 and 200,000 character tests. 20,000 characters holds a lot of integers in it. If you go with maxes, 20,000 chars is 5000 integers. I don't remember how fast that test was, but it was fast.


Also, I can tell you the reason that your code desyncs. You are using the algorithm I initially used. That algorithm is unsafe as a single player may get locked inside of the loop, thus causing a desync. You can't sync booleans to check when to exit the loop as the one sending the boolean will exit the loop before the one receiving and so on :\.


You need to have guaranteed no desync. Yes, I love the algorithm you are using, that was the one I originally used, but again it's flawed.

edit
Also, you should be taking a table of data and just syncing that table up. There are reasons for this =). The current method for taking data is slow and crappy ;p.\

edit
Actually, don't take a table. Use a buffer instead of labels. The person is going to know the order they are writing the data in. No resource ever uses labels because they are stupid >.>. They may seem cool, but they are one of those cool but useless things everybody talks about all the time. Your labels just slow down ur resource, complicate the API, and make it 100% unusable with specialized streams of data like my File I/O thing. Moving to a buffer allows specialized resources to use this, simplifies the API, increases the speed, and is just smart. If you look at save/load resources, none of them ever use labels, they always use a buffer. There is a reason for this...
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
While i agree that would be terribly overkill i'm wondering if using dummies units (as bytes) and the SelectUnit stuff in localized code blocks wouldn't be more efficient, or at least faster.

I know it will work, since i've already used it.
However i used it only to sync a boolean value.

Basically you use a base X where X is the number of the units, you also need one more unit to state that's the end of the sync process.
Then you simply select them with GetLocalPlayer in the needed order, the select event will fire on each computer and you can get which computer selected the unit with GetTriggeringPlayer.
Or you could also use the player X select event and X triggers.
Also, if you remove the unit selection on select event the player shouldn't notice it. But i'm not totally sure about this last one.

EDIT :

Here is a proof of concept, it lacks many things but the sample should work as intented :

JASS:
library SyncInt initializer init

    globals
        private constant integer DUMMY_RAWCODE = 'hfoo'
        private constant player DUMMY_PLAYER = Player(14) // maybe Player() doesn't work on global definition, if it's the case don't use a constant and set the variable inside the initializer
        private unit array Dummy
        private unit Dummy_endSync
        private integer array Data = 0
        private integer N = 0
    endglobals
    
    function SyncIntFromPlayer takes player whichPlayer, integer whichInt returns nothing
        local integer n
        local integer x
        
        if GetLocalPlayer() == whichPlayer then
        
            loop
            exitwhen whichInt == 0
            set x = whichInt
            set n = 1
        
                loop
                    set x = x/n
                    if x == 0 then
                        set x = whichInt*10/n
                        call SelectUnit(Dummy[x],true)
                        call SelectUnit(Dummy[x],false)
                        set whichInt = whichInt - x*n/10
                        exitwhen true
                    endif
                    set n = n*10
                endloop
            
            endloop
            
            call SelectUnit(Dummy_endSync,true)
            call SelectUnit(Dummy_endSync,false) 
        
        endif
        
    endfunction
    
    private function HandleSync takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local player p = GetTriggerPlayer()
        local integer id = GetPlayerId(p)
        local integer i = 0

        if Dummy[GetUnitUserData(u)] != u then // hmm i'm wondering what will happen if the index is < 0 like with the unit Dummy_endSync, it just acts like it's 0 ?
            return
        endif
        
        set Data[N] = GetUnitUserData(u)
        set N = N+1
        
        if u == Dummy_endSync then
        
            set N = N-2
            loop
            exitwhen N < 0
                set i = i+Data[N]*R2I(Pow(10,N))
            set N = N-1
            endloop
            set N = 0
            
            call DisplayTimedTextFromPlayer(p,0,0,666,"data synced from player %s == " + I2S(i))
        endif
        
    endfunction
    
    private function init takes nothing returns nothing
        local integer i = 0
        local trigger trig = CreateTrigger()
        
        loop
        exitwhen i > 10
            set Dummy[i] = CreateUnit(DUMMY_PLAYER,DUMMY_RAWCODE,999999,999999,0) // i don't remember how it's handled, if the unit is created outside the playable map but not outside the entire map that should be fine
            call SetUnitUserData(Dummy[i],i) // of course plz care about unit indexers it's just a quick sample
        set i = i+1
        endloop
        set Dummy_endSync = CreateUnit(DUMMY_PLAYER,DUMMY_RAWCODE,999999,999999,0)
        call SetUnitUserData(Dummy[i],-1)
        set i = 0
        loop
        exitwhen i == 12
            call TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_UNIT_SELECTED)
            call TriggerAddCondition(trig,Filter(function HandleSync))
        set i = i+1
        endloop
        set trig = null
    endfunction
    
endlibrary

Note that i've written the code on jasscraft, and i have not tested it, nor checked it.
Also, i used the decimal base for more simplicity, but it should be more efficient with higher bases, because it will have less unit selections and then less select event fire.
And if you want to handle negative integers as well, then an extra dummy unit would be needed.

EDIT : And oh well i've just realized that in this sample if you try to sync let's say "5687", it would display it in the reverse order "7865", but meh i'm sure you're enough smart to figure yourself how you can fix it :p
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
That would be slower... it increases the number of syncs for each integer to 32 from 1. It also does 32 triggers for every integer : \.

Also, this lib spams gamecaches. That is definitely not ok.

-> set .ON.SyncA = InitGameCache( "Sync" + I2S( .ON ) )

edit
Also, I don't know why your thing works, but it isn't because of this
JASS:
call ExecuteFunc( sync.TSSD.name )
                    call ExecuteFunc( sync.exec.name )
                    call ExecuteFunc( sync.TSRD.name )

That is pointless. The Start/Ready calls only sync up the threads they are called in. They won't magically sync up threads all over wc3. If you don't believe me, try executing a function with sync ready in it and then displaying "Ready" after the native is called. Display "Thread Terminated" in the thread that executed the function. See which message pops up first.

Thread A:
-> execute Thread B
-> display message

Thread B:
-> call sync ready
-> display message

Thread A's message will be display before B's, thus proving my point.

TriggerSyncReady sends out a packet and waits until all players receive that packet. It ensures that no player will advance beyond that call until all players are there. SyncStoredInteger sends out a piece of data. TriggerSyncReady and SyncStored integer have nothing to with each other. It only sometimes works because TriggerSyncReady is sent after SyncStoredInteger, meaning that TriggerSyncReady should fire off after the SyncStoredInteger does. However, this is not always the case, which is why there can be sync failures. Notice now that what you are doing is pointlessly putting thread B to sleep until all players are sync'd up on thread B. Thread B then immediately terminates. All the while, thread A has moved on. If you have verification in thread A immediately after executing thread B, your verification is going to run before thread B is even sync'd up.

TriggerSleepAction does the packet action followed by a delay. The delay is modified based on how much the action has already been waiting. If one player has been waiting for 3 seconds and the TSA is 7 seconds, player 2 will only have a 4 second TSA so as to stay sync'd up with player 1. However, TSA is buggy. A lot of the time, it will only wait for the players to sync up or for the delay to occur. If the TSA has already gone for 5 seconds and its only 5 seconds, player 1 will just move on and player 2 will have a 0 delay TSA. A lot of the time TSA will not even do its delay : |, which is why when I used TSAs of 1000, it pretty much waited for the players to sync up. However, a TSA is tantamount to a TriggerSyncReady with a max delay on it.

Now, I have yet to figure out why your thing works, but a lot of your code is just totally messed up and broken : \. You are doing a lot of pointless operations.

edit
Also, my max tested speed was 100 integers per second, which would be 2680 syncs in 26.8 seconds vs your 2500, so don't give me that garbage ;o.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Nestharus said:
That would be slower... it increases the number of syncs for each integer to 32 from 1. It also does 32 triggers for every integer : \.

Well, that all depends the size of the integer which need to be synced and the base (basically number of dummies units) which is used.
I'm not even sure it depends the number of unit selections, i mean, for sure the more unit selection will be done, the less efficient will be the script.
But about the time to sync by itself, it could be about the same, maybe the jass vm handle unit selection each X frames of the game or something like that.

I would also say that would be slower, but it's worth a test, at least it doesn't use any TSA or such ugly work around, that would be 100 % reliable.

Now as stated using dummies units only for that is really overkill, since units are the most interactive, the least abstractive thing ever in jass.

EDIT : But i'm not sure you were talking about my sample (i'm just too lazy for checking geries' code)
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Well, i don't believe that much in sync natives efficiency, or even reliability.
Don't take me wrong i really hate my code, if we could simply use the sync string native function then i wouldn't even suggest this in the very first place.

Nothing is wrong to test my code, if it sucks hard then rip. (i can't test it myself)
EDIT : Oh and just to be clear if it's a valid solution, don't care about credits, "stealing" code or anything, i won't submit it myself, it's just too much work :p

Also, i'm not sure why you would sync large integers rather than small ones, or even many integers within a short delay, have you a concrete example (not just because you can) ?
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok, this entire resource is now outdated. Came up with an incredible syncing algorithm. So far, I have managed to do 2500 syncs in 19 seconds with no errors. I did it in one burst of 2500. It is guaranteed to never ever desync and always have a sync rate of 100%.

edit
syncs up many integers within short delay for save/load codes ofc. However, reasonable size only goes up to 10,000 syncs, so you are still fairly limited. This is larger than the SC2 bank tho : ). Furthermore, SC2 bank has chance to desync with larger sizes whereas this doesn't ;o.

edit
Ok, the below is literally a big joke. It took me a while to write, so I don't want to get rid of it ;p. It is true that this does point at actual methods located within this resource and that those methods do what this joke makes them out to do... but it's not like I'm making fun of geries for doing the below. I mean, people can make fun of me for doing the Concat thing and so on ;o. I mean... let's be serious. Just what was geries thinking? He has yet to defend the below with anything sensible. I grilled him for an hour and he just responded that it magically juggles threads around and automatically stops the one he is thinking of because his code is magical and is filled with unicorns.

The horror D:

JASS:
private static method TSSD takes nothing returns nothing
            call TriggerSyncStart( )
        endmethod
        
        private static method TSRD takes nothing returns nothing
            call TriggerSyncReady( )
        endmethod

Response
JASS:
function StartWaiting takes nothing returns nothing
    //this function ensures that everyone gets to this line, but it does it
    //for no real reason, lol..
    call TriggerSyncStart()
endfunction
function Wait takes nothing returns nothing
    //this function's purpose is to be called so that it can go to sleep and terminate
    //it has no bearing on anything else. It's just here to be pretty.
    call TriggerSyncReady()
endfunction
function DoSomething takes nothing returns nothing
    call DoNothing() //shebang, fooled you
endfunction
function Start takes nothing returns nothing
    //imma make this function wait for itself
    call ExecuteFunc("StartWaiting")

    //now while the above is waiting, imma do something epic
    //and make this function wait for itself using a different native
    //aren't I cool? I will now have 2 threads running
    call ExecuteFunc("Wait")

    //now while the above threads are sleeping waiting to sync up so that
    //they can simply terminate, imma sing a song
    call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,/*
    */"La la la, I love calling pointless things, lalalalala")

    //and now to end this pointless exercise, I am finally going to do something. Again,
    //none of the previous calls have helped in any way towards accomplishing this
    //something
    call DoSomething()
endfunction

edit
oh shiz, I forgot the acronyms
JASS:
//the most useless function you will ever see in your life, acronym
function TMUFYWESIYL takes nothing returns nothing
    //the below is written in invisible ink to make the comment more challenging to read
    /*
        L     O     L
    */
    call TriggerSyncStart()
endfunction
//the most useless function you will ever see in your life Version 2!
function TMUFYWESIYLV2 takes nothing returns nothing
    //the below is random letters. Make sense of it as you will
    /*
        zofi
        disjoisdgjorwppsfqm
        
        
        dosfkpql
    */
    call TriggerSyncReady()
endfunction
//there just must be a reason for this function. After all, it has a spiffy
//name and is surrounded by question marks. It will remain a mystery.
function DS takes nothing returns nothing
    call DoNothing()     //??????????
endfunction
function Start takes nothing returns nothing
    //??? ? .. _!! KKLOO +FA ZZ???
    call ExecuteFunc("TMUFYWESIYL")

    //i'm just too lazy to do the comment for this one, so imma do a smily
    /*
        :D
    */
    call ExecuteFunc("TMUFYWESIYLV2")

    //my attempt at being eager
    /*
          e
          g


          a

          r
    */
    call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,0.0000,/*
    */"L a, I ep oin less i, lll l l") //much of this text was censored by bliz for being too lolz

    //again, the mysterious function that can only be matched by its even more mysterious
    //comments. You dare not delete it.
    //?????????????????????
    /*
        ??????????????????????????
        ??????????????????????????
    */
    call DS() //??????
endfunction
 
Last edited:
JASS:
//the most useless function you will ever see in your life, acronym
function TMUFYWESIYL takes nothing returns nothing
    //the below is written in invisible ink to make the comment more challenging to read
    /*
        L     O     L
    */
    call TriggerSyncStart()
endfunction
//the most useless function you will ever see in your life Version 2!
function TMUFYWESIYLV2 takes nothing returns nothing
    //the below is random letters. Make sense of it as you will
    /*
        zofi
        disjoisdgjorwppsfqm
        
        
        dosfkpql
    */
    call TriggerSyncReady()
endfunction
//there just must be a reason for this function. After all, it has a spiffy
//name and is surrounded by question marks. It will remain a mystery.
function DS takes nothing returns nothing
    call DoNothing()     //??????????
endfunction
function Start takes nothing returns nothing
    //??? ? .. _!! KKLOO +FA ZZ???
    call ExecuteFunc("TMUFYWESIYL")

    //i'm just too lazy to do the comment for this one, so imma do a smily
    /*
        :D
    */
    call ExecuteFunc("TMUFYWESIYLV2")

    //my attempt at being eager
    /*
          e
          g


          a

          r
    */
    call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,0.0000,/*
    */"L a, I ep oin less i, lll l l") //much of this text was censored by bliz for being too lolz

    //again, the mysterious function that can only be matched by its even more mysterious
    //comments. You dare not delete it.
    //?????????????????????
    /*
        ??????????????????????????
        ??????????????????????????
    */
    call DS() //??????
endfunction

funny-barack-michelle-obama-face.jpg
 
Level 13
Joined
Sep 13, 2010
Messages
550
but it's not like I'm making fun of geries for doing the below.
Nor I want to hurt your feeling but:

Yes, this is truly reflects your personality. But finally I got answers to several things that interested me.

By the way,

JASS:
// this can be something from the whole thing can get started.
Yes my Good Lord, I completed my task!

Bribe, you may close this thread.
 
Top