• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Random desyncs from custom UI?

Status
Not open for further replies.
Level 9
Joined
Mar 26, 2017
Messages
376
I'm just testing my new map out, and I'm getting a LOT of issues with random desync. Like 50% of the players get the boot in the first few minutes of the game.

Now my map is Lua with custom UI. The desyncs all happen in the first few minutes of the game, during the creation of custom UI elements, and the host settings process (which is also done via custom UI buttons). I have broken down the UI creation process in dozens of tiny steps seperated by timers, in an attempt to isolate problems. However, the desync happens at different stages of the loading process.

There are UI modifications in local blocks, but by not calling CreateFrame and GetFrameByName in local blocks I think I done right. Also not 100% of the players desync, which would be expected. In my repeated tests, I also do not see that repeated slots desync.

This is just really disheartening and it seems almost impossible to find out what is causing it, because of randomly occuring, and happening at different times.


Maybe some devs here can share me some lessons learned from their creation process, and difficult obstacles they encountered when making their custom UI work. In the hope that maybe there is something that is relatable to the problems I'm having now. Thanks.
 
There is an issue where a player can desync if you create frames while Warcraft III is not in focus (alt-tabbed).

It may also be unsafe to create handles outside of the main function. For example:

Lua:
-- bad
myFrame = BlzCreateFrame(...)

function main()
 
end

Lua:
-- good
myFrame = undefined

function main()
   myFrame = BlzCreateFrame(...)
end

Creating handles in triggers or timer callbacks that were registered in the main function should be fine.

You should also be careful as some frame natives can be async depending on your usage. For example BlzFrameIsVisible may return different values if you control frame visiblity locally.

Another issue is that getting the client's width or height might return zero if the game client is not in focus, and depending on what you do with that (dividing by zero for example) might give different results on each client.

If you do end up solving your desync problem it would be useful if you reported how you did so here.
 
Level 9
Joined
Mar 26, 2017
Messages
376
Thanks TriggerHappy.

I didn't know you were supposed to create frames in the main function only. I create all my UI at the game start, and also a few minutes in (after host settings have been selected). This is a great help, thanks! I will change this, and report back.

Also perhaps interesting for others. In my desync testing I did come across 3 issues that caused desync, that I have fixed since:
1. I used BlzTriggerRegisterPlayerSyncEvent on the local player only, instead of all players. I thought this was fine, as the end result would be sent to all players anyway. But this was just a misunderstanding of mine of how this trigger works.
2. I accidentily put BlzGetFrameByName in a local block.
3. I accidentily disabled a keypress trigger locally. The way it should be done is create one seperate trigger for each player that can be disabled/enabled in the global game state.
 
Level 9
Joined
Mar 26, 2017
Messages
376
I am just rereading your post now, and I see one thing I don't quite understand. 'Creating handles in triggers or timer callbacks that were registered in the main function should be fine.'

The way my map is built up;
1. in the Main function I do little stuff, and from there I start a timer with 0 second timeout that calls 'Start'
2. in the 'Start' function I create a bunch of UI, that has buttons with press events attached to them
3. after pressing a 'Confirm' button in this UI, a function 'InitPlayers' is started up, where all event registration is done, and a second set of UI is created. Also all of the map units are created at this point
4. the second set of UI also features player choice (skill selection) and has a confirm button. after the second confirm, the rest of the custom UI is created and from here players gain vision of the map and their units

Now I don't understand if this falls under the 'good' way. Because it all basically flows like this: main>timer>trigger event>timer>trigger event>...

I wouldn't even understand how it is possible to call any function, without somehow starting a timer or trigger registration from within the main or config function.

All I put in config is a line: 'LobbyTime = os.clock()', for the host detection system.
 
Also perhaps interesting for others. In my desync testing I did come across 3 issues that caused desync, that I have fixed since:
1. I used BlzTriggerRegisterPlayerSyncEvent on the local player only, instead of all players. I thought this was fine, as the end result would be sent to all players anyway. But this was just a misunderstanding of mine of how this trigger works.
2. I accidentily put BlzGetFrameByName in a local block.
3. I accidentily disabled a keypress trigger locally. The way it should be done is create one seperate trigger for each player that can be disabled/enabled in the global game state.

Yeah, all of those will cause desyncs :D I was listing some of the more unusual reasons for desyncing with frames, but all of the standard reasons apply as well.

I am just rereading your post now, and I see one thing I don't quite understand. 'Creating handles in triggers or timer callbacks that were registered in the main function should be fine.'

The way my map is built up;
1. in the Main function I do little stuff, and from there I start a timer with 0 second timeout that calls 'Start'
2. in the 'Start' function I create a bunch of UI, that has buttons with press events attached to them
3. after pressing a 'Confirm' button in this UI, a function 'InitPlayers' is started up, where all event registration is done, and a second set of UI is created. Also all of the map units are created at this point
4. the second set of UI also features player choice (skill selection) and has a confirm button. after the second confirm, the rest of the custom UI is created and from here players gain vision of the map and their units
Now I don't understand if this falls under the 'good' way. Because it all basically flows like this: main>timer>trigger event>timer>trigger event>...

I wouldn't even understand how it is possible to call any function, without somehow starting a timer or trigger registration from within the main or config function.

That should be fine, yes.

All I put in config is a line: 'LobbyTime = os.clock()', for the host detection system.

That is fine as well, but I suppose it's how you end up using LobbyTime.
 
Level 9
Joined
Mar 26, 2017
Messages
376
Yeah, all of those will cause desyncs :D I was listing some of the more unusual reasons for desyncing with frames, but all of the standard reasons apply as well.

That should be fine, yes.

That is fine as well, but I suppose it's how you end up using LobbyTime.

Just figured I would list those mistakes in this topic for completeness. I am not that technical, so the standard issues I am able to find myself. But the more complex ones elude me.

If the flow I outlined above should be good, I really don't know if I will ever fix the issue. As a last ditch effort I will just try to run all CreateFrame and FrameByName calls in main immediately. I will post the results in this topic. If it fails, I think there is no way around making major cuts :(
 
Just figured I would list those mistakes in this topic for completeness. I am not that technical, so the standard issues I am able to find myself. But the more complex ones elude me.

If the flow I outlined above should be good, I really don't know if I will ever fix the issue. As a last ditch effort I will just try to run all CreateFrame and FrameByName calls in main immediately. I will post the results in this topic. If it fails, I think there is no way around making major cuts :(

Can you confirm that people aren't alt-tabbing the game when frame creation is happening?
 
Level 9
Joined
Mar 26, 2017
Messages
376
Can you confirm that people aren't alt-tabbing the game when frame creation is happening?

I believe this is indeed the case. For instance, when I tested my map with 12 players, the loading took a very long time. Probably because some players did not press on 'confirm' at the loading screen, which caused the loading screen to stay until the maximum time out.
This is a very good angle though, I will run a specific test asking specific players to be tabbed in and tabbed out to confirm this.

Though if frames require all players to be tabbed in, that would basically make them unusable for my map. As my map is competitive and played in a random lobby, and wait times tend to be long, there is no way not at least one player is going to be tabbed out during frame loading.

So is it possible for me to use custom frames at all in this context?
 
I believe this is indeed the case. For instance, when I tested my map with 12 players, the loading took a very long time. Probably because some players did not press on 'confirm' at the loading screen, which caused the loading screen to stay until the maximum time out.
This is a very good angle though, I will run a specific test asking specific players to be tabbed in and tabbed out to confirm this.

Though if frames require all players to be tabbed in, that would basically make them unusable for my map. As my map is competitive and played in a random lobby, and wait times tend to be long, there is no way not at least one player is going to be tabbed out during frame loading.

So is it possible for me to use custom frames at all in this context?

I haven't done any testing into what specifically causes the desync. It could be any created frames, or it could be some specific natives while alt-tabbed. I'm not sure, but it may be worth looking into. I've also had it where I did not desync while alt-tabbed, so it doesn't seem to happen 100% of the time.

There is actually a native to tell if WC3 is in focus, so maybe that can help debug.
 
  • Like
Reactions: MWM
Level 9
Joined
Mar 26, 2017
Messages
376
I haven't done any testing into what specifically causes the desync. It could be any created frames, or it could be some specific natives while alt-tabbed. I'm not sure, but it may be worth looking into. I've also had it where I did not desync while alt-tabbed, so it doesn't seem to happen 100% of the time.

There is actually a native to tell if WC3 is in focus, so maybe that can help debug.

That sounds terrible, did you manage to fully fix the problem in the end?


As an update, I have just done the alt-tab test on Bnet with 6 players, and asked one specific player to tab out.
He did desync very early in the process, and all other players were still in. He desynced at the exact moment where a unit was created and the player trigger registrations were done.

A little bit further in the process though, another player desynced. I didn't remember the exact functions that run at that point.

Further in, I myself was desynced somehow. At a part with a few local BlzFrameSetTexture and BlzFrameSetEnable calls.
 
Last edited:

Uncle

Warcraft Moderator
Level 70
Joined
Aug 10, 2018
Messages
7,391
I personally don't use GetLocalPlayer with anything other than BlzFrameSetVisible. That's always been my strategy and I have added custom UI to a few maps without issues (for the most part).

Although, I think I've experienced the alt-tab issue before, one map I was working on would occasionally desync a player at the start of the game, but never beyond that point.

I could never figure out why but now I think it must have been that they were alt-tabbed during the loading screen or while the UI was being created.

Shame Blizzard doesn't seem to care about this game, I fear these issues will never be fixed or at the very least addressed.
 
Level 9
Joined
Mar 26, 2017
Messages
376
Wanted to update this. I took a small break because the issue left me demotivated.
When players are tabbed in it goes fine, but tabbed out means desync.

I have received a suggestion that moving the entire map code past a 0 second timer might work, but it didn't work for me. I believe that @Lasz managed to make the Direct Strike map UI work with this method. His UI also had complexity and logic, but there seem to be no desync issues.

I wonder if any mappers who struggled with this issue have lessons learned and can chime in on this.

I'm now on the verge of stripping all custom UI from my map entirely. Although it added alot and made it visually attractive. It is not needed for the map to play well, and it's just an addition. I can exchange most functionality with dialogs, multiboard, and unit abilities. It's a shame because the UI alone has been months of work.
 

Uncle

Warcraft Moderator
Level 70
Joined
Aug 10, 2018
Messages
7,391
Have you tried testing the map with certain portions of the UI disabled? I don't think you need to scrap ALL of the code when it's probably one line out of 100's causing the issue. If you spent months making it, it's probably worth a day or two of rigorous testing.
 
Wanted to update this. I took a small break because the issue left me demotivated.
When players are tabbed in it goes fine, but tabbed out means desync.

I have received a suggestion that moving the entire map code past a 0 second timer might work, but it didn't work for me. I believe that @Lasz managed to make the Direct Strike map UI work with this method. His UI also had complexity and logic, but there seem to be no desync issues.

I wonder if any mappers who struggled with this issue have lessons learned and can chime in on this.

I'm now on the verge of stripping all custom UI from my map entirely. Although it added alot and made it visually attractive. It is not needed for the map to play well, and it's just an addition. I can exchange most functionality with dialogs, multiboard, and unit abilities. It's a shame because the UI alone has been months of work.

The current version of Direct Strike is in JASS, and just like you I also had this exact problem back when the map was Lua. I was also very demotivated, but simply translating everything to JASS made it work.

Right now though I'm in the process of rewriting it again, this time in Typescript. I did some early tests to make sure it didn't desync, but sure enough it did desync again. However, @Damage gave me the advice to not run anything on map init and so far it hasn't desynced once in my tests since then. I need more people later on to make sure it's desync free, but so far running everything after a 0.01 second timer has worked for me.
 
Level 9
Joined
Mar 26, 2017
Messages
376
Have you tried testing the map with certain portions of the UI disabled? I don't think you need to scrap ALL of the code when it's probably one line out of 100's causing the issue. If you spent months making it, it's probably worth a day or two of rigorous testing.
I had initially put more than a week of rigorous testing when this UI issue came to light. At first I thought it was wrong coding. So I have split up the execution in like 40-50 parts in trying to localise at what exact time the desync occurs. I failed, it happens at different times, and sometimes it happens, sometimes it doesn't. I now find it is because of tabbing, but I still don't know which exact part of the UI will break from tabbing.

You are right it would be a waste to throw it all away. My plan is to strip away someparts that I believe are most sensitive, and then look if things hold up. If not I will proceed in stripping more stuff.

The current version of Direct Strike is in JASS, and just like you I also had this exact problem back when the map was Lua. I was also very demotivated, but simply translating everything to JASS made it work.

Right now though I'm in the process of rewriting it again, this time in Typescript. I did some early tests to make sure it didn't desync, but sure enough it did desync again. However, @Damage gave me the advice to not run anything on map init and so far it hasn't desynced once in my tests since then. I need more people later on to make sure it's desync free, but so far running everything after a 0.01 second timer has worked for me.
Thanks Lasz, I spoke with Damage on discord and he told me this. I was already intrigued by the fact that your map, which has pretty complicated/interactive UI functions well. Just like some other maps (such as the new custom hero survival). Good to know now it runs on JASS, and that somehow makes the difference.

As for Lord's suggestion I made this (and I believe I understood his instruction correctly)

  • I had no code at all for map initialization.
  • I put the following code in the main function using the mpq editor:
TimerStart(CreateTimer(), 0, false, Start()) (start being the name of my init function)

Unfortunately it made no difference for the desync issue. I have to add though, if you do not specifically request a tester to be tabbed out you won't see it. Because when I ran small scale tests it all went fine, but when I put up an actual lobby it went wrong.
 
Bumping this thread. It seems like my previous tests produced some false negatives, as I'm getting desyncs from alt-tabbing on both the current version of my map and the version I tested 2 months ago.

Here's some of my new findings:
1. The game does not desync if players do not interact with the created frames, in my case button clicks. After about ~3 minutes after the frames' creation the players can start to interact with them again, causing no desyncs.
2. If players do interact with the frames and perform no other actions for ~3 minutes, then decide to perform actions again i.e. issue orders, build, research, or click frames the game does not desync.
3. If players interact with the frames and only build 1-2 structures and performs no other actions for ~3 minutes the game does not desync.
4. If players interact with the frames and carry on as normal i.e. issue orders, click frames etc. etc. (in other words just playing the game) the game desyncs right around 3 minutes.

These tests were carried out with 2 clients on Bnet with one of the clients being alt-tabbed during init.

While I'm not an expert on this I'm starting to think garbage collection is the problem.
 
Last edited:
Level 9
Joined
Mar 26, 2017
Messages
376
Interesting observation. I also felt during my tests a while back, that the clickable frames that affect game state were the culprit. I also think I see delayed effect, but not a ~3 minute delay like you see.

Ever since, I made a bunch of changes to my custom UI, and the issue is now fixed entirely. No single desync in large lobbies. With following changes:
-Removed the entire custom toc and fdf
-No single use of CreateFrame or CreateSimpleFrame
-Only use the BlzGetFrameByName command to manipulate some existing frames, like the black box frame (BlzGetFrameByName command DOES influence handle count, according to Tasyen)
-For the rest I only use a few frame manipulations on existing frames, such as set size, set position

I do instruct the players to restart the WC3 game before playing, and to use borderless windowed mode though, so maybe that also helps.


Another observation: I have been following Overclocked's Hero survival for a while on Wtii's channel. That map creates some custom UI frames, and it never desyncs! But he doesn't use clickable frames with a game effect, and I reckon he doesn't use custom fdf.
 
Right so there's a pattern here in terms of game-state affecting frames. Do you also use TriggerHappy's TypeScript template?

I believe we're treating the frames wrongly, because the C# "WCSharp" template does not have this issue with frames, and it's also transpiling to Lua. At least, LordDamage's map "Assault the Throne" never had this issue.
 
Level 9
Joined
Mar 26, 2017
Messages
376
No I use pure lua XD

I don't really think the code program has too much to do with it. In the end it is still lua, and then just calls the certain native in the game engine, and I think that is what determines whether the map desyncs or not. Unless you pull of some weird lua shenanigans that desync, but I don't think this is the case for either of us. Although perhaps someone more knowledgable like @TriggerHappy could chime in.

Interesting to see @Damage's map is holding up well.
I looked at a video of the map. It does use buttons with a game effect. But the map is only 4 players. So perhaps that means people are less likely to be afk at start? I think the desync occurs when someone is afk during game loading AND that person has display mode to fullscreen. This may be quite rare on a 4 player map, esp. if there is a short wait time in lobby with active people looking out to test the game. As opposed to slow lobbies filled with randoms :p

And perhaps it has to do with: in this map there is only one button click at game start (from what I've seen). Contrary to more complicated UI logic in our maps. Although I would expect if one instance holds up, any amount of button pressing should be safe. But still there may be an interaction with more complex UI that results in desync?

It could be interesting to do a specific test on Damage's map. Where 1 or 2 players are fully afk at game start and have display to fullscreen.
 

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
1,993
I don't know what exactly your troubles were with frames in lua, but this scenario might help. Just a while ago two of my friends requested help regarding modifying text of custom frames for each player with their unit selected. The problem was solved by looping through each player (in a loop), storing the vars that they're going to use in local vars, then create an if block with "if GetLocalPlayer() == Player(i) then" and then use them inside the if condition, like BlzSetFrameText(customframevar, yourvar).
 
Level 15
Joined
Aug 31, 2009
Messages
778
I've never had any desync issues in my map, but the only reason that may be is that I'm extremely paranoid about the GetLocalPlayer() blocks.

I never retrieve, set any variables or any data whatsoever within it, only calling things like "SetFrameText" within it.

As an example of this, take a look at my code for this button:
1620885278521.png


As you can see, inside the GetLocalPlayer() block, absolutely no data is set to a variable. I only call the FrameSet values.

Similarly, in this button trigger:
1620885365282.png


I absolutely never set any variables or data within that GetLocalPlayer() slot.

If this is the solution, then @LazZ would be correct that Garbage Collection is cleaning up those local variables, causing a desync (as the variable is different for each player).
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,255
Some map had a random desync issue and the cause was eventually tracked down to trigger destruction.

If this is the solution, then @LazZ would be correct that Garbage Collection is cleaning up those local variables, causing a desync (as the variable is different for each player).
Garbage collection cannot clean up local variables, or any variable for that matter. They can clean up objects referenced by a variable, but the variable itself will automatically be destroyed when it goes out of scope for a local or never when it is a global. Due to Lua's non-static nature it is possible to explicitly delete global variables by modifying the global table.
 
Talk about being careful what you do inside local blocks. I managed to fix the desync by outcommenting the ForForce part. The game ran without desyncing in multiple tests.

TypeScript:
trig = CreateTrigger()
        BlzTriggerRegisterFrameEvent(trig, btnRaceClassic[i], FRAMEEVENT_CONTROL_CLICK)
        TriggerAddAction(trig, () => {
            let p = GetConvertedPlayerId(GetTriggerPlayer())
            for (let r = 1; r < 6; r++) {
                if (btnRaceClassic[r] == BlzGetTriggerFrame())
                    if (r != 5) {
                        playerRace[p] = r
                    } else {
                        playerRace[p] = 0
                    }
                isRaceSelected[p] = true

                /*
                ForForce(GetPlayersAll(), () => {
                    if (IsPlayerEnemy(GetEnumPlayer(), GetTriggerPlayer()) == false) {
                        if (GetLocalPlayer() == GetEnumPlayer()) {
                            BlzFrameSetText(framePlayerSelectClassic[p], pColor[p] + GetShortPlayerName(Player(p - 1)) + "|r" + ": " + colorReady + "Ready" + "|r (" + raceNameWithColor[playerRace[p]] + ")")
                        }
                    } else {
                        if (GetLocalPlayer() == GetEnumPlayer()) {
                            BlzFrameSetText(framePlayerSelectClassic[p], pColor[p] + GetShortPlayerName(Player(p - 1)) + "|r" + ": " + colorReady + "Ready" + "|r")
                        }
                    }
                })
                */

                if (GetLocalPlayer() == GetTriggerPlayer()) {
                    BlzFrameSetEnable(btnRaceClassic[r], false)
                }
            }
        })

I don't know why this is, but my guess is either:
1. Save stuff in variables for everyone before use.
2. Do not make any function calls inside a local BlzFrameSetText function.

I'm leaning more to no. 2.

GetShortPlayerName is a custom function that loops over a player name and returns it without the bnet tag at the end. I don't know if it's important for this context but here it is:
TypeScript:
export function GetShortPlayerName(p: player) {
    let pn = GetPlayerName(p)
    let pnShort = ""

    for(let i = 0; i < pn.length; i++) {
        if(pn.substring(i, i + 1) == "#") {
            break
        } else {
            pnShort = pn.substring(0, i+1)
        }
    }
    return pnShort
}

Below is the corrected, non-desyncing code.
TypeScript:
trig = CreateTrigger()
        BlzTriggerRegisterFrameEvent(trig, btnRaceClassic[i], FRAMEEVENT_CONTROL_CLICK)
        TriggerAddAction(trig, () => {
            let p = GetConvertedPlayerId(GetTriggerPlayer())
            for (let r = 1; r < 6; r++) {
                if (btnRaceClassic[r] == BlzGetTriggerFrame())
                    if (r != 5) {
                        playerRace[p] = r
                    } else {
                        playerRace[p] = 0
                    }
                isRaceSelected[p] = true
                ForForce(GetPlayersAll(), () => {
                    if (IsPlayerEnemy(GetEnumPlayer(), GetTriggerPlayer()) == false) {
                        let tmpText = pColor[p] + GetShortPlayerName(Player(p - 1)) + "|r" + ": " + colorReady + "Ready" + "|r (" + raceNameWithColor[playerRace[p]] + ")"
                        if (GetLocalPlayer() == GetEnumPlayer()) {
                            BlzFrameSetText(framePlayerSelectClassic[p], tmpText)
                        }
                    } else {
                        let tmpText = pColor[p] + GetShortPlayerName(Player(p - 1)) + "|r" + ": " + colorReady + "Ready" + "|r"
                        if (GetLocalPlayer() == GetEnumPlayer()) {
                            BlzFrameSetText(framePlayerSelectClassic[p], tmpText)
                        }
                    }
                })
                if (GetLocalPlayer() == GetTriggerPlayer()) {
                    BlzFrameSetEnable(btnRaceClassic[r], false)
                }
            }
        })

With that I also retract the statement that game-state affecting frames like button clicks and the like produce desyncs. It all comes down to how they're used.
 
Last edited:
Level 9
Joined
Mar 26, 2017
Messages
376
I've never had any desync issues in my map, but the only reason that may be is that I'm extremely paranoid about the GetLocalPlayer() blocks.

For me personally, I used custom UI for game mode selection that would randomly desync.
It only used one single local player logic: namely based on the person who was longest in the lobby, one Host player was defined (global).
The game menu buttons were visible to all, but if the local player was Host, the button is enabled for Host, and it is not clickable for all other players.
Then I used a trigger "BlzTriggerRegisterFrameEvent(trig_Host, f, FRAMEEVENT_CONTROL_CLICK)" which could only be activated by the Host, since the host was the only player with clickable buttons.

Now, I have recreated the same system with dialogs. (every player sees the dialog, but only when the Host player clicks a dialog button, dialogs for all players are modified) This works fine without desync.


Just wanted to mention this, because I think that LocalPlayer should be good to use, as long as you use sound logic. And if you use faulty logic for LocalPlayer, that would probably result in desync 100% of the times, and not the random desyncs we are seeing right now.
 
Last edited:
Level 9
Joined
Mar 26, 2017
Messages
376
I don't know why this is, but my guess is either:
1. Save stuff in variables for everyone before use.
2. Do not make any function calls inside a local BlzFrameSetText function.

I'm leaning more to no. 2.

Interesting. I don't know why a function call within BlzFrameSetText could desync the game, but you might be on to something.

Now in my current desync-free map, I do use BlzFrameSetText as following:

Lua:
function SetIncG(p, v)
    IncomeGold[p]=v
    if p==LP then BlzFrameSetText(Res, "|c00ffdc00+"..v) end
end

So basically have a string concatenation with a string and a local string variable within the BlzFrameSetText argument.
LP = GetLocalPlayer() (saved into variable)
 
It looks like using the class wrappers for frames in Typescript causes desyncs too.
TypeScript:
export function SetupMainUI() {
    let tinyBlackBox = new Frame("", Frame.fromOrigin(ORIGIN_FRAME_GAME_UI, 0), 0, 0, "BACKDROP", "");
    tinyBlackBox.setSize(0.1, 0.15)
    tinyBlackBox.setAbsPoint(FRAMEPOINT_CENTER, 0.25, 0.05)
    tinyBlackBox.setTexture("war3mapImported\\Black Box.tga", 0, true)
    tinyBlackBox.setLevel(-1)
}
This thing alone desyncs the game.

Even changing it to raw natives makes it desync:
TypeScript:
export function SetupMainUI() {
    let tinyBlackBox = BlzCreateFrameByType("BACKDROP", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "", 0)
    BlzFrameSetSize(tinyBlackBox, 0.1, 0.15)
    BlzFrameSetAbsPoint(tinyBlackBox, FRAMEPOINT_CENTER, 0.25, 0.05)
    BlzFrameSetTexture(tinyBlackBox, "war3mapImported\\Black Box.tga", 0, true)
    BlzFrameSetLevel(tinyBlackBox, -1)
}

^ But that is because it's a local scope variable.

If I put 'tinyBlackBox' in global scope it does not desync:
TypeScript:
let tinyBlackBox: framehandle

export function SetupMainUI() {
    tinyBlackBox = BlzCreateFrameByType("BACKDROP", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "", 0)
    BlzFrameSetSize(tinyBlackBox, 0.1, 0.15)
    BlzFrameSetAbsPoint(tinyBlackBox, FRAMEPOINT_CENTER, 0.25, 0.05)
    BlzFrameSetTexture(tinyBlackBox, "war3mapImported\\Black Box.tga", 0, true)
    BlzFrameSetLevel(tinyBlackBox, -1)
}

Unfortunately that solution desyncs with the typescript wrappers, so anything written with those needs to be changed.

But what I get from this is we always need a global reference to the frames we create. I haven't tested what happens when the local variable gets nulled but I need a break now.
 
Last edited:
Lua:
["src.Miscellaneous.MainUI"] = function() require("lualib_bundle");
local ____exports = {}
local ____DataPlayers = require("src.Data.DataPlayers")
local cameraZoom = ____DataPlayers.cameraZoom
local PlayerCameras = ____DataPlayers.PlayerCameras
local ____DataGame = require("src.Data.DataGame")
local Fortress = ____DataGame.Fortress
local WatchTower = ____DataGame.WatchTower
local ____index = require("node_modules.w3ts.index")
local Frame = ____index.Frame
local Trigger = ____index.Trigger
____exports.minimapText = {}
____exports.frameWatchTower = {}
____exports.frameWatchTowerHealth = {}
____exports.frameWatchTowerFill = {}
____exports.frameFortress = {}
____exports.frameFortressHealth = {}
____exports.frameFortressFill = {}
local tinyBlackBox
local trig
function ____exports.SetupMainUI()
    tinyBlackBox = BlzCreateFrameByType(
        "BACKDROP",
        "",
        BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0),
        "",
        0
    )
    BlzFrameSetSize(tinyBlackBox, 0.1, 0.15)
    BlzFrameSetAbsPoint(tinyBlackBox, FRAMEPOINT_CENTER, 0.25, 0.05)
    BlzFrameSetTexture(tinyBlackBox, "war3mapImported\\Black Box.tga", 0, true)
    BlzFrameSetLevel(tinyBlackBox, -1)
    return
end
function ____exports.ShowCameraButtonsAndHealthIndicators()
    ____exports.zoomIn:setVisible(true)
    ____exports.zoomOut:setVisible(true)
    ____exports.resetCamera:setVisible(true)
    do
        local i = 1
        while i < 3 do
            ____exports.frameWatchTower[i + 1]:setVisible(true)
            ____exports.frameWatchTowerHealth[i + 1]:setVisible(true)
            ____exports.frameWatchTowerFill[i + 1]:setVisible(true)
            i = i + 1
        end
    end
end
function ____exports.HideCameraButtonsAndHealthIndicators()
    ____exports.zoomIn:setVisible(false)
    ____exports.zoomOut:setVisible(false)
    ____exports.resetCamera:setVisible(false)
    do
        local i = 1
        while i < 3 do
            ____exports.frameWatchTower[i + 1]:setVisible(false)
            ____exports.frameWatchTowerHealth[i + 1]:setVisible(false)
            ____exports.frameWatchTowerFill[i + 1]:setVisible(false)
            ____exports.frameFortress[i + 1]:setVisible(false)
            ____exports.frameFortressHealth[i + 1]:setVisible(false)
            ____exports.frameFortressFill[i + 1]:setVisible(false)
            i = i + 1
        end
    end
end
return ____exports
end

This is my entire module (actually, only part of it because I disabled everything else just for testing). I'm not even sure how to read this lol.
 
Level 15
Joined
Aug 31, 2009
Messages
778
All of my frames have a variable assigned to them, but they're contained with Static Classes -- so yeah, technically global. Maybe that's why I don't have any issues?

@LazZ I can see how calling a function could lead to asynchronous behaviour, but I'm probably wrong. Is it not possible that running the function allocates some memory to that function and its return value? Then the memory would not be the same among all players, leading to a desync after the code is eventually cleaned up by the weird garbage collector?

But anyway, in my code, I just do all my function calls first - and assign them to local variables. Only then do I enter the LocalPlayer block and assign stuff to the frames.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,255
The desync might have been related to string creation. Creating unique strings within a local block has been reported to cause desyncs in the past. The solution in such case is to create the string objects for everyone (outside local block) and then use the appropriate ones within the local block.
 
Level 9
Joined
Mar 26, 2017
Messages
376
The desync might have been related to string creation. Creating unique strings within a local block has been reported to cause desyncs in the past. The solution in such case is to create the string objects for everyone (outside local block) and then use the appropriate ones within the local block.
A small question about this. Would this result in a surefire desync? Or is this a random desync, like we're observing?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,255
A small question about this. Would this result in a surefire desync? Or is this a random desync, like we're observing?
I do not know. However I would not rule it out that it could be random since it might only detect the desync later after something else happens that no longer matches between clients.
 
Level 15
Joined
Aug 31, 2009
Messages
778
So in my map, I realized that all my Custom Interface elements were simply being hidden by BlzFrameSetVisible.

I thought to myself... why not just destroy those frames instead once I'm done with them?

So I change merely one line:

BlzFrameSetVisibile(background, false)

to:

BlzDestroyFrame(background)


This instantly caused 2 people to Desync at the exact moment of the call. And specifically this is not inside a GetLocalPlayer() block. Do what you will with that information, but it certainly means something is not going right with that native.
 
I haven't done any testing into what specifically causes the desync. It could be any created frames, or it could be some specific natives while alt-tabbed. I'm not sure, but it may be worth looking into. I've also had it where I did not desync while alt-tabbed, so it doesn't seem to happen 100% of the time.

There is actually a native to tell if WC3 is in focus, so maybe that can help debug.
what's that native? :D
 
Hello I have desync too. (it looks like anonymous meeting intro :D )

But not the first creation of my frames but in the second use.

I create 240 frames on screen with textures (there is no triggers linked to them). I store them in an array in a struct. And I display it periodically. Then I destroy all of them. And nullify my array. I create different names for every frame. And theses frames are stored in an fdf file.

vJASS:
function showFrame takes framehandle fh, player P returns nothing
  call BlzFrameSetVisible(fh, (P == LOCAL_PLAYER))
endfunction

this function is the only time I execute code for using local player

So all my frames are created and updated for every players (I've checked that by setting visibility for every players) and I saw every changes in every players of my localNetwork game.

I uploaded a Log file, but I can't understant what it means.
Does anyone know the meaning of that error in wc3?
Is that problem due to the fact I play with 2 computers on same bnet accout in localNetwork?

I also use contextId different for evey players.
What is the use of contextId? Is it useful the way I use it? Or does the value can be just set to 0 every time?
 

Attachments

  • LAPTOP_Desync.txt
    103.1 KB · Views: 7
Last edited:
Status
Not open for further replies.
Top