- Joined
- Jul 10, 2007
- Messages
- 6,306
JASS:
library IntegerSync /* v1.0.1.0
*************************************************************************************
*
* Used to sync integers between players.
*
* Special Thanks to Halo7568
*
*************************************************************************************
*
* */uses/*
*
* */ Alloc /* hiveworkshop.com/forums/jass-resources-412/snippet-alloc-192348/
* */ Thread /* hiveworkshop.com/forums/submissions-414/snippet-thread-218269/
*
************************************************************************************
*
* constant function IsPositionError takes integer position returns boolean
* - Checks if position is error. Position is retrieved from getErrorPosition in the
* - IntegerSyncStream struct.
*
* struct IntegerSync extends array
* - Used to sync single integers
*
* static method create takes nothing returns IntegerSync
* method destroy takes nothing returns nothing
*
* method sync takes integer data, boolean source returns integer
*
* struct IntegerSyncStream extends array
* - Used to sync up many integers
*
* readonly integer dataSize
* - The number of integers in the stream.
* - This starts at 1, not 0
* readonly integer readIndex
* - The current integer being read from the stream. A stream can only ever be read
* - once. It should be read after syncing process is complete.
* - This starts at 1, not 0
*
* static method create takes nothing returns IntegerSyncStream
* method destroy takes nothing returns nothing
*
* method startBurst takes nothing returns nothing
* - Begins a data burst. Used to prevent broadcasting player from desyncing
* - due to large operations. Furthermore, prevents op limit.
*
* method operator burstOver takes nothing returns boolean
* - Will be true when the current burst is finished.
*
* method broadcast takes integer data returns nothing
* - Broadcasts an integer to other players in the game
* - Always broadcast inside of a burst.
*
* call startBurst()
* loop
* call broadcast(data)
* exitwhen burstOver
* endloop
*
* method operator synced takes nothing returns boolean
* - This will be true when syncing is complete
*
* method startSync takes nothing returns nothing
* - Call from broadcasting player when the broadcasting is complete (broadcasting, not bursts)
* - This will send a request to make the synced boolean true. The synced boolean will become
* - true after all of the broadcasted data is finished syncing.
*
* method finishSync takes nothing returns nothing
* - Call after syncing is complete
* - This gets the data
*
* method getErrorPosition takes nothing returns integer
* - This searches for an error in the stream. Use IsPositionError to determine whether position was error or not.
* - This is properly sync'd for all players.
*
* method read takes nothing returns integer
* - This reads from the stream in the same order the data was broadcasted.
*
* Syncing Example
*
* local integer iterator = DATA_SIZE //some arbitrary number just for this example
* local IntegerSyncStream stream = IntegerSyncStream.create()
*
* //only the broadcasting player should have the iterator
* //the iterator can be used to see if the current player is broadcasting or not
* //this works well because the iterator will be DATA_SIZE when the broadcasting
* //is finished, meaning that the broadcaster will move from a broadcasting
* //state to a waiting state after broadcasting is finished w/o any special vars
* if (GetLocalPlayer() == BROADCASTER) then
* set iterator = 0
* endif
*
* //this outer loop will continue until the syncing is completed
* //the burst loop is located within this
* loop
* //this is where bursting is done. Always put TriggerSyncStart() and TriggerSyncReady()
* //around the burst loop
* call TriggerSyncStart()
* if (iterator != DATA_SIZE) then
* call stream.startBurst()
* loop
* //simply broadcasting the iterator for the hell of it. Can broadcast anything.
* call stream.broadcast(iterator)
*
* //this is the get next integer step. In this case, next integer is retrieved by increasing iterator
* //in File I/O, the next integer would be retrieved by reading a file
* set iterator = iterator + 1
*
* //this exits when either the broadcast is complete or the burst is complete
* //in this case, broadcast is complete when iterator is DATA_SIZE.
* //In File I/O, broadcast would be complete after all files are
* //read
* exitwhen iterator == DATA_SIZE or stream.burstOver
* endloop
*
* //if the broadcasting is complete, begin the sync
* //after this point, the broadcaster will be like the rest of the players waiting for stream.synced to be true
* if (iterator == DATA_SIZE) then
* call stream.startSync()
* endif
* endif
* call TriggerSyncReady()
*
* exitwhen stream.synced or GetPlayerController(BROADCASTER) != MAP_CONTROL_USER
* endloop
*
* Error Check Example
*
* //this should only be used in debug
* //getErrorPosition will only search for errors when debug mode is enabled
* //outside of debug mode, it never returns an error
* set errorPosition = stream.getErrorPosition()
* if (IsPositionError(errorPosition)) then
* //there was an error
*
* call stream.destroy()
* return
* endif
*
* Read Example
*
* local integer array data
* loop
* exitwhen stream.readIndex == stream.dataSize
* set data[stream.readIndex + 1] = stream.read()
* endloop
*
*************************************************************************************/
globals
private constant string BASE = "!#$%&()*+'-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{}~"
private constant integer BASE_LENGTH = StringLength(BASE)
private constant integer BASE_LENGTH2 = BASE_LENGTH*BASE_LENGTH
private gamecache syncC
private string array kthis
endglobals
private struct IdBuilder extends array
private static method onInit takes nothing returns nothing
local integer i = BASE_LENGTH
loop
set i = i - 1
set kthis[i] = SubString(BASE, i, i + 1)
exitwhen 0 == i
endloop
set i = (BASE_LENGTH - 1)*BASE_LENGTH + BASE_LENGTH
loop
set i = i - 1
set kthis[i] = kthis[i/BASE_LENGTH] + kthis[i - i/BASE_LENGTH*BASE_LENGTH]
exitwhen BASE_LENGTH == i
endloop
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,9000,"Done Initializing")
endmethod
endstruct
constant function IsPositionError takes integer position returns boolean
return 0 != position
endfunction
private function BuildId takes integer v returns string
local string s = ""
loop
set s = kthis[v - v/BASE_LENGTH2*BASE_LENGTH2] + s
set v = v/BASE_LENGTH2
exitwhen 0 == v
endloop
return s
endfunction
struct IntegerSync extends array
implement Alloc
static method create takes nothing returns thistype
return allocate()
endmethod
method sync takes integer data, boolean source returns integer
local Thread thread = Thread.create()
call TriggerSyncStart()
if (source) then
call StoreInteger(syncC, kthis[this], "!!", data)
call SyncStoredInteger(syncC, kthis[this], "!!")
endif
call TriggerSyncReady()
call thread.sync()
loop
call TriggerSyncStart()
exitwhen thread.synced
call TriggerSyncReady()
endloop
if (HaveStoredInteger(syncC, kthis[this], "!!")) then
set data = GetStoredInteger(syncC, kthis[this], "!!")
call FlushStoredInteger(syncC, kthis[this], "!!")
endif
call thread.destroy()
return data
endmethod
method destroy takes nothing returns nothing
call deallocate()
endmethod
endstruct
struct IntegerSyncStream extends array
private Thread thread
readonly integer dataSize
readonly integer readIndex
private static constant integer BURST_SIZE = 1000
private integer burst
private static constant integer READ_SIZE = 1000
private integer readBurst
static method create takes nothing returns thistype
local thistype this = IntegerSync.create()
set thread = Thread.create()
set burst = BURST_SIZE
return this
endmethod
method destroy takes nothing returns nothing
call FlushStoredMission(syncC, kthis[this])
set dataSize = 0
set readIndex = 0
set readBurst = 0
call thread.destroy()
call IntegerSync(this).destroy()
endmethod
method broadcast takes integer data returns nothing
local string dc = BuildId(dataSize + 1)
set dataSize = dataSize + 1
call StoreInteger(syncC, kthis[this], dc, data)
call SyncStoredInteger(syncC, kthis[this], dc)
set burst = burst - 1
endmethod
method startBurst takes nothing returns nothing
set burst = BURST_SIZE
endmethod
method operator burstOver takes nothing returns boolean
return 0 == burst
endmethod
method read takes nothing returns integer
if (0 == readBurst) then
call TriggerSyncStart()
set readBurst = READ_SIZE
call TriggerSyncReady()
endif
set readBurst = readBurst - 1
set readIndex = readIndex + 1
return GetStoredInteger(syncC, kthis[this], BuildId(readIndex))
endmethod
method startSync takes nothing returns nothing
call StoreInteger(syncC, kthis[this], "!", dataSize)
call SyncStoredInteger(syncC, kthis[this], "!")
call thread.sync()
endmethod
method finishSync takes nothing returns nothing
set dataSize = GetStoredInteger(syncC, kthis[this], "!")
endmethod
method operator synced takes nothing returns boolean
return thread.synced
endmethod
method getErrorPosition takes nothing returns integer
static if DEBUG_MODE then
local integer burst
local integer dataCheck = dataSize
loop
call TriggerSyncStart()
set burst = BURST_SIZE
loop
exitwhen 0 == dataCheck or 0 == burst or not HaveStoredInteger(syncC, kthis[this], BuildId(dataCheck))
set burst = burst - 1
set dataCheck = dataCheck - 1
endloop
call TriggerSyncReady()
exitwhen 0 == dataCheck or not HaveStoredInteger(syncC, kthis[this], BuildId(dataCheck))
endloop
return IntegerSync(this).sync(dataCheck, 0 != dataCheck)
else
return 0
endif
endmethod
private static method onInit takes nothing returns nothing
set syncC = InitGameCache("!")
endmethod
endstruct
endlibrary
Sync Integer Excerpt
JASS:
private static method syncData takes integer count, player source, IntegerSyncStream stream returns nothing
local integer i = StartBroadcast(count, source)
loop
call TriggerSyncStart()
if (IsBroadcasting(i)) then
call stream.startBurst()
loop
call stream.broadcast(i)
set i = GetNextBroadcast(i)
exitwhen IsBroadcastComplete(i) or stream.burstOver
endloop
if (IsBroadcastComplete(i)) then
call stream.startSync()
endif
endif
call TriggerSyncReady()
exitwhen stream.synced or GetPlayerController(source) != MAP_CONTROL_USER
endloop
call stream.finishSync()
endmethod
Error Check Excerpt
JASS:
private static method errorCheck takes IntegerSyncStream stream returns nothing
local integer errorPosition = stream.getErrorPosition()
if (IsPositionError(errorPosition)) then
call stream.destroy()
set errorPosition = 1/0
endif
endmethod
Read Excerpt
JASS:
private static method read takes IntegerSyncStream stream returns nothing
loop
call stream.read()
exitwhen stream.readIndex == stream.dataSize
endloop
endmethod
Fully Working Example
JASS:
globals
timer downloadTimer
endglobals
function StartDownload takes nothing returns timer
set downloadTimer = CreateTimer()
call TimerStart(downloadTimer, 600000, false, null)
return downloadTimer
endfunction
function GetDownloadTime takes timer downloadTime returns string
local real time
local integer timeI
local integer hours
local integer minutes
local integer seconds
local real decimal
local string hoursS
local string minutesS
local string secondsS
set time = TimerGetElapsed(downloadTime)
set timeI = R2I(time)
set decimal = time - timeI
set decimal = R2I(decimal*100)/100.
set seconds = timeI - timeI/60*60
set timeI = timeI/60
set minutes = timeI - timeI/60*60
set timeI = timeI/60
set hours = timeI
set hoursS = I2S(hours)
set minutesS = I2S(minutes)
set secondsS = I2S(seconds)
if (hours < 10) then
set hoursS = "0"+hoursS
endif
if (minutes < 10) then
set minutesS = "0"+minutesS
endif
if (seconds < 10) then
set secondsS = "0"+secondsS
endif
call DestroyTimer(downloadTime)
return "Downloaded Data in "+hoursS+":"+minutesS+":"+secondsS+SubString(R2S(decimal),1,4)
endfunction
function StartBroadcast takes integer size, player source returns integer
if (GetLocalPlayer() == source) then
return size
endif
return 0
endfunction
function IsBroadcasting takes integer i returns boolean
return 0 != i
endfunction
function GetNextBroadcast takes integer i returns integer
return i - 1
endfunction
function IsBroadcastComplete takes integer i returns boolean
return 0 == i
endfunction
struct tester extends array
private static constant integer COUNT = 10000
private static constant player PLAYER = Player(2)
private static method syncData takes integer count, player source, IntegerSyncStream stream returns nothing
local integer i
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600000,"Stream Sync Start")
set i = StartBroadcast(count, source)
loop
call TriggerSyncStart()
if (IsBroadcasting(i)) then
call stream.startBurst()
loop
call stream.broadcast(i)
set i = GetNextBroadcast(i)
exitwhen IsBroadcastComplete(i) or stream.burstOver
endloop
if (IsBroadcastComplete(i)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600000,"Broadcasting Finished, Waiting For Sync Completion")
call stream.startSync()
endif
endif
call TriggerSyncReady()
exitwhen stream.synced or GetPlayerController(source) != MAP_CONTROL_USER
endloop
call stream.finishSync()
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600000,"Stream Sync End")
endmethod
private static method errorCheck takes IntegerSyncStream stream returns nothing
local integer errorPosition
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600000,"Error Check Start")
set errorPosition = stream.getErrorPosition()
if (IsPositionError(errorPosition)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600000,"Position: "+I2S(errorPosition)+" Failed to Sync")
call stream.destroy()
set errorPosition = 1/0
endif
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600000,"Error Check End")
endmethod
private static method read takes IntegerSyncStream stream returns nothing
local integer i
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600000,"Read Start")
set i = stream.dataSize
loop
call stream.read()
set i = i - 1
exitwhen 0 == i
endloop
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600000,"Read End")
endmethod
private static method sync takes integer count, player source, IntegerSyncStream stream returns nothing
local timer downloadTime
set downloadTime = StartDownload()
call syncData(count, source, stream)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600000,"Downloaded Time: "+GetDownloadTime(downloadTime))
set downloadTime = null
endmethod
private static method exec takes nothing returns nothing
local IntegerSyncStream stream = IntegerSyncStream.create()
call sync(COUNT, PLAYER, stream)
call errorCheck(stream)
call read(stream)
call stream.destroy()
endmethod
private static method init takes nothing returns nothing
call DestroyTimer(GetExpiredTimer())
call ExecuteFunc(exec.name)
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(),0,false,function thistype.init)
endmethod
endstruct
Cool Download Progress Excerpt
JASS:
loop
call TriggerSyncStart()
//burst
call TriggerSyncReady()
exitwhen thread.synced
if (GetLocalPlayer() != source) then
loop
exitwhen not HaveStoredInteger(sync, I2S(stream), I2S(syncCount))
set syncCount = syncCount + 1
endloop
if (syncCount != oldSyncCount) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,R2S(syncCount/I2R(COUNT)*100)+"% Downloaded")
set oldSyncCount = syncCount
endif
endif
endloop
Last edited: