• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Selections And Desyncs

Status
Not open for further replies.
Level 7
Joined
Mar 6, 2006
Messages
282
I've googled this a lot and I'm still not positive I understand it.

To select a unit for a player, you need to use GetLocalPlayer.

That confuses me, because it means the other clients don't know that you selected a unit. But I tested, and they DO know.

1.) Does that mean I can safely use SelectUnit with GetLocalPlayer, and IsUnitSelected will always work (not cause desyncs)?

2.) If all clients know that a certain player selected a unit in a local block, then what is SyncSelections for? I see that it's used in two BJ's that groups units. If SyncSelections is needed at all, then it suggests that Selections aren't always in sync.. but they were when I tested...

halp

edit: fck, I have 3 threads on the front page. w/e, im new to jass but learning quickly.
 
To select a unit for a player, you need to use GetLocalPlayer.

That confuses me, because it means the other clients don't know that you selected a unit. But I tested, and they DO know.

I think you're confused as to whatGetLocalPlayerdoes. It simply returns the player that is playing the map, meaning it returns a different handle for everyone playing the map. So GetLocalPlayer has nothing to do with net traffic, it just lets you execute code for the local player. Certain natives don't have a player parameter so this is where you should use it. If you cause any handle to be different or change gameplay for one player it will desync so be careful.

1.) Does that mean I can safely use SelectUnit with GetLocalPlayer, and IsUnitSelected will always work (not cause desyncs)?

Yes.
 
Last edited:
Level 7
Joined
Mar 6, 2006
Messages
282
It simply returns the player that is playing the map, meaning it returns a different handle for everyone playing the map.

OK so, when you select a unit for a player (with GetLocalPlayer), then how do other players know that SelectUnit() was run for a certain player?

Like:

You and Jack are playing a game.

Trig fires that selects a peasant for Jack ( if GetLocalPlayer == Jack )

The trig selects nothing on your client, because the ITE is false.

You're client: Jack never selected anything
Jack's client: selected a peasant

Where is the synchronization ?


edit: I know I could continue writing my trigger because you said I can use IsUnitSelected without fear, but I want to understand why :)
 
DysfunctionaI said:
OK so, when you select a unit for a player (with GetLocalPlayer ), then how do other players know that SelectUnit() was run for a certain player?

You would useSyncSelectionsif you wanted the other players to know.

For example look at this Blizzard function:
JASS:
function GetUnitsSelectedAll takes player whichPlayer returns group
    local group g = CreateGroup()
    call SyncSelections()
    call GroupEnumUnitsSelected(g, whichPlayer, null)
    return g
endfunction
I'm not 100% if it's needed because I haven't tested myself.
 
Level 7
Joined
Mar 6, 2006
Messages
282
You would useSyncSelectionsif you wanted the other players to know.

For example look at this Blizzard function:
JASS:
function GetUnitsSelectedAll takes player whichPlayer returns group
    local group g = CreateGroup()
    call SyncSelections()
    call GroupEnumUnitsSelected(g, whichPlayer, null)
    return g
endfunction
I'm not 100% if it's needed because I haven't tested myself.

Then why isn't IsUnitSelected() like this?

JASS:
function IsUnitSelectedBJ takes unit whichUnit, player whichPlayer returns boolean
    call SyncSelections()
    return IsUnitSelected(whichUnit, whichPlayer)
endfunction

I've tested (by multiplayer emulation) without SyncSelections, and IsUnitSelected always returned the right answer, so I'm confused.
 
I've googled this a lot and I'm still not positive I understand it.

To select a unit for a player, you need to use GetLocalPlayer.

That confuses me, because it means the other clients don't know that you selected a unit. But I tested, and they DO know.

1.) Does that mean I can safely use SelectUnit with GetLocalPlayer, and IsUnitSelected will always work (not cause desyncs)?

2.) If all clients know that a certain player selected a unit in a local block, then what is SyncSelections for? I see that it's used in two BJ's that groups units. If SyncSelections is needed at all, then it suggests that Selections aren't always in sync.. but they were when I tested...

halp

edit: fck, I have 3 threads on the front page. w/e, im new to jass but learning quickly.

Selections are sort of a special case because they have their own behavior. After running my own tests, I have gotten a few conclusions that may help. Here is the test code:
JASS:
library SelectionTest initializer Init 

    globals
        trigger selectTrigger = CreateTrigger()
        timer syncTimer = CreateTimer()
        
        group g = CreateGroup()
    endglobals

    private function B2S takes boolean b returns string 
        if b then
            return "true"
        endif
        return "false"
    endfunction
    
    private function GroupFilter takes nothing returns boolean
        call BJDebugMsg(GetUnitName(GetFilterUnit()) + ": selected.")
        return false 
    endfunction

    private function TestSync takes nothing returns nothing 
        local boolean isSelected = false
        if GetLocalPlayer() == Player(0) then
            call ClearSelection()
            call SelectUnit(gg_unit_ogru_0002, true)
        endif
        
        call TimerStart(syncTimer, 5, false, null)
        call SyncSelections()
        call PauseTimer(syncTimer)
        call BJDebugMsg("Delay: " + R2S(TimerGetElapsed(syncTimer)))
        
        set isSelected = IsUnitSelected(gg_unit_ogru_0002, Player(0))
        call GroupEnumUnitsSelected(g, Player(0), Filter(function GroupFilter))
        
        call BJDebugMsg("Selected: " + B2S(isSelected))
        call DestroyTimer(GetExpiredTimer())
    endfunction
    
    private function GameStart takes nothing returns nothing 
        call TriggerExecute(selectTrigger)
    endfunction
    
    private function EscapePress takes nothing returns boolean
        call GameStart()
        return false 
    endfunction

    private function Init takes nothing returns nothing 
        local trigger t = CreateTrigger() 
        
        call FogEnable(false)
        call FogMaskEnable(false)
        call TimerStart(CreateTimer(), 0, false, function GameStart)
        
        call TriggerRegisterPlayerEventEndCinematic(t, Player(0))
        call TriggerAddCondition(t, Condition(function EscapePress))
        
        call TriggerAddAction(selectTrigger, function TestSync)
    endfunction

endlibrary

The important thing is to test in multiplayer. Single-player won't give you the same results. You can test it easily with kloader, built-in to JNGP 2.0.X. I think I made a post a little while ago detailing what I *thought* SyncSelections() would do, but I finally got around to actually testing it.

First thing to note is that Warcraft III will automatically sync selections. What do I mean by this? If you have this code:
JASS:
call SelectUnit(unit, GetLocalPlayer() == Player(0))
Then you wait a few seconds (e.g. 2 seconds), IsUnitSelected(unit, Player(0)) will return true for all players.

Second thing to note is that IsUnitSelected and GroupEnumUnitsSelected try to return the same thing for all players. For example, if you were to test this in multiplayer:
JASS:
call SelectUnit(unit, GetLocalPlayer() == Player(0))
call BJDebugMsg(B2S(IsUnitSelected(unit, Player(0))))
It would return false for all players, even Player 1 (Red). If you were to test this in single player, you would get true. The GroupEnumUnitsSelected() group the same units for all players as well. Internally, I'm guessing they don't flag a unit as "selected" until all players have received that Player 1 has selected it. Either that or they query them all, and if the flags are out of sync, they'll do some magic. idk, it is some guesswork at that point.

So... what exactly does SyncSelections() do? My general theory would be that it forces the selections between players to be sync'd and pauses the thread until their sync'd (or attempts to). Turns out that it isn't 100% reliable. According to my test, if you have some unit selected prior, it might not sync the selection properly. It'll return "Peasant" in the group, and the isSelected will return false. If you already have the grunt unit selected, it'll say true (naturally). The delay (on my computer) ranged from about 0.225-0.300 according to the timer, but it likely varies depending on connection speeds. I have yet to test whether SyncSelections() forces the selections to sync earlier, or whether it does anything useful at all. :p

SyncSelections provides a little delay, so it can improve the accuracy of the GroupEnumUnitsSelected() and IsUnitSelected(), but I believe that is about it. It can, but not necessarily will. However, the most important thing to note is that the selection natives appear to have sync'd values among all players, so you don't have to worry about getting different values from different players.

Hopefully that'll clear some things up. Feel free to test it yourself! Those are just the results I got.
 
Level 7
Joined
Mar 6, 2006
Messages
282
Either your test was invalid or the game handles it internally.

It HAS to be the latter... but that means the call to SelectUnit wasn't really local, because it's being broadcasted somehow. And if that's the case, then why have a function to sync selections?!

AND if you can use IsUnitSelected safely, then why is GroupEnumUnitsSelected unsafe without SyncSelections?

Argh, the agony.

edit: osht, just saw the above post, gonna read.

OK, I read it and I'm doing some serious testing atm. I've already found hilarious results and I'm trying to consistently reproduce them. There is DEFINITELY magic going on in the background... I'm seeing some wacky ass shit right now, I'll post again in a sec.


======================================================================


I just edited out a wall of text I wrote cuz I was basically still testing. Here is my revised results.

My computer showed that it takes anywhere between 0.075 - 0.175 seconds to run SyncSelections.

What I think SyncSelections does, is it's almost like "get selected units, and remember them for like 100 milliseconds" which is enough time to run some sort of code that uses your selected units, and might get interrupted by a new selection. That BJ GetUnitsSelectedAll() is an example.

The reason I think it 'remembers' your selected units is because if you spam SyncSelections() and spam select new units, you can desync your own client's user interface (it thinks you have the wrong unit selected). But it doesn't desync you from a game because at each time when SyncSelections was called, it made sure to broadcast the correct units from each player, even if your client doesn't have the right unit selected for you.

Further proof of that would be how some maphacks allow you to select a unit without broadcasting it to everyone. It's called safeclick, and that doesn't desync you either ( I was very interested in AMHS back then ).

Also, you can't deselect the bugged selection unless you run SyncSelections again (I have no idea why, there must be a hardcoded method of relaying selected units locally, and globally).

So the verdict is, SyncSelections makes sure that the correct selection info is broadcast from all players at the time of the call, but it has a major con that can occur if it's used too frequently or maybe if there's lag.

My next test is to see how much stress I can put on IsUnitSelected before it desyncs. It will probably never desync, although it theoretically should at some point, since Blizz figured they needed the SyncSelections native, there's definitely some sort of middle-man.
 
Last edited:
My computer showed that it takes anywhere between 0.075 - 0.175 seconds to run SyncSelections.

It likely varies based on connection.

DysfunctionaI said:
What I think SyncSelections does, is it's almost like "get selected units, and remember them for like 100 milliseconds" which is enough time to run some sort of code that uses your selected units, and might get interrupted by a new selection. That BJ GetUnitsSelectedAll() is an example.

The odd thing is that it doesn't always work (in my tests). It will usually return the previous unit selected before SelectUnit() was ran. Unless that is what you meant. xD Maybe SyncSelections() will just choose the last selections that were fully synced.

DysfunctionaI said:
The reason I think it 'remembers' your selected units is because if you spam SyncSelections() and spam select new units, you can desync your own client's user interface (it thinks you have the wrong unit selected). But it doesn't desync you from a game because at each time when SyncSelections was called, it made sure to broadcast the correct units from each player, even if your client doesn't have the right unit selected for you.

Sounds neat. Could you share the code you used to test it? I believe I had something similar happen where it would change up my selections after the sync happened (or it would deselect or something), and I wonder if that is what you are referring to.

DysfunctionaI said:
My next test is to see how much stress I can put on IsUnitSelected before it desyncs. It will probably never desync, although it theoretically should at some point, since Blizz figured they needed the SyncSelections native, there's definitely some sort of middle-man.

I think that IsUnitSelected() is designed to always return results that are synced/consistent from player to player. Although, there are issues with assuming that SyncSelections is necessary. Blizzard has a couple of useless natives, and for all we know they could've changed how selections behaved over patches that may deprecated SyncSelections. That may not be true, but I'm just throwing it out there. :)
 
Level 7
Joined
Mar 6, 2006
Messages
282
It will usually return the previous unit selected before SelectUnit() was ran. Unless that is what you meant. xD Maybe SyncSelections() will just choose the last selections that were fully synced.

That's exactly what I meant; you worded it much better, haha.

Sounds neat. Could you share the code you used to test it? I believe I had something similar happen where it would change up my selections after the sync happened (or it would deselect or something), and I wonder if that is what you are referring to.

It might be the same thing. The code was simply:

event - on cinematic skipped
action - run syncselections

I didn't use a trigger for SelectUnit, I selected them manually. Then, in multiplayer, I could easily reproduce a bug that happens if you click two different units really fast, and press Esc RIGHT after you click each unit. You'll know it bugged out because the unit you currently have selected won't perform any orders at all, but instead, the PREVIOUS unit performs the orders that you're giving (that's why I think the UI thinks you have the wrong unit selected).

After that happens, you can't seem to de-select the bugged unit, and you have to run SyncSelections again to fix it. I don't think it's exploitable for any new system, so I stopped testing after that. I did this with JNGP 2.0.X multi-instance loader.

I think that IsUnitSelected() is designed to always return results that are synced/consistent from player to player. Although, there are issues with assuming that SyncSelections is necessary. Blizzard has a couple of useless natives, and for all we know they could've changed how selections behaved over patches that may deprecated SyncSelections. That may not be true, but I'm just throwing it out there. :)

Valid points, that does make sense.
 
Status
Not open for further replies.
Top