JASS: GetLocalPlayer()

Discussion in 'Tutorial Repository' started by PurgeandFire, Apr 13, 2008.

  1. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    GetLocalPlayer()

    Introduction:

    What is GetLocalPlayer()?
    JASS:
    constant native GetLocalPlayer takes nothing returns player


    GetLocalPlayer is one of the most useful natives, but it is also one of the most dangerous. GetLocalPlayer is a native that retrieves the current player executing the code at that instance. If used correctly, you can perform an action for only one player, or even a group of players, so that the other players won't be affected.

    GetLocalPlayer and Desyncs:

    GetLocalPlayer is generally prone to desyncs. Desyncs occur when data becomes desynchronized between players in a way that affects gameplay. For example, if a unit has position A for player 1 and position B for player 2, Wc3 will realize the game is out of sync. As a result, Wc3 will simply disconnect one of the players. In this tutorial, you will learn how to use GetLocalPlayer() properly without causing desyncs.

    Players in JASS:

    Just a brief note for GUI users.

    In JASS, Player indexes range from 0-11 instead of 1-12.

    (0 = 1, 1 = 2, 2 = 3 ... 10 = 11, 11 = 12)

    So, you would do something like:
    JASS:
    if GetLocalPlayer() == Player(7) then
    // Player(7) refers to Player 8 (Pink)
    endif

    Then it would check if the current player executing the code is equal to Player 8 (Pink).

    Other common ones would be to use:


    The first is the Triggering Player and the second is the owner of the triggering unit. You can use any player input that you want, but these are the most common ones, and useful for GUI users to know.

    GetLocalPlayer Basic Usage:

    GetLocalPlayer can be used to perform an action for a specific player, as stated previously. Now, let's look at a basic example of a GetLocalPlayer block:

    JASS:
    function Test takes nothing returns nothing
        if GetLocalPlayer() == Player(0) then
            //actions
        endif
    endfunction


    Let's break the function down into parts, and show the definitions. Then we'll stitch the definitions together to get a basic concept of the function.

    function Test... = The function "Test" takes nothing and returns nothing. It simply performs some actions.
    if GetLocalPlayer() = If the player executing this code (local player==player currently executing this code)
    == Player(0) then = Is Equal to Player 1 (Red), then
    //actions = do these actions
    endif/endfunction = . (Period)

    So, now let's connect all those pieces.

    The function "Test" takes nothing and returns nothing. If the player running this trigger is equal to Player 1 (Red), then do these actions.

    So, this will perform the actions in that block if the player running the trigger is Player 1 (Red).

    Disconnections through Locally Creating/Destroying Agents:

    When you create or destroy agents within a local player block, it will cause a desync.

    First, let me show an example:

    JASS:
    function Test takes nothing returns nothing
        if GetLocalPlayer() == Player(0) then
            call AddSpecialEffect("none.mdl",0.,0.)
            // Will this desync?
        endif
    endfunction


    This will desync.
    JASS:
    native AddSpecialEffect takes string modelName, real x, real y returns effect


    This function returns effect.
    JASS:
    type effect extends agent


    Effect extends agent.

    So you are creating an agent. Creating agents locally for players will cause desyncs. Non-agent handles, however, will not desync when created locally for players (e.g. texttags, ubersplats, lightning, weathereffects, and anything else that has its own handle stack).

    ** No longer applies as of patch 1.24 ** There is a super special function called "Typecasting - Handle to Integer". Handle to Integer will retrieve a certain handle's value. So, let's look at the function:
    JASS:
    function H2I takes handle h returns integer
    return h
    return 0
    endfunction
    //Outdated since this method of typecasting no longer works!

    JASS:
    native GetHandleId takes handle h returns integer
    //Update for 1.24. Use this instead of H2I.


    The old H2I trick. The Jass parser only checks the last return value. So we allow it to return a handle when it is supposed to return an integer. This famous trick was exploited by SuperIKI, followed by Peppar. However, now we have to use GetHandleId(), which is just as easy to use.

    Anyway, we can get the handle value using this function. If the value is greater than 0x100000 (or 1048576, in non-hexadecimal form), it will desync on creation/destruction in the block. If you test many handles, you might get something around 1048670, or generally something greater than 1048576 (depending on how many handles exist on the map)... To test to retrieve the value, you can use this:

    JASS:
    function Example takes nothing returns nothing
    local location L = Location(0,0) //create a handle
    call BJDebugMsg(I2S(GetHandleId(L))) //get its id, display it
    call RemoveLocation(L) //remove it
    set L = null //null for handle id to be recycled
    endfunction


    And it will show the value in game. But what about "special handles"? Try this:

    JASS:
    function Example takes nothing returns nothing
    local texttag t = CreateTextTag()
    call BJDebugMsg(I2S(GetHandleId(t)))
    call DestroyTextTag(t)
    set t = null
    endfunction


    That would probably return 99, unless there are more texttags. Some handles aren't allocated like regular handles. Why 0x100000? Handles are normally allocated with an internal ID of 0x100000. That is where it starts off. Then it progresses on, adding 1 each time. However, things like texttags start off with an id of 99, and progressively go down until they reach 0. For whatever reason, handles that have their own handle stacks are safe for local-creation/destruction.

    Anyway, so if you were to create a unit (which is a normally-allocated handle), it would cause a desync because its HandleId > 0x100000 (1048576). (0x denotes a hexadecimal number).

    In patch 1.24, Blizzard introduced the agent type. It refers to all reference-counted objects. These types are still handles, but they are all a part of a joint handle stack. If you use GetHandleId() on an agent, the handle ID returned will be greater than 0x100000 (0x denotes a hex number). However, non-agent handles have a stack that starts with an ID of 99 and goes down to 0. This limits those handles to only 100, but with GetLocalPlayer() you can create those handles locally. This means that the new limit is 100 per player, instead of 100 global handles.

    To see which types are agents and which aren't, see the common.j:
    http://wiki.thehelper.net/wc3/jass/common.j

    Manipulating the Editor's System:

    Say you want to create a special effect for a player. How would you do that?
    JASS:
    function Test takes nothing returns nothing
        if GetLocalPlayer() == Player(0) then
            call AddSpecialEffect("war3mapImported\\FX.mdl",0.,0.)
        endif
    endfunction


    WRONG! You cannot create a special effect locally because it is an agent. This will surely disconnect the other players. So, how do you show an effect for just one player? In most cases, you would locally show/hide something for a player. But effects don't have a function for that. Have no fear, because there is an alternative! Special effects don't have a hide/display function, so this is what we'll do!

    JASS:
    function Test takes nothing returns nothing
        local string s = ""
        //So the path will be nothing, so it won't show at all
        if GetLocalPlayer() == Player(0) then
            set s = "war3mapImported\\FX.mdl"
            //An actual path, so it [I]will[/I] have a path for that player but not for
            //the other players
        endif
        call DestroyEffect(AddSpecialEffect(s,0.,0.))
    endfunction


    Congratulations, you made it correctly!

    There is a rumor that the string table can become desynced if the first time you use a string is within a GetLocalPlayer() block.

    It will not cause an immediate disconnection, and it is unclear whether it has any future implications. Still, if you'd like to remain on the safe side, you may want to choose this method instead:
    JASS:
    function Test takes nothing returns nothing
        local string s = "war3mapImported\\FX.mdl"
        if GetLocalPlayer() != Player(0) then
            set s = ""
        endif
        call DestroyEffect(AddSpecialEffect(s,0.,0.))
    endfunction


    This way, the string "s" will first be filled with "war3mapImported\\FX.mdl". If this is a new string, it'll be entered into the string table for all players. Then it checks if the local player is not player 1 (the player you'll show the effect to), and then it will set the string to "". Basically, if the player is not player 1, change the path to an empty model "". It is the same exact concept as above, just in reverse. However, it allows the string table to remain sync'd.

    Basic Usage:

    Let's say you have a multiboard, but you only want it for a player? Remember, you can't create it for a player. They have no path to manipulate, so what do you do? You can hide/show it locally for players!

    JASS:
    function Test takes multiboard mb returns nothing
    call MultiboardDisplay(mb,false)
    if GetLocalPlayer() == Player(0) then
    call MultiboardDisplay(mb, true)
    endif
    endfunction


    It will display it only for player 1!

    Other Causes of Desyncs:

    Note that creating and destroying handles are not the only way to cause a desync through a local block. Many things that affect gameplay in ways to affect things like pathing, positions, etc. can also cause desyncs. For example, hiding a unit won't desync immediately, but it will desync once they interact with something. Some other things that fall into this category are changing movement speed, locally moving positions, etc. Usually aesthetics won't cause desyncs, such as showing/hiding a multiboard, changing the vertex color of a unit, etc.

    It is unclear what exactly will desync and what will not. Be as safe as you can when dealing with local blocks. Even some things, such as setting a variable within a block, can cause desyncs later on if you use it in a condition, because it will later be considered true only for a specific player. Take these kinds of things into account before deciding how you want to approach your problem.

    Performing Functions For Forces

    Performing functions for forces, also has a technique. Instead of using GetLocalPlayer() over and over again, you can use a function called "IsPlayerInForce()". An example:
    JASS:
    function DisplayTextToForce takes force toForce, string message returns nothing
    if (IsPlayerInForce(GetLocalPlayer(), toForce))
    call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, message)
    endif
    endfunction


    See this function? The "toForce" area is the force you will perform it for, and the message is the message to be displayed.

    IsPlayerInForce checks if a player (argument 1) is in a specified force (argument 2). Since GetLocalPlayer() returns a player, (the player performing the trigger at that moment), you can check if that player is in that force, then it will perform that function to that player. This trigger will run for all players, and those in the force will receive a message, and those outside the force, will receive nothing.

    Conclusion:

    Now you know how to show stuff for only the local player! In GUI, you can also just use custom scripts as blocks to perform things locally. Be careful though, as many GUI functions have hidden things you might not know about!

    Credits:

    • SuperIKI and Peppar for H2I
    • Earth-Fury for the "technical" side of GetLocalPlayer()
    • weaaddar for a small H2I definition
    Enjoy! :)

    Some quotes that might help for some overall knowledge, thanks to phyrex and strilanc:


    1. Creating or destroying an agent locally will cause a desync.
    2. Mentioned in the tutorial. However, it doesn't necessarily cause a disconnection in the map. The table might not be synchronized, but it isn't clear whether this will cause issues.
    3. Mentioned in the tutorial (pathing, move speed, etc.)
    4. Assigning a new random seed can be performed through this native:
    JASS:
    native SetRandomSeed takes integer seed returns nothing

    If you set a random seed locally, then in some situations where you perform if GetRandomInt() or GetRandomReal(), the results may be different and will perform the actions locally.
    5. Essentially the same as 3, except a bit more broad since there are probably things besides just tinkering with widgets that can cause a desync in a local player block.
    6. TriggerSleepAction() is equivalent to the "Wait" function in GUI. Using this in a local player block will desync.

    ** Update: March 5th and March 25th ** : I reformatted a bit of it for the 1.24 update since H2I no longer applies. I've also added "0x" to denote the hexadecimals (0x100000) since I forgot it. It is decently important since I don't want people to be misled into thinking that handles desync when over 100,000 instead of 0x100000. =P Well, usually handles will be either higher than 0x100000 or 0-99.

    Update: August 20, 2012: I updated some things to be a bit more factual and easier to read, as well as some updates for xenForo.

    Update: July 29, 2013: Updated to be more factual. A lot of the information was pre-1.24b, and now there is an easier way to determine what desyncs and what doesn't.
     
    Last edited: May 4, 2014
    • Like Like x 14
  2. NullCurrent

    NullCurrent ( ゚ε ゚)

    Ratings:
    +110 / 0 / -0
    Very Useful. This will help with fade filters, floating text, and things of that sort.

    But, could you give more examples to determine which player the actions will effect.

    Code:
    if GetLocalPlayer() == Player(0) then
    
    Such as, what other ways could you say "Player(0)" to make it effect the player using a spell/action, etc.

    +Rep
     
    • Like Like x 1
  3. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    Oh, ok. In JASS, Players range from 0-11 instead of 1-12.

    (0 = 1, 1 = 2, 2 = 3 etc.)

    So, you would do something like:
    JASS:
        if GetLocalPlayer() == Player(7) then
    
    endif


    Player 7 is JASS for Player 8 in GUI.

    Other common ones would be to use:
    JASS:
    GetTriggeringPlayer()
    GetOwningPlayer(GetTriggerUnit())


    The first is the Triggering Player and the second is the owner of the triggering unit.

    I'll add this small post to the tutorial. Thanks! :)
     
    • Like Like x 1
  4. NullCurrent

    NullCurrent ( ゚ε ゚)

    Ratings:
    +110 / 0 / -0
    Perfect, I know that this will help a lot of people. =]
     
  5. WastedSavior

    WastedSavior A day without sunshine is like, well, night. Staff Member

    Ratings:
    +217 / 0 / -0
    JASS:
    function Trig_Convert_Fade_Actions takes nothing returns nothing
        if GetLocalPlayer() == udg_Fade then
        call CinematicFadeBJ( bj_CINEFADETYPE_FADEOUTIN, 10.00, "ReplaceableTextures\\CameraMasks\\White_mask.blp", 0, 0, 0, 0 )
        endif
    endfunction


    I was wondering if this is correct? It works in single player, but i don't currently have the opportunity to test it with a group.
     
  6. GoGo-Boy

    GoGo-Boy You can change this now in User CP

    Ratings:
    +40 / 0 / -0
    Very nice tutorial, it will surely help a lot people. I definitely missed s.th. like this when I got interested in GetLocalPlayer() :D +Rep (if I can)

    To WastedSavior:
    You don't create a handle with this, hence it will work.
     
  7. phyrex1an

    phyrex1an Staff Member and irregular helper Staff Member

    Ratings:
    +446 / 0 / -0
    That's a false conclusions.

    First, it does create handles. A timer to be specific. CinematicFadeBJ calls FinishCinematicFadeAfterBJ which does this:
    JASS:
    function FinishCinematicFadeAfterBJ takes real duration returns nothing
        // Create a timer to end the cinematic fade.
        set bj_cineFadeFinishTimer = CreateTimer()
        call TimerStart(bj_cineFadeFinishTimer, duration, false, function FinishCinematicFadeBJ)
    endfunction


    Second, creating handles is not the only thing that can/will cause a desync. Lets see if I remember them all :):
    1. Creating or destroying a handle
    2. Use a string for the first time
    3. Change the game in any way that affects gameplay (eg, change the move speed of a unit).
    4. Assign a new random seed.
    5. Pretty much anything that have an effect that you can read later.
    6. TriggerSleepAction

    Not all of the causes desyncs directly. For example the following code wont desync until the second if:

    JASS:
    set udg_Integer = 1
    if Player(0) == GetLocalPlayer() then
        set udg_Integer = 2
    endif
    call TriggerSleepAction(5)
    if udg_Integer == 2 then
       call CreateTimer()
    endif


    On a related note, are you really sure that creating texttags doesn't desync?
    It doesn't seem reasonable to me, especially not with the reason given.
     
    • Like Like x 1
  8. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    A day later than I need it.
    I only spent 2 hours or so working with GetLocalPlayer yesterday :D

    Great tutorial though!

    You can use GetLocalPlayer() to make Players 'own' Trackables. Which is what i was doing :)
     
  9. GoGo-Boy

    GoGo-Boy You can change this now in User CP

    Ratings:
    +40 / 0 / -0
    Ohh okay I rushed in an fast answer phyrex1an >_<. Overlooked the other function call and... seems like I did not know as much as you, thanks therefore hehe.
     
  10. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    Mind if I quote this in the first post? Yes, it is not the only way to desync. It was night so I wasn't thinking of other possibilities. >.<


    If no other texttags were created, then it will return 99. It is unlikely that they will desync because they aren't allocated like regular handles. I might test it later on. :)

    Thanks. :) And just remember the sfx method because it is kind of the same for trackables.
     
  11. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    Bump. D:

    I guess I can go on assuming this is correct. Common, feedback, anyone? I'll help you if you get stuck! :(

    Anyway, bump for approval, unless their is something wrong or untrue... I'll fix it!
     
  12. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    Cool tutorial, but one question:

    For the 'trick' in getting an SFX string for only one player, wouldn't the effect still be visible to all players?
     
  13. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    Thanks for the reply. :D

    Anyway, here is the example I provided in the tutorial:
    JASS:
    function Test takes nothing returns nothing
        local string s = ""
    //So the path will be nothing, so it won't show at all
        if GetLocalPlayer() == Player(0) then
            set s = "war3mapImported\\FX.mdl"
    //An actual path, so it [i]will[/i] have a path for that player but not for
    //the other players
        endif
        call AddSpecialEffect(s,0.,0.)
    endfunction


    Okay. So the string starts off as "". That string's name is "s".

    So [ s = "" ]

    So if I add a special effect, the path will be "", so it won't display anything.

    Now, the GetLocalPlayer() block will set the path for that player to the actual effect path.

    So it will show nothing [""] for the players except for Player 1 (Red) who will have an effect of [war3mapImported\\FX.mdl]

    Understand? :D So you set the path locally, and thus it will only display for that player while it displays "" (which is nothing) for the other players. ;)
     
  14. Insane!

    Insane! Shh I didn't edit this, go away.

    Ratings:
    +121 / 0 / -0
    pictures are not showing up
     
  15. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    Aw damn, imgbite got suspended. I'll upload them to IMGDevil and they'll be back, hold on a sec.

    EDIT: Reuploaded! :)
     
  16. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    Kinda... what confused me is that the SFX won't be visible to the other players (still not really understanding GetLocalPlayer but I'll learn in time :p)

    Thanks for the response though :)
     
  17. GoGo-Boy

    GoGo-Boy You can change this now in User CP

    Ratings:
    +40 / 0 / -0
    You shouldn't forgot that the game isn't doing the absolute same stuff on each computer. And well GetLocalPlayer() is the way to do actions for a single.
     
  18. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    Yeah, the one that will be displayed to the other players will be nothing, ( "".mdl ) :p

    Yeah, I was really confused of this at first too. I should have also mentioned that you can do it for forces by using:
    JASS:
        if IsPlayerInForce(GetLocalPlayer(),AllianceForce) == true then
    //actions
        endif
     
    • Like Like x 1
  19. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    That would allow you to display a special effect to a particular team?
     
  20. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    Yup. It checks if the local player (the player running the trigger) is within that force, and then you can just set the path and display it.. bla bla blah you know the rest. ;)


    I updated the tutorial to have information about forces, and very slight information about string table syncing for the AddSpecialEffect technique I used.
     

Share This Page