Skip to content
Stijn edited this page Sep 8, 2024 · 13 revisions

This is a list of things that can cause a desync in 1.32 and later. These are not caused by HiveWE, but having a list is handy.

General

  1. Code in GetLocalPlayer() blocks will cause desyncs if it makes changes that should have happened on both player clients at the same time. The following will cause a desync because the unit is deleted on only one client.
if (GetTriggeringPlayer() == GetLocalPlayer) then
    KillUnit(GetTriggeringUnit())
endif

But the following will not because a UI frame being visible or not is not something that needs to be synchronised between players.

if (GetTriggeringPlayer() == GetLocalPlayer) then
    HideFrame(SomeFrameID)
endif
  1. Using GetLocationZ() for things that need to be synced between clients (e.g. killing a unit) will cause desyncs as there are height differences between Classic and Reforged graphics.
  2. The CreepCampPathingCellDistance gameplay constant will desync if players don't restart their game before playing your map.
  3. Frames are cached between maps, so if you create a frame before loading your .toc it can create the frame for some people but not others.
  4. BlzDestroyFrame() will cause desyncs
  5. (Unconfirmed) Using Player Slot/Controller comparisons on initialization can occasionally cause desyncs because at the time of the initialization you don't necessarily have the information on other players

Lua specific

  1. Creating objects outside of the main function in Lua is prone to desyncs. To lazily solve this I moved TSTL's module creation inside of the main function.
  2. Using StartTimerBJ and bj_lastStartedTimer will cause desyncs. This also means that the GUI Countdown Timer - Start Timer will desync. Use TimerStart() in a custom script block instead.
  3. Looping table entries with pairs doesn't guarantee the order of entries is the same across multiple clients, therefore this can cause desyncs if you're handling sensitive game data/objects. To circumvent this, always make loopable tables with integer keys instead and use ipairs. Tables with non-integer keys are fine as long as you're not looping them.
  4. local unitID = GetHandleId(unit) leads to desyncs. It's never needed though, as you can use the Unit object itself as the key of arrays or lists, meaning the handle ID has no purpose.

GetLocalPlayer() specific

Not all functions are async safe and can be used inside of a GetLocalPlayer() block. Obvious ones are KillUnit() but less obvious ones exist.

ForForce()
ForGroup()
GetRandomInt()
GetRandomReal()
math.random()
Select group for player GUI