wc3campaigns
WC3C Homepage - www.wc3c.netUser Control Panel (Requires Log-In)Engage in discussions with other users and join contests in the WC3C forums!Read one of our many tutorials, ranging in difficulty from beginner to advanced!Show off your artistic talents in the WC3C Gallery!Download quality models, textures, spells (vJASS/JASS), systems, and scripts!Download maps that have passed through our rigorous approval process!

Go Back   Wc3C.net > Resources > - Submit a resource -
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 01-24-2010, 04:15 PM   #1
Strilanc
User
 
Strilanc's Avatar
 
Join Date: Jun 2007
Posts: 917

Submissions (4)

Strilanc has a spectacular aura about (131)

2008 Spell olympics - Fire - Gold

Default Async Lag System

Summary
Some maps, such as tower defenses, are not sensitive to latency. If a player lags for five or ten or even twenty seconds it's better for them to lose the time than hold everyone else and wait at the lag screen [deal with your own lag!]. Host bots allow raising the lag ceiling this high, but doing so effectively hides if a player is actually lagging or not.

This system detects when a player is falling behind, and displays a timer dialog showing how far behind they are (in game seconds). The system also implements a -wait command which, if the hosting bot supports it, will jumpstart the lag screen instead of waiting until the lag ceiling. The map can trigger this automatically at critical points if it wants.

Effectively, this system lowers the cost of raising the lag ceiling. If your map uses it then a host bot can detect it and raise the lag ceiling higher than normal, which makes the game much less annoying if one player is lagging.

Mechanics
Every player keeps track of the elapsed game time and, twice per second, stores and syncs it in a game cache value (different for each player; they don't overwrite each other). When a player starts to lag, their synced values will take longer to reach the host than the syncs of the other players. A lagger's synced game time will fall behind, and the system just checks for that happening.

Collapse JASS:
///////////////////////////////////////////////////////////////////////////
/// Asynchronous Lag Library
/// v1.00
/// Strilanc
///////////////////////////////////////////////////////////////////////////
/// This library is meant to complement using a host bot. Host bots tend to have higher
/// limits on the length of time a player can lag before the game is held by the lag screen.
/// This is generally a good thing, but it hides the fact that a player is lagging.
///
/// This library detects players lagging and displays them using timer dialogs. The host bot
/// does not need to support that aspect of the library.
///
/// This library also provides a -wait command, which sends a signal the bot can catch in
/// order to trigger the lag screen. The host bot must support this signal in order for the
/// command to work.
///////////////////////////////////////////////////////////////////////////
/// Notes:
/// - Requires vJass in order to compile.
/// - Just paste the code into the map and the library will do its thing.
/// - You may call SignalBotToShowLagScreen() to force lag screens [if anyone is lagging] at critical points in the map.
/// - This library uses SyncStoredInteger, which has been known to interact badly with TriggerSleepAction. You
///   should use PolledWait or timers instead (GUI people: use "Wait (Game-Time)" instead of "Wait"). TriggerSleepAction
///   has tons of problems anyways, so you should already not be using it.
///////////////////////////////////////////////////////////////////////////
library HostBotAsyncLag initializer init
    globals
        private constant string WAIT_COMMAND = "-wait"
        private constant integer TICK_PERIOD = 500 //ms
        private constant integer MAX_DELAY = 2000 //ms
        
        private gamecache gc
        private timer array timers
        private timerdialog array dialogs
        private integer gameTime = 0
        private string array colorCodes
        
        private constant string FILENAME = "HostBot.AsyncLag"
        private constant string TICK_KEY = "tick"
        private constant string WAIT_KEY = "wait"
    endglobals
    
    ///Signals any supporting host bot to hold the game until all lagging players catch up
    function SignalBotToShowLagScreen takes nothing returns nothing
        call SyncStoredInteger(gc, WAIT_KEY, WAIT_KEY)
    endfunction

    ///Determines the latest sync-received time from the player with the given id
    private function GetPlayerSyncReceivedTime takes integer id returns integer
        return GetStoredInteger(gc, TICK_KEY, I2S(id))
    endfunction
    ///Advances and sync-sends the local time, without desyncing the local game cache
    private function AdvanceLocalTime takes integer dt returns nothing
        local integer id = GetPlayerId(GetLocalPlayer())
        local integer syncedTime = GetPlayerSyncReceivedTime(id)
        set gameTime = gameTime + dt
        //send local time
        call StoreInteger(gc, TICK_KEY, I2S(id), gameTime)
        call SyncStoredInteger(gc, TICK_KEY, I2S(id))        
        //restore synced local time [to avoid desyncing cache values between players]
        call StoreInteger(gc, TICK_KEY, I2S(id), syncedTime)
    endfunction
    ///Determines the latest sync-received time from any player. [This value is the same for all players]
    private function ComputeSharedTime takes nothing returns integer
        local integer t = 0
        local integer id = 0
        loop
            exitwhen id >= 12
            set t = IMaxBJ(t, GetPlayerSyncReceivedTime(id))
            set id = id + 1
        endloop
        return t
    endfunction
    
    ///Advances the local time and identifies any laggers, without desyncing the game
    private function OnTick takes nothing returns nothing
        //display any lagging players
        local integer dt
        local integer sharedTime = ComputeSharedTime()
        local integer i = 0
        loop
            exitwhen i >= 12
            call TimerDialogDisplay(dialogs[i], false)
            if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i)) == MAP_CONTROL_USER then
                set dt = sharedTime - GetPlayerSyncReceivedTime(i)
                if dt > MAX_DELAY then //player is lagging
                    call TimerDialogSetTitle(dialogs[i], "("+WAIT_COMMAND+") |cFF"+colorCodes[i]+GetPlayerName(Player(i))+"|r lag")
                    call TimerStart(timers[i], dt/1000.0, false, null)
                    call PauseTimer(timers[i])
                    call TimerDialogDisplay(dialogs[i], true)
                endif
            endif
            set i = i + 1
        endloop

        call AdvanceLocalTime(TICK_PERIOD)
    endfunction
    
    private function init takes nothing returns nothing
        local trigger t
        local integer i
        
        //init ticker
        set t = CreateTrigger()
        call TriggerAddAction(t, function OnTick)
        call TriggerRegisterTimerEvent(t, TICK_PERIOD/1000.0, true)
        
        //init lag screen signalling command
        set t = CreateTrigger()
        call TriggerAddAction(t, function SignalBotToShowLagScreen)
        set i = 0
        loop
            exitwhen i >= 12
            call TriggerRegisterPlayerChatEvent(t, Player(i), WAIT_COMMAND, true)
            set i = i + 1
        endloop
        
        //preload timers and dialogs
        set i = 0
        loop
            exitwhen i >= 12
            set timers[i] = CreateTimer()
            set dialogs[i] = CreateTimerDialog(timers[i])
            set i = i + 1
        endloop

        //init game cache
        call FlushGameCache(InitGameCache(FILENAME))
        set gc = InitGameCache(FILENAME)
        call StoreInteger(gc, WAIT_KEY, WAIT_KEY, 0)
        
        //init color codes
        set colorCodes[0] = "FF0303" //red
        set colorCodes[1] = "0042FF" //blue
        set colorCodes[2] = "1CE6B9" //teal
        set colorCodes[3] = "540081" //purple
        set colorCodes[4] = "FFFC01" //yellow
        set colorCodes[5] = "FEBA0E" //orange
        set colorCodes[6] = "20C000" //green
        set colorCodes[7] = "FF00FF" //pink
        set colorCodes[8] = "808080" //grey
        set colorCodes[9] = "0080FF" //light blue
        set colorCodes[10] = "008000" //dark green
        set colorCodes[11] = "800000" //brown

        set t = null
    endfunction
endlibrary
Attached Images
File Type: png AsyncLatency.png (1.9 KB, 452 views)
__________________
Don't pay attention to this signature, it's self-contradictory.
Strilanc is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Old 01-24-2010, 08:04 PM   #2
Ammorth
I blink, therefore I am.
 
Ammorth's Avatar
 
Join Date: Sep 2006
Posts: 1,812

Submissions (10)

Ammorth is a glorious beacon of light (461)Ammorth is a glorious beacon of light (461)Ammorth is a glorious beacon of light (461)Ammorth is a glorious beacon of light (461)

Default

Looks nice.

One question though, if this is used when no bot is present, are there any side-effects? If so, is there a way to detect if a bot is present? That way, the script can be disabled for that hosting.
__________________
Ammorth is offline   Reply With Quote
Old 01-25-2010, 02:35 AM   #3
Strilanc
User
 
Strilanc's Avatar
 
Join Date: Jun 2007
Posts: 917

Submissions (4)

Strilanc has a spectacular aura about (131)

2008 Spell olympics - Fire - Gold

Default

Under a normal wc3 host, you might see the timer dialog for a fraction of a second before the lag screen pops up. Nothing serious at all.
__________________
Don't pay attention to this signature, it's self-contradictory.
Strilanc is offline   Reply With Quote
Old 04-16-2010, 04:00 PM   #4
Rising_Dusk
Obscurity, the Art


Projects Director
Project Leader: OD
 
Join Date: Feb 2006
Posts: 9,729

Submissions (27)

Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)

Hero Contest #3 - 1st PlaceApproved Map: Desert of ExileApproved Map: Advent of the ZenithHero Contest #2 - 1st PlaceHero Contest - Third place>

Send a message via AIM to Rising_Dusk Send a message via MSN to Rising_Dusk
Default

This seems to be more relocated to the duties of debugging than actually playing with all of the overhead of timers and timerdialogs. All of the timer dialogs seem to be a real hassle and would intrude upon lots of maps. Could you not just set a series of 12 global variables (not in an array, but a list, like DELAY_PLAYER_RED or something) and then give the user access to those globals to do with as necessary? I think that would be a better solution.
__________________
Rising_Dusk is offline   Reply With Quote
Old 04-16-2010, 05:08 PM   #5
Strilanc
User
 
Strilanc's Avatar
 
Join Date: Jun 2007
Posts: 917

Submissions (4)

Strilanc has a spectacular aura about (131)

2008 Spell olympics - Fire - Gold

Default

Hmmm, yeah that could work. It is, essentially, a way to measure delay in real time.
__________________
Don't pay attention to this signature, it's self-contradictory.
Strilanc is offline   Reply With Quote
Old 04-19-2010, 03:37 PM   #6
Rising_Dusk
Obscurity, the Art


Projects Director
Project Leader: OD
 
Join Date: Feb 2006
Posts: 9,729

Submissions (27)

Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)Rising_Dusk has a reputation beyond repute (1192)

Hero Contest #3 - 1st PlaceApproved Map: Desert of ExileApproved Map: Advent of the ZenithHero Contest #2 - 1st PlaceHero Contest - Third place>

Send a message via AIM to Rising_Dusk Send a message via MSN to Rising_Dusk
Default

Exactly, but it should be as un-intrusive as possible for the actual map itself, while still giving the player that option.
__________________
Rising_Dusk is offline   Reply With Quote
Old 07-06-2010, 06:05 PM   #7
Strilanc
User
 
Strilanc's Avatar
 
Join Date: Jun 2007
Posts: 917

Submissions (4)

Strilanc has a spectacular aura about (131)

2008 Spell olympics - Fire - Gold

Default

I finally got around to working on this.

Basically I'm splitting the system into two pieces:
- A 'LatencyMeasure' library, which just provides a GetPlayerLatency function and the machinations to make it work.
- The Async Lag library, which has the timer dialog / host bot command part of it.
__________________
Don't pay attention to this signature, it's self-contradictory.
Strilanc is offline   Reply With Quote
Old 07-20-2010, 07:58 PM   #8
Strilanc
User
 
Strilanc's Avatar
 
Join Date: Jun 2007
Posts: 917

Submissions (4)

Strilanc has a spectacular aura about (131)

2008 Spell olympics - Fire - Gold

Default

I have a candidate update but I'm not sure if I'm satisfied with it. There's an awful lot of variance in the measurement, because of how it works. I'm going to have to think of ways to improve it.

Collapse JASS:
///////////////////////////////////////////////////////////////////////////
/// Latency Measure Library
/// Version: v1.01
/// Author: Strilanc
/// Summary: Provides the ability to measure players' latency (command delay).
///////////////////////////////////////////////////////////////////////////
/// == Functions ==
/// GetPlayerLatency(player) returns a smoothed estimate of a player's latency.
/// GetPlayerRawLatency(player) returns a jumpy measurement of a player's current latency.
///
/// == How does it work? ==
/// The library works by sending out periodic per-player game cache syncs containing the game
/// time. This causes the time in game cache to lag behind the true game time based on their delay.
/// So players' latencies are measured as the difference between their cached time and the current time.
/// 
/// == Potential Pitfalls ==
/// - Requires vJass in order to compile.
/// - The latency is measured in game seconds, not real seconds. The main side effect of this is that
///   the measured latencies will never go higher than the time it takes for the lag screen to show up.
/// - Game cache syncs can prematurely end TriggerSleepAction calls. Do not use TriggerSleepAction
///   if you are using this library. Use PolledWait or timers instead (GUI people: use "Wait (Game-Time)"
///   instead of "Wait"). TriggerSleepAction has tons of problems anyways, so you should already not be using it.
/// - The system causes small amounts of network traffic.
/// - The system trusts clients to send accurate game times. A malicious client could manipulate their own measurement,
///   or even the measurements of other players. So *DO NOT* use this system for important effects like booting laggers.
///////////////////////////////////////////////////////////////////////////
library LatencyMeasure initializer init
    globals
        private constant real TICK_PERIOD = 0.5 //seconds
        private constant real ESTIMATE_RETAIN_FACTOR = Pow(0.75, TICK_PERIOD) //factor per tick
        
        private gamecache gc
        private real array latencies
        private timer clock = CreateTimer()
        
        private constant string FILENAME = "Latency.gc"
        private constant string TICK_KEY = "tick"
    endglobals
    
    ///Returns the current volatile measurement of a player's latency
    function GetPlayerRawLatency takes player p returns real
        return TimerGetElapsed(clock) - GetStoredReal(gc, TICK_KEY, I2S(GetPlayerId(p)))
    endfunction
    ///Returns a smoothed estimate of a player's latency
    function GetPlayerLatency takes player p returns real
        return latencies[GetPlayerId(p)]
    endfunction
    
    ///Synchronously stores a local real in game cache.
    ///The value is set after the sync action bounces off the host.
    private function SyncStoreLocalReal takes gamecache gc, string missionKey, string key, real value returns nothing
        local real oldValue = GetStoredReal(gc, missionKey, key)
        call StoreReal(gc, missionKey, key, value)
        call SyncStoredReal(gc, missionKey, key)
        call StoreReal(gc, missionKey, key, oldValue)
    endfunction
        
    ///Periodically sync-stores the local time and updates latency estimates
    private function OnTick takes nothing returns nothing
        ///Adjusts latency estimates towards the current measured latencies.
        local integer i = 0
        loop
            exitwhen i >= 12
            set latencies[i] = latencies[i] * ESTIMATE_RETAIN_FACTOR
            set latencies[i] = latencies[i] + GetPlayerRawLatency(Player(i)) * (1 - ESTIMATE_RETAIN_FACTOR)
            set i = i + 1
        endloop
        
        //sync-store the game time separately for each player
        call SyncStoreLocalReal(gc, TICK_KEY, I2S(GetPlayerId(GetLocalPlayer())), TimerGetElapsed(clock))
    endfunction
    
    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerAddAction(t, function OnTick)
        call TriggerRegisterTimerEvent(t, TICK_PERIOD, true)
        
        call FlushGameCache(InitGameCache(FILENAME))
        set gc = InitGameCache(FILENAME)
        
        call TimerStart(clock, 999999999, false, null)

        set t = null
    endfunction
endlibrary
__________________
Don't pay attention to this signature, it's self-contradictory.
Strilanc is offline   Reply With Quote
Old 07-21-2010, 08:17 PM   #9
Tot
6
 
Tot's Avatar
 
Join Date: Oct 2008
Posts: 841

Tot will become famous soon enough (53)Tot will become famous soon enough (53)

Default

nitpicker:

Collapse JASS:
            set latencies[i] = latencies[i] * ESTIMATE_RETAIN_FACTOR + GetPlayerRawLatency(Player(i)) * (1 - ESTIMATE_RETAIN_FACTOR)
instead of
Collapse JASS:
            set latencies[i] = latencies[i] * ESTIMATE_RETAIN_FACTOR
            set latencies[i] = latencies[i] + GetPlayerRawLatency(Player(i)) * (1 - ESTIMATE_RETAIN_FACTOR)
__________________
Current Projects:
  • Masters Of WarCraft: Some mixture of AoS and RPG
    Terrain: 100%, Coding: 75%, Heroes: 0%, Items: 0%, Creeps: 0%, Upgrades: 0%
  • hunting emos
____________________________________
scheiss kack dreck sausacksau bundeswehr
Tot is offline   Reply With Quote
Old 07-22-2010, 12:41 PM   #10
Strilanc
User
 
Strilanc's Avatar
 
Join Date: Jun 2007
Posts: 917

Submissions (4)

Strilanc has a spectacular aura about (131)

2008 Spell olympics - Fire - Gold

Default

Quote:
Originally Posted by Tot
nitpicker:

Collapse JASS:
            set latencies[i] = latencies[i] * ESTIMATE_RETAIN_FACTOR + GetPlayerRawLatency(Player(i)) * (1 - ESTIMATE_RETAIN_FACTOR)
instead of
Collapse JASS:
            set latencies[i] = latencies[i] * ESTIMATE_RETAIN_FACTOR
            set latencies[i] = latencies[i] + GetPlayerRawLatency(Player(i)) * (1 - ESTIMATE_RETAIN_FACTOR)

Seriously?
I just don't like putting a huge constant name on the same line twice.
__________________
Don't pay attention to this signature, it's self-contradictory.
Strilanc is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off


All times are GMT. The time now is 08:08 PM.


Affiliates
The Hubb The JASS Vault Clan WEnW Campaign Creations Clan CBS GamesModding Flixreel Videos

Powered by vBulletin (Copyright ©2000 - 2019, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services