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

playing with GetLocalPlayer

Status
Not open for further replies.
Level 17
Joined
Apr 27, 2008
Messages
2,455
I've decided to play a bit with GetLocalPlayer, since now it's quite trivial to test desyncs, thx to Moyack and his JNGP 2 with multiple players emulating.
Of course thx for all the tool makers and contributors.

I will do more tests later, but for now it's quite exciting that what i thought was right.
In short, as long you don't interact with the gameplay result and don't create an handle only for one player, and don't open a new "thread" for one player, you should be able to do almost everything.

So far i've tried these things with no desync :

- creating a different unit for each player (the same in fact but not the same rawcode).
- setting a different position for the same unit for each player.
- calling ForForce with only one player inside it for player1 and empty for player 2
- calling TriggerEvaluate
- setting a different ability level : >>> read here <<<
- calling ExecuteFunc with 2 different valid functions
- starting a timer (but it must already been started at least one time before)

These ones desync :

- calling TriggerExecute

Now, of course these examples will lead to a desync in many cases, but still it's cool to know for some advanced stuff :thumbs_up:

I provide a test map in attachments.
Feel free to add your own tests or eventually give some ideas :)
 

Attachments

  • desyncTests.w3x
    14.8 KB · Views: 77
  • desyncTestsv0.1.w3x
    17 KB · Views: 54
  • desyncTestsv1.0.w3x
    116.7 KB · Views: 68
Last edited:
Level 9
Joined
Jul 30, 2012
Messages
156
Hmm, I think I can post something related to the topic:

Changing ability level for only 1 player


Through the use ofGetLocalPlayer and the Channel ability, it's possible to create a spell that only 1 player can use, in the case that multiple players have control over a single unit.

As you may know, the Channel ability can be made hidden in some levels. And you can set ability level with GetLocalPlayer! However, there is a correct way to do it:

Incorrect way:
JASS:
call UnitAddAbility(u, 'ANcl')
if GetLocalPlayer() == Player(?) then
    call SetUnitAbilityLevel(u, 'ANcl', 2)
endif
Modifying the level of an ability for only 1 player obviously doesn't work. Internally it will perform some operations that might involve the creation or destruction of agents. You must make sure that the level modification is done for all players.

Incorrect way 2:
JASS:
local integer i = 2
call UnitAddAbility(u, 'ANcl')
if GetLocalPlayer() == Player(?) then
    set i = 3
endif
call SetUnitAbilityLevel(u, 'ANcl', i)
This seems to be good right? But it doesn't work! Why not?

It's because the operation of modifying ability level works internally by increasing the level 1 by 1, until it reaches the requested level. The above code sets the ability's level to 2 for all players, which is done in only 1 operation. But for a specific player, it will set the level to 3, and this translates to two operations (the ability will first go to level 2, then level 3).

Because it's performing an operation in a single computer, but not in the others, it will desync for the same reason as the first way.

The correct way:
JASS:
local integer i = 1
if GetLocalPlayer() == Player(?) then
    set i = 3
endif
call UnitAddAbility(u, 'ANcl')
call SetUnitAbilityLevel(u, 'ANcl', 2)
call SetUnitAbilityLevel(u, 'ANcl', i)
Can you see it? First, it will add the ability and set it to level 2, for all players. This is made in one operation. Then it will set the level back to 1, for all players except one. That is the second operation.

But for a specific player, the level will increase to 3, instead of being decreased to 1. This is also made in a single operation, as the current level of the ability is already 2.

Because it is performing two level-modify operations for all computers, this will NOT desync. Now, if you set the Channel ability to be visible on level 3, but not on level 1, you will have an ability that is visible to only 1 player.

And that player can cast the ability normally, without desyncing, because the ability exists on everyone else's computers, they just can't see it. Obviously the other ability stats must be exactly equal for all levels, or it will interfere with gameplay.

I tested this trick on multiplayer emulation and it works perfectly, at least with channel. I don't know if there would be any problems if you try to do that with other abilities, but I don't see how that could be useful, since Channel is the only one that can be hidden.
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
OT: As long as you keep handles the same, you won't get desync instantly. Surely, you can create different units at different position and change other stuff locally. But as soon as any other event fires for this unit u'll meet the consequences. They won't pass some conditions or/and will not properly iteract with unit actions due to differences. Easiest example:

Hero for Player1, normal unit for Player2. Works just fine until first IsUnitType(Hero) check.
X1 for P1, X2 for P2. Works just fine unless X1/X2 belong to some rectangle which is used in "RegisterUnitLeave / Enter region".
and so on.

In a complex maps, with tons of triggers, it's impossible to avoid all those issues. But as a new start for another map - surely it is.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
@leandrotp :

Good to know, perfectly on the subject and well explained :)

Cool info. So if you have a unit in separate areas between two players (e.g. Player 1 has him at (0,0) and Player 2 has him at (1000, 0)), does it desync once you order him to do something? When exactly will it desync?

I assume it would desync on issued order.

In fact i created an unit only for Player red but give it a different area for the player red and blue.
Indeed it desync when you try to move the unit but you can launch no target abilities as long they won't affect other units, i've just tested it with the hero moutain king

@DracoL1ch :

Of course, that what i had in mind when i said that i will still desync in many cases, but it can still be applied in special games.
In fact i thought about Uther party before beginning the tests.
 
Last edited:
I'm not sure if this is known, but you can actually create two different widgets for players locally, as long as they have the exact same object data (except for non-gameplay things like models).

So, for example, you could have a default WC3 model shown for one player and a custom one for players that have the model file in an external folder.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I'm not sure if this is known, but you can actually create two different widgets for players locally, as long as they have the exact same object data (except for non-gameplay things like models).

So, for example, you could have a default WC3 model shown for one player and a custom one for players that have the model file in an external folder.

It's still good to say, but for me that was obvious since you can do that for the most interactive widget ever : an unit.
 
It's still good to say, but for me that was obvious since you can do that for the most interactive widget ever : an unit.
Still, there is danger in doing that: GetUnitTypeId() will of course return different values once you did that. That should be accounted for via a GetUnitTypeIdEx() function that "corrects" the output data to the source unit.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
You can reset the limit op locally, with ForForce and TriggerEvaluate. However i'm not sure how you could safely use them.
Call TriggerExecute and ExecuteFunc will lead to a desync though.
Also, if a trigger is evaluated locally because of an event which happens locally, it will also lead to a desync.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
You can make this test yourself with timers but it's pointless for me.
Simply because you can start a timer in a local block, as long it was "preloaded" (at least already started one time).

About condition, i expect a desync, since you can call TriggerEvaluate in a local block.
But if the condition is evaluated because the trigger fire with a local event -> desync.

Just check the test map, i know i could post the test code but meh ...
 
Status
Not open for further replies.
Top