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

[Solved] Target of (Current Camera) desync

Status
Not open for further replies.
Level 9
Joined
Jul 30, 2018
Messages
445
Hey!

So, here's my trigger:

  • Order Attack East
    • Events
    • Conditions
    • Actions
      • For each (Integer LoopInteger_OrderUnit) from 1 to 21, do (Actions)
        • Loop - Actions
          • Custom script: if GetLocalPlayer() == udg_TempPlayer[0] then
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Grid[LoopInteger_OrderUnit] contains (Target of (Current camera))) Equal to True
              • Or - Any (Conditions) are true
                • Conditions
                  • LoopInteger_OrderUnit Not equal to 7
                  • LoopInteger_OrderUnit Not equal to 14
                  • LoopInteger_OrderUnit Not equal to 21
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (TempPlayer[0] is an ally of Player 13 (Maroon)) Equal to True
                • Then - Actions
                  • Unit Group - Pick every unit in (Units in Grid[LoopInteger_OrderUnit] owned by Player 13 (Maroon)) and do (Actions)
                    • Loop - Actions
                      • Unit - Order (Picked unit) to Attack-Move To (Center of Grid[(LoopInteger_OrderUnit + 1)])
                • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (TempPlayer[0] is an ally of Player 14 (Navy)) Equal to True
                • Then - Actions
                  • Unit Group - Pick every unit in (Units in Grid[LoopInteger_OrderUnit] owned by Player 14 (Navy)) and do (Actions)
                    • Loop - Actions
                      • Unit - Order (Picked unit) to Attack-Move To (Center of Grid[(LoopInteger_OrderUnit + 1)])
                • Else - Actions
            • Else - Actions
          • Custom script: endif
It works perfectly in a single player game, but desyncs in multiplayer game. I'm guess it's the Target of (Current Camera). Is there a way to make this not desync? I tried with and without GetLocalPlayer(), but doesn't seem to make any difference.

Is there a different way to get a player's current position of camera?

(And yeah, I know it leaks, I'm just testing at the moment!)
 
Level 10
Joined
Dec 11, 2009
Messages
234
Read more about how GetLocalPlayer() works.

Basically you're ordering units to move ONLY on one computer, and on others those units will not receive that order... Like, in my game the units will attack-move somewhere, but for others they will just stand in place. Of course it would desync, almost instantly.
And also you CREATE objects locally when getting the Center of Rect, etc. which will also lead to desync, but after some time, depending on many things.

I tried with and without GetLocalPlayer(), but doesn't seem to make any difference.
"Current Camera" is different for each player, hence "Target of (Current camera)" creates different locations locally.
 
Level 9
Joined
Jul 30, 2018
Messages
445
Good point about the Local Player. I haven't really played around with that that much, and I now realize how that my trigger could never work.

"Current Camera" is different for each player, hence "Target of (Current camera)" creates different locations locally.

However, that causes the desync anyway? Because, it did indeed desync even without getting the Local Player.

Edit: I also noticed there are two different functions, even though they almost look the same: Target of current camera view and Target of (Current Camera). I see that they are indeed two different natives: constant native GetCameraTargetPositionLoc takes nothing returns location and native CameraSetupGetDestPositionLoc takes camerasetup whichSetup returns location, respectively.

Is there a significant difference between them and which is better? A comment on the common.j for the constant native does say: // These return values for the local players camera only...

(I tested, and both functions do, however, desync all the same.)
 
Last edited:
Level 10
Joined
Dec 11, 2009
Messages
234
I personally use only GetCameraTargetPositionX() and GetCameraTargetPositionY(). But for GUI usage I would say that in most cases you should probably use GetCameraTargetPositionLoc(), because the second option you mentioned is CameraSetupGetDestPositionLoc(GetCurrentCameraSetup()), which is just awful if you look closely at what happens there.

The best example how to use GetCameraTargetPositionLoc() and how not to use it... comes from Blizzard themselves!
I am talking about the infamous SmartCameraPanBJ(). Check out the difference:

SmartCameraPanBJ in WC 1.30 and before (causes desyncs)
JASS:
function SmartCameraPanBJ takes player whichPlayer, location loc, real duration returns nothing
    local real dist
    if (GetLocalPlayer() == whichPlayer) then
        // Use only local code (no net traffic) within this block to avoid desyncs.

        set dist = DistanceBetweenPoints(loc, GetCameraTargetPositionLoc())
        if (dist >= bj_SMARTPAN_TRESHOLD_SNAP) then
            // If the user is too far away, snap the camera.
            call PanCameraToTimed(GetLocationX(loc), GetLocationY(loc), 0)
        elseif (dist >= bj_SMARTPAN_TRESHOLD_PAN) then
            // If the user is moderately close, pan the camera.
            call PanCameraToTimed(GetLocationX(loc), GetLocationY(loc), duration)
        else
            // User is close enough, so don't touch the camera.
        endif
    endif
endfunction
SmartCameraPanBJ in WC 1.31+ (should be good, but I never used it anyway)
JASS:
function SmartCameraPanBJ takes player whichPlayer, location loc, real duration returns nothing
    local real dist
	local location cameraLoc = GetCameraTargetPositionLoc() // <------------------------------------------------ !!!
    if (GetLocalPlayer() == whichPlayer) then
        // Use only local code (no net traffic) within this block to avoid desyncs.

        set dist = DistanceBetweenPoints(loc, cameraLoc) // <--------------------------------------------------- !!!
        if (dist >= bj_SMARTPAN_TRESHOLD_SNAP) then
            // If the user is too far away, snap the camera.
            call PanCameraToTimed(GetLocationX(loc), GetLocationY(loc), 0)
        elseif (dist >= bj_SMARTPAN_TRESHOLD_PAN) then
            // If the user is moderately close, pan the camera.
            call PanCameraToTimed(GetLocationX(loc), GetLocationY(loc), duration)
        else
            // User is close enough, so don't touch the camera.
        endif
    endif
	call RemoveLocation(cameraLoc) // <------------------------------------------------------------------------- !!!
endfunction
 
Level 9
Joined
Jul 30, 2018
Messages
445
I'm new to JASS, but I've been trying to learn it lately. Can you give some hints?

So, basically what I try to achieve is that I have created a button and when a player clicks it, it should order units to attack-move to the next grid (rect) depending on where the player's camera is at the moment of clicking the button.

The button click function:
JASS:
function ButtonOrderAttackEClick takes nothing returns nothing
    set udg_TempPlayer[0] = GetTriggerPlayer()
    call TriggerExecute( gg_trg_Order_Attack_East )
    call BlzFrameSetEnable(BlzGetFrameByName("ScriptDialogButton", 3), false)
    call BlzFrameSetEnable(BlzGetFrameByName("ScriptDialogButton", 3), true)
endfunction

Then, the trigger I posted above already: it now looks like this, but still desyncs:
  • Order Attack East
    • Events
    • Conditions
    • Actions
      • For each (Integer LoopInteger_OrderUnit) from 1 to 21, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Grid[LoopInteger_OrderUnit] contains (Target of current camera view)) Equal to True
              • Or - Any (Conditions) are true
                • Conditions
                  • LoopInteger_OrderUnit Not equal to 7
                  • LoopInteger_OrderUnit Not equal to 14
                  • LoopInteger_OrderUnit Not equal to 21
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (TempPlayer[0] is an ally of Player 13 (Maroon)) Equal to True
                • Then - Actions
                  • Unit Group - Pick every unit in (Units in Grid[LoopInteger_OrderUnit] owned by Player 13 (Maroon)) and do (Actions)
                    • Loop - Actions
                      • Unit - Order (Picked unit) to Attack-Move To (Center of Grid[(LoopInteger_OrderUnit + 1)])
                • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (TempPlayer[0] is an ally of Player 14 (Navy)) Equal to True
                • Then - Actions
                  • Unit Group - Pick every unit in (Units in Grid[LoopInteger_OrderUnit] owned by Player 14 (Navy)) and do (Actions)
                    • Loop - Actions
                      • Unit - Order (Picked unit) to Attack-Move To (Center of Grid[(LoopInteger_OrderUnit + 1)])
                • Else - Actions
              • Custom script: exitwhen true
            • Else - Actions
I did try to also run the first condition (checking in which region the target of current camera view is) in the button clicks function, but it still desynced.
 
1.31 brought a much more easy way to sync data.
JASS:
native BlzTriggerRegisterPlayerSyncEvent           takes trigger whichTrigger, player whichPlayer, string prefix, boolean fromServer 
native BlzSendSyncData                             takes string prefix, string data returns booleanreturns event
with BlzTriggerRegisterPlayerSyncEvent(triger, Player(0), "myprefix", false) you listen to data send by Player red with prefix "myprefix" when he localy runs BlzSendSyncData("myprefix", "mydata")
 
you need to sync the cam target data, the syncing could be done like that using the new natives:
JASS:
function SyncCamDataRead takes nothing returns nothing
   if BlzGetTriggerSyncPrefix() == "SyncCamX" then
       set udg_PlayerCamX[GetConvertedPlayerId(GetTriggerPlayer())] = S2R(BlzGetTriggerSyncData())
   else
       set udg_PlayerCamY[GetConvertedPlayerId(GetTriggerPlayer())] = S2R(BlzGetTriggerSyncData())
   endif
endfunction

function SyncCamDataSend takes nothing returns nothing
   call BlzSendSyncData("SyncCamX", R2S(GetCameraTargetPositionX()))
   call BlzSendSyncData("SyncCamY", R2S(GetCameraTargetPositionY()))
endfunction

function SyncCamDataInit takes nothing returns nothing
   local integer playerIndex = 0
   local trigger trig = CreateTrigger()
   call TriggerAddAction(trig, function SyncCamDataRead)
   loop
       call BlzTriggerRegisterPlayerSyncEvent(trig, Player(playerIndex), "SyncCamX", false)
       call BlzTriggerRegisterPlayerSyncEvent(trig, Player(playerIndex), "SyncCamY", false)
       set playerIndex = playerIndex + 1
       exitwhen playerIndex == bj_MAX_PLAYER_SLOTS
   endloop
   call TimerStart(CreateTimer(), 1, true, function SyncCamDataSend) // Once a second
endfunction
After you called SyncCamDataInit() at map init. Players will once a second share their camtarget X/Y, that shared data is stored in udg_PlayerCamX and udg_PlayerCamY using the playersIndexes.
Inside your GUI Trigger you know would have to create a Location out of this variables instead of the target cam loc.
 
Status
Not open for further replies.
Top