• 🏆 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!

Easiest way of syncing a boolean?

Status
Not open for further replies.
Level 12
Joined
Mar 13, 2012
Messages
1,121
How about
JASS:
native SyncStoredBoolean        takes gamecache cache, string missionKey, string key returns nothing

Take a look here for a basic guide.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
There are 3 ways to synchronize local values. What you need to keep in mind is that the synchronization is not synchronous. You need to wait until the synchronization has occurred. There is a library out there called Network that has a very simple struct in it called Packet. This'll do what you want with minimal effort. SyncStoredBoolean will require something like the Thread library.

Here is a demo

JASS:
local Packet packet
local integer var = 0

if (GetLocalPlayer() == Player(0)) then
    set var = 5 //set variable to 5 for player 0
elseif (GetLocalPlayer() == Player(6)) then
    set var = 9 //set variable to 9 for player 6
endif

//if the variable isn't 0, synchronize that variable
set packet = Packet.broadcast(var, var != 0)

//packet[0] will be 5
//packet[6] will be 9
//everything else will be 0

if (packet[0] == 5) then
    //true
endif

if (packet[6] == 9) then
    //true
endif

if (packet.has(0)) then
    //true
endif

if (packet.has(4)) then
    //false
endif

Here is the link

https://github.com/nestharus/JASS/blob/master/jass/Systems/Network/Network.w3x?raw=true

Here is the Packet API

JASS:
/*
************************************************************************************
*
*   struct Packet extends array
*
*       Description
*       -----------------------
*
*           This is used to synchronize single integers. It can synchronize single integers
*           from multiple players at once.
*
*       Creators/Destructors
*       -----------------------
*
*           static method broadcast takes integer data, boolean source returns Packet
*               -   Synchronize integer data
*               -   if source is true, data will be synchronized from the local player
*
*               Examples
*                   The below will send 6 to every other player in the game
*                   Player 0 is broadcasting the number, but it may be multiple players
*                   ------------------------------------------------------------------------------
*                   -   broadcast(6, GetLocalPlayer() == Player(0))
*
*                   The below will send var to every other player in the game
*                   Var is sent from players where var is not equal to 0, meanning that multiple values
*                   of var may be synchronized
*                   ------------------------------------------------------------------------------
*                   -   broadcast(var, var != 0)
*
*           method destroy takes nothing returns nothing
*
*       Operators
*       -----------------------
*
*           method operator [] takes integer playerId returns integer
*               -   Reads synchronized value that was sent from playerId
*
*       Methods
*       -----------------------
*
*           method has takes integer playerId returns boolean
*               -    This will return true if a value was sent from playerId
*/
 
If you need to sync just one boolean, you would do something along these lines (it is the method described in Xarian's post):
JASS:
library SyncExample initializer Init

    globals
        private gamecache cache
    endglobals

    function SyncBoolean takes player fromPlayer, boolean value returns boolean
        local string missionKey = I2S(GetPlayerId(fromPlayer))

        /*
            Store the data from `fromPlayer` then sync it
        */
        if GetLocalPlayer() == fromPlayer then
            call StoreBoolean(cache, missionKey, "b", value)
            call SyncStoredBoolean(cache, missionKey, "b")
        endif

        /*
            Pause the thread until the data is synchronized
        */
        call TriggerSyncReady()
        return GetStoredBoolean(cache, missionKey, "b")
    endfunction

    private function Init takes nothing returns nothing
        set cache = InitGameCache("SyncExample")
    endfunction

endlibrary

player fromPlayer -> This is the player who has the correct value. You will sync his value to the rest of the players.
boolean value -> This is the boolean that you want to sync to other players.
returns boolean -> Returns the synced value

Note that the place you're calling from has to be a trigger action (iirc). TriggerSyncReady() won't work if you're calling it from within a timer callback, group callback, etc. It *might* work with trigger conditions, but I haven't tested it. In fact, I haven't tested it thoroughly at all. I just know that it works on multiplayer (tested) and it doesn't desync. Xarian's description seemed credible enough, and hey--it is practically the same method ppl have used for years before that, just without the TriggerSyncStart.

And feel free to inline it assuming you only need it for one thing. I assume you intend to sync just one value from one player to all other players. If you don't know which player has the sync value, I recommend using Nestharus' Network library.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
This is what I have so far...

Unfortunately, the game still desyncs... I don't know what I'm doing wrong. The only thing I need is a boolean state in HAS_EXTENSION_PACK[0-5] that indicates if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 for that player.
JASS:
globals
    gamecache gc = null
endglobals

function Trig_Initialization_3_Actions takes nothing returns nothing
    local boolean b = false
    local string s = ""
    local integer i = 0
    call FlushGameCache(InitGameCache("gaiascache"))
    set gc = InitGameCache("gaiascache")
    loop
        exitwhen i > 5
        set b = false
        set s = I2S(i)
        if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 then
            set b = true
        endif
        if GetLocalPlayer( ) == Player(i) then
             call StoreBoolean( gc , "A" , s , b )
             call SyncStoredBoolean( gc , "A" , s )
        endif
        set i = i + 1
    endloop
    call TriggerSyncReady()
    set i = 0
    loop
        exitwhen i > 5
        set s = I2S(i)
        set HAS_EXTENSION_PACK[i] = GetStoredBoolean(gc, "A", s)
        set i = i + 1
    endloop
endfunction

//===========================================================================
function InitTrig_Initialization_3 takes nothing returns nothing
    set gg_trg_Initialization_3 = CreateTrigger(  )
    call TriggerRegisterTimerEventSingle( gg_trg_Initialization_3, 1.50 )
    call TriggerAddAction( gg_trg_Initialization_3, function Trig_Initialization_3_Actions )
endfunction
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
You didn't read my post. Purge didn't either.

What you need to keep in mind is that the synchronization is not synchronous. You need to wait until the synchronization has occurred.

TriggerSyncReady does not wait until synchronization has occurred for those values.

This does

https://github.com/nestharus/JASS/blob/master/jass/Systems/Thread/script.j#L72

method sync takes nothing returns nothing
method wait takes nothing returns nothing

JASS:
local Thread thread = Thread.create()
call SyncStoredBoolean(...)
call thread.sync()
call thread.wait()
call thread.destroy()

// done, we're synchronized

It'd be cool if you could do this (I just realized)

call Thread.create().sync().wait().destroy()

lol


It's easiest to just use Network.

All you need are WorldBounds, Thread, and Network.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Alright, ill try your system.
However, I have no clue how to use it... the example in the system's header looks like complete gibberish to me.

This is what I have now:
JASS:
globals
    gamecache gc = null
endglobals

function Trig_Initialization_3_Actions takes nothing returns nothing
    local boolean b = false
    local string s = ""
    local integer i = 0
    local Thread thread = Thread.create()
    call FlushGameCache(InitGameCache("gaiascache"))
    set gc = InitGameCache("gaiascache")
    if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 then
        set b = true
    endif
    loop
        exitwhen i > 5
        set s = I2S(i)
        if GetLocalPlayer( ) == Player(i) then
             call StoreBoolean( gc , "A" , s , b )
             call SyncStoredBoolean( gc , "A" , s )
             call thread.sync()
        endif
        set i = i + 1
    endloop
    call thread.wait()
    call thread.destroy()
    
    set i = 0
    loop
        exitwhen i > 5
        set s = I2S(i)
        set HAS_EXTENSION_PACK[i] = GetStoredBoolean(gc, "A", s)
        if HAS_EXTENSION_PACK[i] then
            call BJDebugMsg("Player "+I2S(i)+" has the extension pack")
        else
            call BJDebugMsg("Player "+I2S(i)+" doesn't have extension pack")
        endif
        set i = i + 1
    endloop
endfunction

//===========================================================================
function InitTrig_Initialization_3 takes nothing returns nothing
    set gg_trg_Initialization_3 = CreateTrigger(  )
    call TriggerRegisterTimerEventSingle( gg_trg_Initialization_3, 1.50 )
    call TriggerAddAction( gg_trg_Initialization_3, function Trig_Initialization_3_Actions )
endfunction
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Zwiebelchen, here is the synchronize method from Network for Packet. You'll see that it's not as easy as the little code I just posted.

What it does is that it waits until it receives a value from a player. When a player has received all values from all other players, that player states that they are synchronized. When all players are synchronized, then the loop exits.

Why are players that send no information also sending data? The waiting player doesn't know who is sending information.

JASS:
        private method synchronize takes integer data, boolean source returns nothing
            local Thread thread = Thread.create()
            local integer syncPlayer = 11
            
            if (source) then
                call Memory.write(0, GetPlayerId(GetLocalPlayer()), this, data)
            else
                call Memory.writeB(0, GetPlayerId(GetLocalPlayer()), this)
            endif
            
            loop
                // are all players synchronized?
                exitwhen thread.synced

                // got value from player? then move on to next player
                if ((GetPlayerSlotState(Player(syncPlayer)) != PLAYER_SLOT_STATE_PLAYING or GetPlayerController(Player(syncPlayer)) != MAP_CONTROL_USER) or (Memory.has(0, syncPlayer, this) or Memory.hasB(0, syncPlayer, this))) then
                    set syncPlayer = syncPlayer - 1

                    // got value from all players? let Thread know that you are synchronized.
                    if (-1 == syncPlayer) then
                        call thread.sync()
                    endif
                endif

                call TriggerSyncReady()
            endloop
            
            call thread.destroy()
        endmethod


Here are the things you need for Packet and Thread. Alternatively, you could just steal the loop from Packet and the loop from Thread ^_-.

https://github.com/nestharus/JASS/blob/master/jass/Systems/Thread/script.j#L72
https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
https://github.com/nestharus/JASS/blob/master/jass/Systems/Network/Network.w3x?raw=true
http://www.hiveworkshop.com/forums/spells-569/unique-id-allocation-260897/?prev=status=a&u=Nestharus

The stuff from Thread is a little complicated... so uhmm.. I'd just use it heh.
 
@Nestharus: I did read your post. But I also read OP's post which said it should be a bare-bones example.

But I've always been curious what you meant by this:
TriggerSyncReady does not wait until synchronization has occurred for those values.

I think you mentioned it before, but I never asked about it. By 'values', do you mean boolean in particular? Or just any value?

Under what circumstances does TriggerSyncReady() fail to wait the proper duration? I'm not doubting your claim and I'm sure you've tested it thoroughly, but I just wanted to know the deets.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
JASS:
function Trig_SyncPack_Actions takes nothing returns nothing
    local integer i = -1
    local Packet packet
    local integer playerId = 11
    
    if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 then
        set i = 1
    endif
    call BJDebugMsg("test1")
    set packet = Packet.broadcast(i, i != -1)
    loop
        if (packet.has(playerId)) then
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Player "+I2S(playerId)+": "+I2S(packet[playerId]))
            if packet[playerId] == 1 then
                set HAS_EXTENSION_PACK[playerId] = true
            endif
        endif
    
        exitwhen 0 == playerId
        set playerId = playerId - 1
    endloop
    call BJDebugMsg("test2")
endfunction

//===========================================================================
function InitTrig_SyncPack takes nothing returns nothing
    set gg_trg_SyncPack = CreateTrigger(  )
    call TriggerRegisterTimerEventSingle( gg_trg_SyncPack, 1. )
    call TriggerAddAction( gg_trg_SyncPack, function Trig_SyncPack_Actions )
endfunction
It shows only "test1", not "test2". (Actually, it shows "test1" twice for some weird reason...) What am I doing wrong?

I have a feeling that the more systems I use for this, the worse it gets. -_-
Besides, this example part of your network map doesn't work in your demo map either.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
packet.has(playerId) is also wrong

Not sure why your thread is crashing.
I don't really understand how to use your system then.

Can you give me an example based on a 6x6 input/output snippet?

6 local data booleans (1 per player) synchronized to all 6 players each (in a global boolean array[6]).

PS: I'm using the old Jasshelper.

TriggerSyncReady just synchronizes the trigger, not the value. If you send 2 packets, packet 2 may arrive before packet 1. It isn't reliable, especially when dealing with lots of data. It's failed for me plenty if times. The loop thing always works without fail.

So if I send only 1 packet at a time, there is no way it can fail, right?

If so, then why can't I just do this?

JASS:
globals
     gamecache cache = null
endglobals

function Trig_SyncExtensionPackage_Actions takes nothing returns nothing
    local string missionKey
    local boolean value
    local integer i = 0
    
    call FlushGameCache(cache)
    set cache = InitGameCache("gaiascache")
    
    loop
        exitwhen i > 5
        set missionKey = I2S(i)
        if GetLocalPlayer() == Player(i) then
             if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 then
                set value = true
             else
                set value = false
             endif
             call StoreBoolean(cache, missionKey, "b", value)
             call SyncStoredBoolean(cache, missionKey, "b")
        endif
        call TriggerSyncReady()
        set HAS_EXTENSION_PACK[i] = GetStoredBoolean(cache, missionKey, "b")
        if HAS_EXTENSION_PACK[i] then
            call BJDebugMsg("Player "+I2S(i)+" has pack")
        else
            call BJDebugMsg("Player "+I2S(i)+" does not have pack")
        endif
        set i = i + 1
    endloop
endfunction

//===========================================================================
function InitTrig_SyncExtensionPackage takes nothing returns nothing
    set gg_trg_SyncExtensionPackage = CreateTrigger(  )
    call TriggerRegisterTimerEventSingle( gg_trg_SyncExtensionPackage, 0.2 )
    call TriggerAddAction( gg_trg_SyncExtensionPackage, function Trig_SyncExtensionPackage_Actions )
endfunction
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
I'd say..

JASS:
globals
    triggeraction Trig_SyncPack_Actions_a
    trigger Trig_SyncPack_Actions_t
endglobals

function Trig_SyncPack_Actions takes nothing returns nothing
    local Packet packet
    local integer playerId = 11
    
    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "test1")

    set packet = Packet.broadcast(1, GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0)
    loop
        set HAS_EXTENSION_PACK[playerId] = packet.has(playerId)
    
        exitwhen 0 == playerId
        set playerId = playerId - 1
    endloop

    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "test2")

    call TriggerRemoveAction(Trig_SyncPack_Actions_t, Trig_SyncPack_Actions_a)
    call DestroyTrigger(Trig_SyncPack_Actions_t)

    set Trig_SyncPack_Actions_a = null
    set Trig_SyncPack_Actions_t = null
endfunction

function InitTrig_SyncPack_T takes nothing returns nothing
    call DestroyTimer(GetExpiredTimer())

    set Trig_SyncPack_Actions_t = CreateTrigger()
    set Trig_SyncPack_Actions_a = call TriggerAddAction(t, function Trig_SyncPack_Actions)
    call TriggerExecute(t)
endfunction
function InitTrig_SyncPack takes nothing returns nothing
    call TimerStart(CreateTimer(), .2, false, function InitTrig_SyncPack_T)
endfunction

Only really have 5 minutes right now.

Also keep in mind that I don't think any of this is going to work with only 1 player.

edit
nvm, it works just fine with 1 player... lol

GameStart thing doesn't work in single player mode though I think... or something o-o

the following works perfectly for me

JASS:
struct PacketTest extends array
	private static method init takes nothing returns nothing
        //call WaitForGameToStart()
		call DestroyTimer(GetExpiredTimer())
        call test.execute()
    endmethod
    private static method onInit takes nothing returns nothing
		call TimerStart(CreateTimer(), 1, false, function thistype.init)
        //call init.execute()
    endmethod
    
    
    private static method test takes nothing returns nothing
        local integer i = -1
        local Packet packet
        local integer playerId = 11
        
        if (GetLocalPlayer() == Player(0)) then
            set i = 5
        elseif (GetLocalPlayer() == Player(1)) then
            set i = 12
        endif
        
		call DisplayTimedTextFromPlayer(GetLocalPlayer(), 0, 0, 60000, "Start")
        set packet = Packet.broadcast(i, i != -1)
        loop
            if (packet.has(playerId)) then
                call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,I2S(playerId)+": "+I2S(packet[playerId]))
            endif
        
            exitwhen 0 == playerId
            set playerId = playerId - 1
        endloop
		call DisplayTimedTextFromPlayer(GetLocalPlayer(), 0, 0, 60000, "End")
    endmethod
endstruct

edit
So if I send only 1 packet at a time, there is no way it can fail, right?

wrong! TriggerSyncReady once again does not wait until the packet arrives... it sends its own packet and that packet can arrive before the gamecache packet.

I honestly have no idea why you are running into probs. It works for me, lol.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Ah I see the problem now...

I'll try out your code suggestions and see if it works.

EDIT: Nope, still crashes thread at the broadcast step.


... anyway, just a very simple question:
Wouldn't it be enough to just use SyncStoredInteger and then just wait long enough for the retrieval of the value?
I don't need the synchronisation as fast as possible. I only need it 3 seconds after the syncing started. By then all values should be perfectly synced anyway, right?

Like this:
JASS:
globals
     gamecache cache = null
     boolean syncstarted = false
     timer synctimer = null
endglobals

function Trig_SyncExtensionPackage_Actions takes nothing returns nothing
    local string missionKey
    local boolean value
    local integer i = 0
    
    if not syncstarted then
        call FlushGameCache(cache)
        set cache = InitGameCache("gaiascache")
        set synctimer = CreateTimer()
        loop
            exitwhen i > 5
            set missionKey = I2S(i)
            if GetLocalPlayer() == Player(i) then
                 if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 then
                    set value = true
                 else
                    set value = false
                 endif
                 call StoreBoolean(cache, missionKey, "b", value)
                 call SyncStoredBoolean(cache, missionKey, "b")
            endif
            set i = i + 1
        endloop
        set syncstarted = true
        call TimerStart(synctimer, 3, false, function Trig_SyncExtensionPackage_Actions)
    else
        call DestroyTimer(synctimer)
        loop
            exitwhen i > 5
            set missionKey = I2S(i)
            set HAS_EXTENSION_PACK[i] = GetStoredBoolean(cache, missionKey, "b")
            if HAS_EXTENSION_PACK[i] then
                call BJDebugMsg("Player "+I2S(i)+" has pack")
            else
                call BJDebugMsg("Player "+I2S(i)+" does not have pack")
            endif
            set i = i + 1
        endloop
    endif
endfunction


//===========================================================================
function InitTrig_SyncExtensionPackage takes nothing returns nothing
    set gg_trg_SyncExtensionPackage = CreateTrigger(  )
    call TriggerRegisterTimerEventSingle( gg_trg_SyncExtensionPackage, 0.2 )
    call TriggerAddAction( gg_trg_SyncExtensionPackage, function Trig_SyncExtensionPackage_Actions )
endfunction
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
No idea why you crash and I don't. Go for timer then.
Yeah I guess the 3 second wait should be sufficient for all syncing. It's not like I'm broadcasting huge streams of data here.

First, see if TriggerSleepAction crashes for you. An executed trigger should not crash for this stuff. Did you try my version also?
Tried your version; it's always the broadcast method that crashes. I have no idea why, though.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
I just found this:
JASS:
struct tester extends array
     private static method onInit takes nothing returns nothing
         local integer i = 11
         local boolean array playing
         set playing[GetPlayerId(GetLocalPlayer())] = true
         
         call TriggerSleepAction(0.)
         
         loop
             if (playing[i]) then
                 call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetPlayerName(Player(i)) + " is playing")
             endif
             exitwhen i == 0
             set i = i - 1
         endloop
     endmethod
endstruct
Does this work? Looks very hack-ish to me... but if it does, that would be ingenious...
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Not unless you sync it.
The thread I found it in said that the TriggerSleepAction does sync it for some weird reason. But might be just a faulty conclusion.


Anyway, I'm still having trouble with my current approach using a 3 second timer to disconnect the SyncStoredBoolean() from the GetStoredBoolean(). It seems that still the data is not properly synced up... dunno why that is. 3 seconds should be more than enough to sync, right?
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Alternatively, you could use 2 dummies unit and Select/unselect them with GetLocalPlayer.
And then use the select unit event.
That won't be noticeable, since you don't need to use a wait between select and unselect, but yeah it's awfull to use the most interactive thing (an unit) just for that.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Just use Packet

Try both examples I posted. Modified one from map and one for yours. Tell me if either work for you. If one does, modify it to your needs.
I tried all examples from packet already. Again, the thread always crashes on the broadcast call.

This is so frustrating...

Here's what I use now. Still crashes after displaying test1.
JASS:
function Trig_SyncExtensionPackage_Actions takes nothing returns nothing
    local Packet packet
    local integer var = 0
    local integer i = 0

    if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 then
         set var = 1
    endif

    //if the variable isn't 0, synchronize that variable
    call BJDebugMsg("test1")
    set packet = Packet.broadcast(var, var != 0)
    call BJDebugMsg("test2")

    loop
        exitwhen i > 5
        if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i)) == MAP_CONTROL_USER then
            if packet[i] > 0 then
                set HAS_EXTENSION_PACK[i] = true
                call BJDebugMsg("Player "+I2S(i)+" has pack")
            else
                call BJDebugMsg("Player "+I2S(i)+" does not have pack")
            endif
        endif
        set i = i + 1
    endloop
endfunction


//===========================================================================
function InitTrig_SyncExtensionPackage takes nothing returns nothing
    set gg_trg_SyncExtensionPackage = CreateTrigger(  )
    call TriggerRegisterTimerEventSingle( gg_trg_SyncExtensionPackage, 0.2 )
    call TriggerAddAction( gg_trg_SyncExtensionPackage, function Trig_SyncExtensionPackage_Actions )
endfunction
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Okay, I tested the example from my previous post again in your network test map... and for some odd reason it works there (I adjusted nothing; just copy and pasted the trigger in).

So this brings me to the conclusion that it has something to do with my map. I get no syntax or jasshelper errors on this; but could it be a compiler or dependency issue?
Also, do I actually need vision over the map for this to work? I'm not sure how your syncing method works, but I suppose you use selection events? Could something mess up there if the map is completely covered in fog of war?

Also, I see you use UnitUserData in Thread... could a unit indexer interfere here?


EDIT: Found the issue. The dummy unit of thread wasn't properly created as the default position from Thread was invalid. Entered fixed coordinates for this and now it works.
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Woo. You didn't have WorldBounds?
I do, but it still screwed up for some reason.

I'll report back to this thread once I have done some multiplayer sessions to see if the syncing works flawless.

EDIT: First test confirmed working... I'll try some more.
 
Last edited:
Level 12
Joined
Mar 13, 2012
Messages
1,121
That's how thread does synchronization, but that alone still isn't reliable.
That doesn't help. Your network resource and others are very interesting but I (and I guess many others) would appreciate it a lot if you would add explanations to the important points.

So please, share you knowledge on the whole sync topic.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Zwiebelchen, you don't need to test it. Lib already tested, lol. If you get it to run, which you did, you win : ).
With multiplayer and syncing, there are always some corner cases that are hard to catch.
I could imagine host-bot mechanics messing up some stuff. Or heavy lag.

Testing is good. Practical usage data is better.

That doesn't help. Your network resource and others are very interesting but I (and I guess many others) would appreciate it a lot if you would add explanations to the important points.

So please, share you knowledge on the whole sync topic.
Nesth has never been good about explaining his stuff. ;)
But what should I say? It seems to work now... and with it effectively just being a one-liner in my use-case scenario, I couldn't care less about how it works internally. ;P
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
I constantly experience desyncs now that I have implemented your network system. The desync happens as soon as the Broadcast method fires.

JASS:
function Trig_SyncExtensionPackage_Actions takes nothing returns nothing
     local Packet packet
     local integer var = 0
     local integer i = 0

     if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 then
          set var = 1
     endif

     //if the variable isn't 0, synchronize that variable
     set packet = Packet.broadcast(var, var != 0)

     loop
         exitwhen i > 5
         if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i)) == MAP_CONTROL_USER then
             if packet[i] > 0 then
                 set HAS_EXTENSION_PACK[i] = true
             endif
         endif
         set i = i + 1
     endloop
endfunction

I used the trigger as posted above. Any ideas?

Remember that more than one player at the same time can have var = 1.
Might that be the problem? Should I replace
JASS:
     if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 then
          set var = 1
     endif
with
JASS:
     if GetLocalPlayer() == Player(i) then
          if GetSoundFileDuration("GaiasRetaliation\\Voice\\Intro\\01.wav") > 0 then
               set var = 1
          endif
     endif
so that for any player, only the local player himself can possibly have var == 1?


EDIT: Nevermind. Seems like the desync only happens if you didn't restart WC3 and played another map before.
 
Last edited:
Level 12
Joined
Mar 13, 2012
Messages
1,121
EDIT: Nevermind. Seems like the desync only happens if you didn't restart WC3 and played another map before.

That's not a "nevermind" but a potential fatal problem of syncing or Nestharus' way of doing those things.

Nestharus can you give details on how you achieve "reliable syncing" and on some of your syncing knowledge. For once other people want to learn too and also input of others might be valuable.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
That's not a "nevermind" but a potential fatal problem of syncing or Nestharus' way of doing those things.
Nono, this is hostbot related. It actually happens in other games aswell. I guess it has something to do with hashtables.

That desync is so weird...

Test sync by order.
There is, however, indeed a problem with Network after extensive field-testing over the last days:

It seems that in a 6 player game, the syncing can take up to 5 minutes (weirdly enough, as I only sync 1 boolean per player) in which the game does not freeze, but actually "lag" as in: the game is not freezing, but it doesn't respond to any input. Chatting is possible; however, playing is not.
Is there the possibility the system might mess something up, i.e. when someone lags at a critical moment? I can't imagine this syncing process to take so long for such a small number of variables.

It only seems to happen in near full-house games. With 5 or less players, it goes significantly faster. Still, 5 minutes is way too much.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
The syncing is taking a long time because of the TriggerSyncReady spam. If we did a timer poll instead, it should probably go faster. I'd have to test it. Someone else tried it with orders and got speeds that were way faster, but I think gamecache is actually viable if we get away from TriggerSyncReady. Instead, we'd just kind of wait for data to arrive on like a .03125 timer or something.

As you saw before, the synchronization can vary in how long it takes wildly. Even when you did a 3 second sleep, it didn't synchronize. There's just a lot of testing that needs to be done. Everyone knew about the low synchronization speeds with more players and stuff >.>. The TriggerSyncReady stuff doesn't help at all either.

Sadly, I don't have time to do this since I'm spending all of my free time on a compiler. Once the compiler is done, I need to work on collections and other things before I can actually get back to Network, so it'll be awhile. If I had more people helping me, or if someone else wanted to learn about synchronization and try to improve Network, it'd be a different story.

For those who are interested, this is how the Packet synchronization works. Every single player sends data, even if they aren't sending anything. You start at player 11 and stop. When you receive data from player 11, or player 11 isn't a human, you move down to player 10 and stop again. You keep waiting for data at each player until you have received data from player 0. Once this happens, you use Thread to synchronize with everyone, or rather to let everyone know that you are synchronized. Thread does a SelectUnit. When this runs, an event will occur for all players. An integer is used as a bit array, and the player that fired the selection event is set to true for the bit array. When all valid players have fired the event, then everyone knows that everyone else is ready to go and you move on.

The wait method waits until the bit array is all true.
The sync method from Thread fires the SelectUnit for yourself to fire the event for everyone.

So yea, that's exactly how Packet works. Inside of every loop is a TriggerSyncReady. This thing runs on every iteration. TriggerSyncReady isn't as reliable as Thread, especially with very large operations. It has a timeout or something. Anyways, TriggerSyncReady gets spammed to no end absolutely everywhere, which likely completely kills the synchronization speed. It's essentially just used as a wait. TriggerSleepAction does the same thing as TriggerSyncReady, but has a longer delay. We need something asynchronous, like a timer. I think if we move it all to timers, it'll work. The problem is how to get everyone back into the code... if we know that everyone is ready to go, that is everyone has received data (we use Thread just to run the synchronization and we have the array), then what we can do is for the first player that sees that everyone is good to go (last player to run the timer, everyone else will be out of it), they can do a SelectUnit, which will have everyone reenter the code at the same time. From here, they can pause and destroy the timer. We'll have to make it so that the SelectUnit event only runs once too. I think destroying it immediately will not cause a desync and extra events from other players that might have seen the synchronization ready thing and sent out the flag will just be ignored.


Uhm, so yea... in order to speed it up, I have lots of ideas, but no time to implement : P.



Also keep in mind that there is some sort of buffer for sending data off. This is why if you have a save/load code or something and you want to synchronize it, you can't send it all at once. If you make the buffer angry, it's ganna go really, really slow. If you stay within bounds, it'll be happy. For orders, someone else ran into an order limit where they got throttled there. There are lots of things you have to consider.

Stream ran faster because it had less TriggerSyncReady between the data and stayed within the buffer. I think that the TriggerSyncReady spam is also making the buffer mad, or rather hits the buffer limit when ou have 6 players in the map all spamming it together. We definitely need to get rid of the spam. The Network library and Thread library have like everything you need. They just need to be ... modified a bit ^_-.
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
So, is there anything I can do to improve the speed on this without actually modifying packet?
Does it ignore players 6-11 if they don't exist?

So far, it looks like the syncing takes only a couple of seconds in a 4 player game, roughly a 2-3 minutes (though it's random; doesn't always happen) in a 5 player game and up to 5-6 minutes in a 6 player game (someone stopped it at 5:30).
Would splitting up the syncing into brackets help? Like syncing player 1 first, then syncing player 2, etc. instead of all at once?

I hope you find the time improving network soon... it's pretty awesome and looks very reliable so far... just the delay is killing it. As I use it in combination with local files to set player flags, it pretty much opens up all kinds of sick potential to bypass the 8MB limit in multiplayer games.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Uhm, no way to fix without editing lib.

Hopefully someone reading this will be interested. I have a codeless save/load lib that depends on Network too, so fixing Network also fixes codeless save/load.

I really need my own personal team -.-. I have a lot of projects and a lot of work that needs to be done for THW Community : /. Network, DDS, BigInt, Trigger, and Std Collections to name a few.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Uhm, no way to fix without editing lib.

Hopefully someone reading this will be interested. I have a codeless save/load lib that depends on Network too, so fixing Network also fixes codeless save/load.

I really need my own personal team -.-. I have a lot of projects and a lot of work that needs to be done for THW Community : /. Network, DDS, BigInt, Trigger, and Std Collections to name a few.
Whats wrong with BigInt and DDS? Last time I checked they get the job done. Codeless save/load would be your coolest resource it it weren't for the ridicolous delay, so better focus on network first. ;)
There's a lot of cool stuff I could imagine possible with a faster network system.

For example; I currently use external folders only for stuff I can create safely on runtime from file; like special effects, sounds, music, images.
With network, you have more power and freedom as now you are able to do stuff that would desync if not broadcasted. For example, I currently use it to play a cinematic if a player has the external pack.
But network allows for even more here:
If, for example, all players in a session have the external folder, I could have an alternate set of units or destructables that replace the default units/destructables, all with imported model files without taking up precious map space.

This is pretty cool in my book. It actually didn't take me long to convince players enabling local files for my extension folder. Lots of players use it already. And since it automatically changes the flag as soon as an "invalid" player is in the game, you still have full bnet compat.
 
Status
Not open for further replies.
Top