1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still haven't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. Rubbed the right way, the genie is out of its lamp! The 12th Concept Art Contest Results have been announced.
    Dismiss Notice
  4. The Lich King has a new champion. The results for the 19th Icon Contest are here!
    Dismiss Notice
  5. Race against the odds and Reforge, Don't Refund. The 14th Techtree Contest has begun!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

desync problems (lua)

Discussion in 'World Editor Help Zone' started by bigbull, Mar 9, 2020.

  1. bigbull

    bigbull

    Joined:
    Mar 7, 2020
    Messages:
    6
    Resources:
    0
    Resources:
    0
    hi,

    i have desync problems with one of my custom resurrection abilitys. If any player uses this ability, everyone gets kicket out (no error messege) to the score board. This ability works fine in single player. The desync happen on cast.
    My lua code with some comments, is below.

    Thanks in advance

    Code (vJASS):

        function ReviveClass:Execute(info)
            //get death units
            local deathUnits = UnitClass.GetDeathInRange(self.radius, info.targetLoc)
            local count = 0
            for _, v in pairs(deathUnits) do
                local loc = GetUnitLoc(v)
                local id = GetUnitTypeId(v)
                local typeObj = UnitTypeClass.UNITS[GetUnitTypeId(v)] //table with information if its a hero, building and much more
                local orderble = typeObj:CanGetOrder() //simple boolean check if its a hero, building ...
                //if its not a building ... create a new one (same type) and delete the corpse
                if orderble then
                    //UnitClass.NewUnit ... Create a unit and store in a table with some information
                    //Im using this a lot, so it shouldn be the problem
                    local unitObj = UnitClass.NewUnit(units[info.unit],loc,id)
                    RemoveUnit(v)
                    RemoveLocation(loc)
                    count = count + 1
                    //resurrect max 2 units
                    if count >= self.max then break end //max=2
                end
            end
            //create an effect at location for 4.5 sec
            CreateEffectLoc(4.5,info.targetLoc,"Abilities\\Spells\\Human\\Resurrect\\ResurrectCaster.mdl")
        end

        UnitClass.GetDeathInRange = function(radius, loc)
            local death = {}
            local g = GetUnitsInRangeOfLocAll(radius, loc)
            local u = FirstOfGroup(g)
            while u ~= nil do
                if IsUnitAliveBJ(u) == false then
                    death[u]=u
                end
                GroupRemoveUnit(g, u)
                u = FirstOfGroup(g)
            end
            DestroyGroup(g)
            return death
        end

        function CreateEffectLoc(duration,loc,model)
            local ExpiredFunc = function()
                local t = timers[GetExpiredTimer()]
                local e = t.arg[1]
                BlzSetSpecialEffectPosition(e,0, 0, 1000)
                DestroyEffectBJ(e)
                t:Remove()
            end
            local effect = AddSpecialEffectLoc(model, loc)
            TimerClass:Create(duration,false,ExpiredFunc,effect)
        end

        //Im using this timer a ton, so it shouldn be the problem
        timers = {}
        TimerClass = {}
        function TimerClass:Create(duration,isPeriodic,ExpiredFunc,...)
            local this = {}
            this.this = CreateTimer()
            this.duration = duration
            this.isPeriodic = isPeriodic
            this.ExpiredFunc = ExpiredFunc
            this.arg = {...}
            self.__index = self
            setmetatable(this, self)
            timers[this.this] = this
            TimerStart(this.this, duration, isPeriodic, this.ExpiredFunc)
            return this
        end

        function TimerClass:Remove()
            if self.isPeriodic == false then
                DestroyTimer(self.this)
                timers[self] = nil
            end
        end
     
     
    Last edited: Mar 9, 2020
  2. bigbull

    bigbull

    Joined:
    Mar 7, 2020
    Messages:
    6
    Resources:
    0
    Resources:
    0
    The desync only happening in bnet games. I can't reproduce it on local network...
     
  3. Jilaer

    Jilaer

    Joined:
    Aug 27, 2019
    Messages:
    5
    Resources:
    0
    Resources:
    0
    Maybe it was caused by pairs
     
  4. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    875
    Resources:
    0
    Resources:
    0
    Apparently the garbage collector can cause problems.

    Quoting someone from this thread Narrowing Down Desync Cause

    "UPDATE 2: Seems by default wc3 will use something called `__jarray(0.0)` when you create a global array of reals. If I change that to use `{}` the desync comes back. So I guess something is wrong with `{}` for whatever reason in lua. I don't really understand why that would matter, since `__jarray(0.0)` just makes a table anyway...

    Maybe it's related to the sizing of the lua tables. From what I understand whenever you hit the size limit it doubles it internally. So maybe `__jarray()` starts it with a large size already or something...

    UPDATE 3: If anyone comes here from google or search or whatever, I found out it was caused by lua garbage collection. You can turn it off to verify, and then manually call it on a timer to sync it for each player and that fixes the issue!"
     
  5. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,700
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    There are definitely some things to watch out for with garbage collection. For example if you create a global trigger outside of any scope it will get garbage collected (maybe at different times for each player), at least last time I tested.

    Also, you say the code desyncs when the ability is cast but I don't see any code related to casting in the script you posted?

    You can no longer disable garbage collection. It only worked during the beta.
     
  6. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    875
    Resources:
    0
    Resources:
    0
    Do you know if Pairs causes issues as well? Because i'm using it A LOT, lol.
     
  7. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,700
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    The order of pairs is undefined so yes it can desync depending on how you are using it. If you need to rely on order then you can use ipairs or something like ordered pairs. There is even a compressed version that can replace pairs.
     
  8. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    875
    Resources:
    0
    Resources:
    0
    Thanks for the information! Time to go find and replace all of my pairs with ipairs.