Desync issue: Evevators + GetlocationZ / GetTerrainCliffLevel

Level 3
Joined
Jan 1, 2021
Messages
7
I found a desynchronization problem in reforged, which is reproduced in 100% of cases. It can be reproduced on one computer in a local network, completely in SD graphics.

The problem with the GetLocationZ() has been known for a long time, but it was previously believed that desync was caused by the difference between SD and HD graphics in different players.

I believe that this is not true and desync will occur even if all players in one session have only SD or only HD graphics.

If you use the GetLocationZ() or GetTerrainCliffLevel() functions at the point where destructible objects such as Elevator ('DTrf', 'DTrx') or FootSwitch ('DTfp', 'DTfx') are placed. And based on this data you perform various actions, then this will lead to desynchronization.

Disabling the "Pathing - Is Walkable" property solves the problem, but the destructible loses its key properties.

Screenshot_2.png


I have attached the map (ElevatorDesyncRef.w3x). Here is a simple code for testing. You don't need other players for testing. Just run two instances of wc3r offline and create a LAN game. In the game, use the ability with a projectile via Elevator or FootSwitch (both bottom), the second player will be desynchronized. The top Elevator and FootSwitch are safe.
You may need to use the ability several times at different angles.

Screenshot_1.png


However, I have not been able to reproduce this issue with any other destructible objects that have the "Pathing - Is Walkable" property. It is possible that the Elevator and FootSwitch have unique animations.
 

Attachments

  • ElevatorDesyncRef.w3x
    17 KB · Views: 16
I have a problem with the provided test code in the map and the claim about GetTerrainCliffLevel desyncs. In the maps code GetTerrainCliffLevel depends on the result of GetLocationZ. Therefore the code can not tell anything about GetTerrainCliffLevel.

Code from the map
JASS:
if GetLocationZ(nloc) > height then
        if GetLocationZ(sloc) == GetLocationZ(nloc) then
            set height=GetLocationZ(nloc)
            set zvelocity=- 0.5 * zvelocity
        else
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // Both functions will cause desynchronization if you use them at a point on the edge of an Elevator or FootSwitch.                     //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            if GetTerrainCliffLevel(GetLocationX(nloc), GetLocationY(nloc)) > GetTerrainCliffLevel(GetLocationX(sloc), GetLocationY(sloc)) then //
                call KillUnit(slide) //
            endif //
            // OR                                                                                                                               //
            if GetLocationZ(nloc) > GetLocationZ(sloc) then //
                call KillUnit(slide) //
            endif //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        endif
 endif

Before GetTerrainCliffLevel even matters first the "if using GetLocationZ" needs to be passed, which to the other claim is already not netsafe.
 
Last edited:
Level 3
Joined
Jan 1, 2021
Messages
7
I have a problem with the provided test code in the map and the claim about GetTerrainCliffLevel desyncs. In the maps code GetTerrainCliffLevel depends on the result of GetLocationZ. Therefore the code can not tell anything about GetTerrainCliffLevel.

Before GetTerrainCliffLevel even matters first the "if using GetLocationZ" needs to be passed, which to the other claim is already not netsafe.

This is a sad mistake, I agree with you. This post focuses on Elevators and GetLocationZ, but does not sufficiently explain the problems of the GetTerrainCliffLevel function.

I am attaching the updated map and code, here only the GetTerrainCliffLevel function is used. My tests show that GetTerrainCliffLevel causes desynchronization much less often, but it still causes it.

In the updated map, to achieve the effect of desynchronization, the projectile must be launched about ten times. Try to change direction, including to touch the edge of the cliff.

JASS:
function go_slide takes nothing returns nothing
    local timer k = GetExpiredTimer()
    local unit slide = LoadUnitHandle(udg_ht, GetHandleId(k), StringHash("slide"))
    local real zvelocity = LoadReal(udg_ht, GetHandleId(k), StringHash("zvelocity"))
    local real angle = LoadReal(udg_ht, GetHandleId(k), StringHash("angle"))
    local real height = LoadReal(udg_ht, GetHandleId(k), StringHash("height"))
    local real zforce = LoadReal(udg_ht, GetHandleId(k), StringHash("force"))
    local location sloc = GetUnitLoc(slide)
    local location nloc = PolarProjectionBJ(sloc, zforce, angle)

    call SetUnitFlyHeight(slide, height - I2R(GetTerrainCliffLevel(GetLocationX(sloc), GetLocationY(sloc))), 0)

    if GetTerrainCliffLevel(GetLocationX(nloc), GetLocationY(nloc)) > height then
        if GetTerrainCliffLevel(GetLocationX(sloc), GetLocationY(sloc)) == GetTerrainCliffLevel(GetLocationX(nloc), GetLocationY(nloc)) then
            // set height = GetLocationZ(nloc)
            set height = GetTerrainCliffLevel(GetLocationX(nloc), GetLocationY(nloc))
            set zvelocity = -0.5 * zvelocity
        else
            if GetTerrainCliffLevel(GetLocationX(nloc), GetLocationY(nloc)) > GetTerrainCliffLevel(GetLocationX(sloc), GetLocationY(sloc)) then //
                call KillUnit(slide)                                                                                                            //
            endif                                                                                                                               //
        endif
    endif

    call SetUnitPositionLoc(slide,nloc)
    call SetUnitFacing(slide,angle)
    call RemoveLocation(nloc)
    call RemoveLocation(sloc)
    
    if IsUnitDeadBJ(slide) then
        call PauseTimer(k)
        call DestroyTimer(k)
    endif
    
    call SaveReal(udg_ht, GetHandleId(k), StringHash("height"), height + zvelocity/25.0)
    call SaveReal(udg_ht, GetHandleId(k), StringHash("zvelocity"), zvelocity - 40)
    call SaveReal(udg_ht, GetHandleId(k), StringHash("angle"), angle)
    
    set k = null
    set slide = null
    set sloc = null
    set nloc = null
endfunction
 

Attachments

  • ElevatorDesyncRef2.w3x
    17.1 KB · Views: 5
sorry, But you example code still depends on GetLocationZ, so it can not say, if GetTerrainCliffLevel is a desyncs source or not. To say that GetTerrainCliffLevel desyncs you need a isolated case without such other network unsafe factors.

function go_act takes nothing returns nothing
call SaveReal(udg_ht, GetHandleId(k), StringHash("height"), GetLocationZ(castloc))

->


local real height= LoadReal(udg_ht, GetHandleId(k), StringHash("height"))
...
if GetTerrainCliffLevel(GetLocationX(nloc), GetLocationY(nloc)) > height then
....
call SaveReal(udg_ht, GetHandleId(k), StringHash("height"), height + zvelocity / 25.0)
 
Top