• 🏆 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!
  • ✅ Time to vote for the top 3 models! The POLL for Hive's 6th HD Modeling Contest: Mechanical is now open! 📅 Poll close on July 16, 2024! 🔗 Cast your vote now!

[Wurst] Desync Issue

Not open for further replies.
I am currently working on this map : Island Troll Tribes
github Link : island-troll-tribes/island-troll-tribes

Since 1st december, we lost our bot and got alot of desync issue, we "fixed" some of them, blizzard update fixed others, from time to time, 1 player still desync, but recently, on the latest version I made, I got issue where half/every player in the game just desync, happens on 1 game out of 3.

Desync happens "randomly", they can happen at 5, 10, 17, 23min into the game, but it doesn't seem really consistent. I watched some replays and didn't find any leads.

Last version without much desync, is the Beta 34, it was compiled before last blizzard update, to make sure issue comes from the update or my code, I'll have to revert the current version(Beta 36) and re-compile, good thing I use git, will do that when I have the time to test with players.

I've read some Hive thread about desync, here's some information I got out of it and compared with my issues :
-Mac user can be the only one to desync.
-We got custom constant & interface, haven't tried to reset them yet.
-We got alot of items spawning randomly on the map.
-I got lot of custom models, don't know if it can cause a desync.

There are several uses of GetLocalPlayer(), I kinda trust the guy who made those, but I'll share a few just in case:

public function ZoomSetCamera takes integer i returns nothing
     if GetLocalPlayer()==Player(i) then
        call SetCameraField(CAMERA_FIELD_ZOFFSET,ZOOM_DISTANCE[i],1)
        call SetCameraField(CAMERA_FIELD_FARZ,5000,0)
package SimError

sound error

public function simError(player forPlayer, string msg)
let redMsg = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00" + msg +"|r"
if GetLocalPlayer() == forPlayer
     DisplayTimedTextToPlayer(forPlayer, 0.52, 0.96, 5.00, redMsg)

     error = CreateSoundFromLabel("InterfaceError", false, false, false, 10, 10)

I also have 2 short replays, one desynced whole game at 4:22m, the others desynced 3 players at 4:30m, I didn't desync on the second one. I got more replays, but they're longer, like 17 or 30min.

So I am asking for clues, at this moment I am kinda lost, I need player to test and try to find a pattern, feels bad to fill a lobby just to look for crashes.


  • ITT Desync 422m.w3g
    63.8 KB · Views: 38
  • ITT Desync 430m.w3g
    82.4 KB · Views: 31
Code snippets above look fine.

One thing to know is that interface changes and other map metadata seems to cause desyncs.

To make matters worse, desync can happen just because the player was in a previous game where interface changes were applied.

I can't confirm those last two points on 1.31.x though.

Can you provide war3map.j?
Here's some more uses:

function texttag_setVisibility takes texttag this_2, boolean flag_2 returns nothing
    call SetTextTagVisibility(this_2, flag_2)

function canPlayerSeeDamageText takes player p_2, unit target_2, string stackPos returns boolean
    local boolean stackTrace_tempReturn
    set wurst_stack[wurst_stack_depth] = stackPos
    set wurst_stack_depth = wurst_stack_depth + 1
    set stackTrace_tempReturn = dispatch_LinkedList_LinkedList_LinkedList_has(DamageTexts_damageTextPlayers, playerToIndex(p_2), "when calling has in DamageTexts, line 29") and IsUnitVisible(target_2, p_2)
    set wurst_stack_depth = wurst_stack_depth - 1
    return stackTrace_tempReturn

call texttag_setVisibility(receiver_8, canPlayerSeeDamageText(GetLocalPlayer(), u_2, "when calling canPlayerSeeDamageText in DamageTexts, line 25"))

function player_getTribe takes player this_2, string stackPos returns integer
    local integer stackTrace_tempReturn
    set wurst_stack[wurst_stack_depth] = stackPos
    set wurst_stack_depth = wurst_stack_depth + 1
    set stackTrace_tempReturn = Tribe_ofPlayer(this_2, "when calling ofPlayer in PlayerExtensions, line 10")
    set wurst_stack_depth = wurst_stack_depth - 1
    return stackTrace_tempReturn

                set display = player_getTribe(GetLocalPlayer(), "when calling getTribe in Respawn, line 83") == player_getTribe(owner_2, "when calling getTribe in Respawn, line 83")
                call timerdialog_setTitle(timerDialog_2, string_format_1("{0} Respawn", player_getName(owner_2)))
                call timerdialog_display(timerDialog_2, display)

function pingMinimap_6691 takes real pos_x_2, real pos_y_2, real duration_2 returns nothing
    call PingMinimap(pos_x_2, pos_y_2, duration_2)

function pingMinimap_6692 takes real pos_x_2, real pos_y_2 returns nothing
    call pingMinimap_6691(pos_x_2, pos_y_2, PingMinimap_DEFAULT_DURATION)

function Hint_display takes integer this_2 returns nothing
    if force_containsPlayer(Hint_playersF, Player_localPlayer) then
        call printTimed(LegacyColors_GOLD_COLOR + "Hint: |r" + LegacyColors_GENERAL_COLOR + Hint_message[this_2] + "|r", 10.)
        if Hint_ping_x[this_2] != 0. or Hint_ping_y[this_2] != 0. then
            call pingMinimap_6692(Hint_ping_x[this_2], Hint_ping_y[this_2])

function TeleportBeacon_onSelected takes integer this_2, string stackPos returns nothing
    local player whichPlayer
    local unit beacon
    local integer level
    local real range_2
    local real beaconPos_x
    local real beaconPos_y
    local integer list
    local string path
    local string cond_result
    local integer degs
    local real angle_radians_3
    local real pos_x_2
    local real pos_y_2
    local real tuple_temp
    local real tuple_temp_2
    local real tuple_temp_3
    local real tuple_temp_4
    local real tuple_temp_5
    set wurst_stack[wurst_stack_depth] = stackPos
    set wurst_stack_depth = wurst_stack_depth + 1
    set whichPlayer = GetTriggerPlayer()
    if dispatch_HashMap_HashMap_HashMap_has(TeleportBeacon_effects[this_2], playerToIndex(whichPlayer), "when calling has in TeleportBeacon, line 51") then
        set wurst_stack_depth = wurst_stack_depth - 1
        set whichPlayer = null
        set beacon = null
    set beacon = dispatch_UnitEntity_UnitEntity_UnitEntity_getUnit(this_2, "when calling getUnit in TeleportBeacon, line 54")
    set level = unit_getAbilityLevel(beacon, TeleportBeacon_SPELL_TELEPORT_BEACON)
    set range_2 = TeleportBeacon_RANGE[level]
    set tuple_temp = unit_getPos(beacon)
    set tuple_temp_2 = unit_getPos_return_y
    set beaconPos_x = tuple_temp
    set beaconPos_y = tuple_temp_2
    set list = new_LinkedList("when calling new_LinkedList in TeleportBeacon, line 58")
    if Player_localPlayer == whichPlayer then
        set cond_result = TeleportBeacon_rangeCheckModFirst
        set cond_result = ""
    set path = cond_result
    set degs = 0
        exitwhen degs > 340
        set tuple_temp_3 = real_fromDeg(int_toReal(degs))
        set angle_radians_3 = tuple_temp_3
        set tuple_temp_4 = vec2_polarOffset(beaconPos_x, beaconPos_y, angle_radians_3, range_2)
        set tuple_temp_5 = vec2_polarOffset_return_y
        set pos_x_2 = tuple_temp_4
        set pos_y_2 = tuple_temp_5
        call dispatch_LinkedList_LinkedList_LinkedList_add_1(list, effectToIndex(addEffect(path, pos_x_2, pos_y_2)), "when calling add in TeleportBeacon, line 64")
        set degs = degs + 20
    call dispatch_IterableMap_HashMap_IterableMap_put(TeleportBeacon_effects[this_2], playerToIndex(whichPlayer), list, "when calling put in TeleportBeacon, line 66")
    set wurst_stack_depth = wurst_stack_depth - 1
    set whichPlayer = null
    set beacon = null

@Jaccouille does your desync happen when a debug stack trace appears?
Not open for further replies.