• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJASS] SyncNibble

Level 23
Joined
Feb 6, 2014
Messages
2,466
JASS:
library SyncNibble /*
    
                            SyncNibble v1.00
                                by Flux
                                
    SyncNibble allows you to synchronize a nibble (4 bits) which is represented
    as an integer between 0 and 15. If you want to synchronize negative integers,
    use 2's complement or use the most significant bit as a sign bit.
    
    ******************************* API: *********************************
    
    - Sync.start(player source, integer nibble)
            Start synchronizing a 4-bit integer of player source to all other players
            Returns false upon improper use.
    
    - Sync.wait()
            Pause current execution of the code until synchronization is finished.
    
    - Sync.nibble
            Contains the synchronized 4-bit integer after Sync.wait()
            If Sync.nibble == -1, it means synchronization failed.
    
    */
    globals
        //Important DUMMY_ID needed by the System
        private constant integer DUMMY_ID = 'sync'
        
        //Time between each check attempt
        private constant real REFRESH_RATE = 0.05
        
        //If the number of retry check attempt reaches MAX_RETRY, the synchronization
        //will count as a failed and will display FailedMessage.
        private constant integer MAX_RETRY = 20
    endglobals
    
    //Configure the displayed message upon a failed synchronization attempt
    private function FailedMessage takes nothing returns nothing
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "|cffffcc00[SyncNibble]:|r Failed to synchronize due to poor network condition!")
    endfunction
    
    struct Sync extends array
        static integer nibble
        
        private static unit array dummy
        private static player source
        private static integer retry
        private static boolean free = true
        private static timer t = CreateTimer()
        
        static method wait takes nothing returns nothing
            loop
                exitwhen free
                call TriggerSleepAction(0.00)
            endloop
        endmethod
        
        private static method check takes nothing returns nothing
            local integer i = 0
            local boolean b = true
            loop
                exitwhen IsUnitSelected(dummy[i], source)
                if i > 15 then
                    set retry = retry + 1
                    if retry == MAX_RETRY then
                        set thistype.nibble = -1
                        set free = true
                        call PauseTimer(t)
                        if GetLocalPlayer() == source then
                            call ClearSelection()
                        endif
                        call FailedMessage()
                    endif
                    return
                endif
                set i = i + 1
            endloop
            set thistype.nibble = i
            set free = true
            call PauseTimer(t)
            if GetLocalPlayer() == source then
                call ClearSelection()
            endif
        endmethod

        static method start takes player p, integer whichNibble returns boolean
            local integer i = 0
            if free then
                if whichNibble >= 0 and whichNibble < 16 then
                    set free = false
                    loop
                        exitwhen i > 15
                        call SetUnitOwner(dummy[i], p, false)
                        set i = i + 1
                    endloop
                    if GetLocalPlayer() == p then
                        call ClearSelection()
                        call SelectUnit(dummy[whichNibble], true)
                    endif
                    set source = p
                    set retry = 0
                    call TimerStart(t, REFRESH_RATE, true, function thistype.check)
                    return true
                debug else
                    debug call BJDebugMsg("|cffffcc00[SyncNibble]:|r Invalid nibble!")
                endif
            debug else
                debug call BJDebugMsg("|cffffcc00[SyncNibble]:|r Busy")
            endif
            return false
        endmethod
        
        
        private static method onInit takes nothing returns nothing
            local integer i = 0
            loop
                exitwhen i > 15
                set dummy[i] = CreateUnit(Player(14), DUMMY_ID, 0, 0, 0)
                call UnitRemoveAbility(dummy[i], 'Amov')
                set i = i + 1
            endloop
        endmethod
        
    endstruct

endlibrary



attachment.php




JASS:
library Tester initializer OnInit uses SyncNibble

    
    private function BeginTest takes nothing returns nothing
        local integer i1 = GetRandomInt(0, 15)
        local integer i2 = GetRandomInt(0, 15)
        local integer nibble
        call BJDebugMsg("|cffffcc00**********Test about to begin***********|r")
        //Make sure they have different values
        loop
            exitwhen i2 != i1
            set i2 = GetRandomInt(0, 15)
        endloop
        
        //Assign a different value to each player
        if GetLocalPlayer() == Player(0) then
            set nibble = i1
        elseif GetLocalPlayer() == Player(1) then
            set nibble = i2
        endif

        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "your local integer is " + I2S(nibble))
        
        //Display who is the source
        if GetTriggerPlayer() == Player(0) then
            call BJDebugMsg("About to sync from Player(0)")
        elseif GetTriggerPlayer() == Player(1) then
            call BJDebugMsg("About to sync from Player(1)")
        endif
        
        call BJDebugMsg("Synchronizing the nibble")
        if Sync.start(GetTriggerPlayer(), nibble) then
            call BJDebugMsg("Synchronization Starting...")
            call Sync.wait()
            call BJDebugMsg("Synchronization Finished")
        else
            call BJDebugMsg("Synchronization Failed")
        endif

        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "your synchronized integer is " + I2S(Sync.nibble))
        
        call BJDebugMsg("|cffffcc00*********************************************|r")
    endfunction
    
    private function OnInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local string s = "test"
        call TriggerRegisterPlayerChatEvent(t, Player(0), s, true)
        call TriggerRegisterPlayerChatEvent(t, Player(1), s, true)
        call TriggerAddAction(t, function BeginTest)
        //Test Messages
        call TriggerSleepAction(0.1)
        call BJDebugMsg("Type '" + s + "' to sync an integer")
    endfunction
    
endlibrary



My last submission before I go inactive :/
 

Attachments

  • SyncNibble v1.00.w3x
    19.8 KB · Views: 70
  • SyncNibble.jpg
    SyncNibble.jpg
    100.7 KB · Views: 360
Level 23
Joined
Feb 6, 2014
Messages
2,466
I'm optimistic that this method has consistent sync time (regardless of no. of players) because it works the same concept as using ForceUIKey. I'm estimating 1 nibble per 0.2 second (min TSA) so 20 bits/sec.


Is this method faster than the Sync natives? If so have you tested by how much?
Yes, a benchmark would be needed.
My only way of testing is Multiplayer Emulation, will that be sufficient?
 
I think the only reason my method was slow was because it spammed TriggerSyncReady. If we can drop most of the TriggerSyncReady, or all of it, and use timers instead, it'll be quick : ).


I just haven't been inspired to do this. However, it is open source, so anyone is free to do it ^_^.


TriggerSleepAction is a combination of TriggerSyncReady and a timeout, so spamming that isn't going to alleviate the problem.


The problem is that you can't start a local timer or you'll cause a desync After everyone gets the flag, I think we can do a single TriggerSyncReady and that'll do it.



Transmit Value
Start .03125 Timer
Send Flag Back When Receive Value
When Got All Flags, call TriggerSyncReady
Done


I don't know if this method will desync or not, but I think it'll work : O. Would have to register a trigger to a timer with an action so that TriggerSyncReady can be called.
 
Level 23
Joined
Feb 6, 2014
Messages
2,466
A bit cannot represent numbers greater than 7, let alone a nibble. You are syncing an integer (32bits) which is storing a single-digit hex value.

Err, I think you got confused Bribe. A bit can only represent 0 or 1 and a nibble can represent a single hexadecimal (0x0 to 0xf).

@Nestharus, if I may ask, what does TriggerSyncReady exactly do? And the only reason I'm using TSA is to have a blocking (also called synchronous) execution. I could also implement a non-blocking (asynchronous) execution using a callback when synchronization is finished.
 
Level 23
Joined
Feb 6, 2014
Messages
2,466
I meant to say Byte instead of bit, but a nibble is indeed 4 bits and not 16 like you have here. https://en.m.wikipedia.org/wiki/Nibble

0 = 0000 = 0x0
15= 1111 = 0xf

When you input 15, you're actually inputing 1111. When you input 7, you're inputting 0111 (or 0x7) etc.

Too bad JASS doesn't support Bitwise operation else it would make more sense.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I could be wrong, but i think i've already successfully sync data with the select unit event.
Without any more stuff than GetLocalPlayer, SelectUnit and the selection unit events, and no ClearSelection or other bullshit, and without any delay between selection/deselection of an unit, neither between 2 different unit selection.
I used dummies units and one dummy unit for the end of sync.

Obviously, you can safely select/deselect an unit in a local block, the events will still fire on each player computer. You already do that with your mouse.

But i didn't care to benchmark it.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Never tested, but maybe selection events are not delayed (fire when selection happens).
For sure, selection action is delayed, and maybe only that.
But if you select/deselect several units with trigger without any wait, maybe the total delay will be the same or so VS select/deselect a single unit.

Anyway with this method that was totally unoticeable and didn't fuck up the player unit selection.
 
Top