• 🏆 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!

Camera Zooming Desync

Status
Not open for further replies.
Level 16
Joined
Mar 27, 2011
Messages
1,349
Evening,

I'm making a Camera Zoom system but am experiencing desycns between players. The below code works fine:

  • Map Init
    • Events
      • Map Initialization
    • Conditions
    • Actions
      • Custom script: set udg_LocalPlayer = GetLocalPlayer()
  • Camera Zoom
    • Events
      • Player - Player 1 (Red) types a chat message containing -zoom as A substring
      • Player - Player 2 (Blue) types a chat message containing -zoom as A substring
    • Conditions
    • Actions
      • Set VariableSet TempInteger1 = (Integer((Substring((Entered chat string), 6, 10))))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • TempInteger1 Greater than or equal to 1200
          • TempInteger1 Less than or equal to 2200
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TempInteger1 Not equal to Player_CameraZoom[(Player number of (Triggering player))]
            • Then - Actions
              • Game - Display to (Player group((Triggering player))) for 8.00 seconds the text: (Setting camera to + ((String(TempInteger1)) + distance.))
              • Camera - Set (Triggering player)'s camera Distance to target to (Real(TempInteger1)) over 1.00 seconds
              • Set VariableSet Player_CameraZoom[(Player number of (Triggering player))] = TempInteger1
            • Else - Actions
              • Game - Display to (Player group((Triggering player))) for 8.00 seconds the text: (You are already Zoomed at a Distance of + ((String(TempInteger1)) + .))
        • Else - Actions
          • Game - Display to (Player group((Triggering player))) for 8.00 seconds the text: You can only Zoom t...

Now I want to ensure the Camera's distance to target does not change (unless that player enters a new zoom command of course). Unintentional zoom changes often occur when a player uses their mouse scroll wheel which resets it to Warcraft 3 default camera distance (1650). I thought I could periodically check if the camera is not at the desired zoom and correct it. I've tried various approaches but they all desync unfortunately.

Tested using two Warcraft 3 clients on the same computer. See attached screenshot for in-game desync symptoms Failed coded approaches below:


  • Camera Correct
    • Events
      • Time - Every 0.30 seconds of game time
    • Conditions
      • CinematicRunning Equal to False
    • Actions
      • Player Group - Pick every player in PlayerGroupAllies and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • LocalPlayer Equal to (Picked player)
            • Then - Actions
              • Set VariableSet Player_LocalCamZoom = (Integer((Distance to target of (Current camera))))
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Or - Any (Conditions) are true
                    • Conditions
                      • Player_LocalCamZoom Less than or equal to (Player_CameraZoom[(Player number of (Picked player))] - 10)
                      • Player_LocalCamZoom Greater than or equal to (Player_CameraZoom[(Player number of (Picked player))] + 10)
                • Then - Actions
                  • Game - Display to (Player group((Picked player))) for 2.00 seconds the text: (String(TempInteger1))
                  • Camera - Set (Picked player)'s camera Distance to target to (Real(Player_CameraZoom[(Player number of (Picked player))])) over 1.00 seconds
                • Else - Actions
            • Else - Actions
  • Camera Correct Copy
    • Events
      • Time - Every 0.30 seconds of game time
    • Conditions
      • CinematicRunning Equal to False
    • Actions
      • Set VariableSet Player_LocalCamZoom = (Integer((Distance to target of (Current camera))))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Or - Any (Conditions) are true
            • Conditions
              • Player_LocalCamZoom Less than or equal to (Player_CameraZoom[(Player number of (Picked player))] - 10)
              • Player_LocalCamZoom Greater than or equal to (Player_CameraZoom[(Player number of (Picked player))] + 10)
        • Then - Actions
          • Game - Display to (Player group(LocalPlayer)) for 2.00 seconds the text: (String(Player_LocalCamZoom))
          • Camera - Set LocalPlayer's camera Distance to target to (Real(Player_CameraZoom[(Player number of (Picked player))])) over 1.00 seconds
        • Else - Actions


As I've researched, camera data is not synced between players though I didn't think my triggers were causing local clients to access desynced network data (as I understand desyncs to happen when a client tries to access networked data when it is not synchronized between clients). Each client is simply comparing it's own local variable (Player_LocalCamZooom to a synced integer (Player_CameraZoom[*array*]) then setting it's own local camera but not touching the local camera data of another client or it's local Variable.

1. Why and where is the desync occuring?
2. How can I fix this?
3. If this approach can't work, are there any other methods I could take to "lock" player's distance to target?
 

Attachments

  • Camera Desync.jpg
    Camera Desync.jpg
    442 KB · Views: 18
Last edited:
Level 19
Joined
Feb 27, 2019
Messages
590
If you dont set this value to a variable in Camera Correct, will it still desync?
Set VariableSet Player_LocalCamZoom = (Integer((Distance to target of (Current camera))))

How can a single variable LocalPlayer be all different players at once? It doesnt make sense to me. I dont know a lot about the subject though.

EDIT: Hmm unless its different local variables ofcourse. I might understand now.
 
Level 16
Joined
Mar 27, 2011
Messages
1,349
If you dont set this value to a variable in Camera Correct, will it still desync?
Set VariableSet Player_LocalCamZoom = (Integer((Distance to target of (Current camera))))

I removed that code and it worked.

  • Camera Correct Copy Copy
    • Events
      • Time - Every 0.30 seconds of game time
    • Conditions
      • CinematicRunning Equal to False
    • Actions
      • Camera - Set LocalPlayer's camera Distance to target to (Real(Player_CameraZoom[(Player number of LocalPlayer)])) over 1.00 seconds

I just thought it would be more efficient to check if the player's camera had actually changed before resetting it's distance property. I'm happy to mark this as solved, but I'm not really sure why this function is desyncing the clients.

How can a single variable LocalPlayer be all different players at once? It doesnt make sense to me. I dont know a lot about the subject though.

EDIT: Hmm unless its different local variables ofcourse. I might understand now.

Yeah, the "LocalPlayer" variable, of type "Player", is set at Map Initialization. I use a custom script to call a Jass function which returns the player whom is operating on the local client. In other words, it'll tell me who is the local player :)

  • Map Init
    • Events
      • Map Initialization
    • Conditions
    • Actions
      • Custom script: set udg_LocalPlayer = GetLocalPlayer()
 
Level 19
Joined
Feb 27, 2019
Messages
590
Yeah, the "LocalPlayer" variable, of type "Player", is set at Map Initialization. I use a custom script to call a Jass function which returns the player whom is operating on the local client. In other words, it'll tell me who is the local player :)

  • Map Init
    • Events
      • Map Initialization
    • Conditions
    • Actions
      • Custom script: set udg_LocalPlayer = GetLocalPlayer()
Thank you, Ill try that in my map.

I dont quite understand the desync issue myself but I got it from reading Is 'Pan Camera (Timed)' safe? somehow
Perheaps the answer lies here Fixing SmartCameraPanBJ Desync
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,540
  • Events
  • Map Initialization
  • Actions
  • Custom script: set udg_LocalPlayer = GetLocalPlayer()
I'm 99% sure that this doesn't work the way you think it does. I don't think you want to do this.

At the end of the day you're using a Player variable for LocalPlayer, which can only be set to one thing at a time. Maybe there are cases where you could take advantage of this variable when using GetLocalPlayer(), but it definitely doesn't work the way you want it to in that Periodic trigger.

For example, using it for something like this may work:
  • Custom script: set udg_LocalPlayer = GetLocalPlayer()
  • If LocalPlayer is Equal to Player 1 (Red) then do actions...
But this is really only useful so that you can stick with GUI functions over Custom script (Jass).

Instead, in your Periodic trigger use a Pick Every Player function and iterate over your desired players, adjusting the camera for each picked player. You shouldn't have to worry about Local players there.

Another slight optimization would be to change Player_CameraZoom to a Real variable, this way you don't need to convert it from an int -> real every 0.30 seconds. It'd be better for the conversions to be calculated in the Cam chat message trigger since this would happen way less often than every 0.30 seconds.
 
Last edited:
Level 16
Joined
Mar 27, 2011
Messages
1,349
At the end of the day you're using a Player variable for LocalPlayer, which can only be set to one thing at a time. Maybe there are cases where you could take advantage of this variable when using GetLocalPlayer(), but it definitely doesn't work the way you want it to in that Periodic trigger.

Instead, in your Periodic trigger use a Pick Every Player function and iterate over your desired players, adjusting the camera for each picked player. You shouldn't have to worry about Local players there.

The below trigger seems to work. Player 1 (red) has PLayer 1 Red stored in the local global variable, player 2 (blue) has player 2 (blue) in the local global variable. So essentially the below trigger is making 1 camera call per client, instead of looping over every player and make 1-10 camera calls (based on player count). What concern do you have over using the bloew trigger versus picking every player and doing the camera setting for each picked player?

  • Camera Correct Copy Copy
    • Events
      • Time - Every 0.30 seconds of game time
    • Conditions
      • CinematicRunning Equal to False
    • Actions
      • Camera - Set LocalPlayer's camera Distance to target to (Real(Player_CameraZoom[(Player number of LocalPlayer)])) over 1.00 seconds
Another slight optimization would be to change Player_CameraZoom to a Real variable, this way you don't need to convert it from an int -> real every 0.30 seconds. It'd be better for the conversions to be calculated in the Cam chat message trigger since this would happen way less often than every 0.30 seconds.

This is a good idea. Thanks.
 
Level 19
Joined
Feb 27, 2019
Messages
590
MY GUESS: I can see that creating a unit for LocalPlayer would cause a desync but for some reason perheaps it does not cause a desync when concerning the camera of a particular client. That information is not transmitted to the other clients in any way, lest put in a variable?, therefor there is no desync. However using the comparison of "Distance to current camera" creates a location that differs for each local client which in turn desyncs if I understood Daffa's explanation correctly "The code create location on local block (this one : set dist = DistanceBetweenPoints(loc, GetCameraTargetPositionLoc())), which causes desync as location may not be created at local block"

This action in general may also create an unavoidable location leak in GUI is it not?

I am wildy speculating here mind me. Trying to understand some things.
 
Level 16
Joined
Mar 27, 2011
Messages
1,349
Are you sure it works for other players besides p1? I mean maybe it does work but I've never seen anyone do this before.

Yeah, I had two Warcraft 3 instances in my computer and a multiplayer game between the two. It seemed to work ok. No desyncs. I'll repost if I find any further issues with it.

I can see that creating a unit for LocalPlayer would cause a desync but for some reason perheaps it does not cause a desync when concerning the camera of a particular client. That information is not transmitted to the other clients in any way, lest put in a variable?, therefor there is no desync

That's my impression of whats happening yes.

However using the comparison of "Distance to current camera" creates a location that differs for each local client which in turn desyncs if I understood Daffa's explanation correctly "The code create location on local block (this one : set dist = DistanceBetweenPoints(loc, GetCameraTargetPositionLoc())), which causes desync as location may not be created at local block"

I think Daffa was referring to the "Pan Camera as Necessary" function, which internally contained the Jass function GetCameraTargetLoc(). This function created a location handle apparently and this caused a desync. I don't know much about what' happening here either, but if this is true, the GetCameraTargetPosition() may also be internally used in the GUI Camera Distance function and thus have the same problem. This was a good find, thanks.

To be honest, GetCameraDistance causing a desync does sound like a bug. Whats the point of the function if it guarantees a desync in multiplayer...?
 
Last edited:
Status
Not open for further replies.
Top