[TUT] Moving from Player Names to UUID.

Discussion in 'Resources' started by Alshain01, Mar 7, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    Alshain01

    If haven't heard by now, Mojang is slowly making an attempt to allow users to alter their user name. This poses a problem for many plugins because User Names are used by these plugins as a form of identification. In fact, this has been the recommendation for quite some time due to the pitfalls of attempting to store Player objects. This tutorial will cover the basics of using UUID and more specifically show comparisons to the old player name method.

    What are UUID?
    UUID or Universally Unique Identifiers (also known as GUID or Globally Unique Identifier) are strings that, in theory, will be unique to each object. I say "in theory" because it is possible to generate two UUIDs that are identical however, depending on the complexity of the UUID, this is an extreme rarity. Java UUID are 128-bit values, I don't intend to veer off into the probabilities of generating a non-unique UUID in this tutorial, trust me when I say that it will likely not occur. Mojang is implementing login server UUIDs which will guarantee that each player will have a unique UUID and retain their same UUID across all servers using online authentication. Newer version of Bukkit have converted to using this login server implementation, however older versions still had UUID methods built in, they were just server local.

    Example of a Java UUID (as a String):
    067e6162-3b6f-4ae2-a171-2470b63dff00

    When to use UUID in your code?
    UUID's will be required whenever you store information about a player for long term. For example, if you wanted to store the number of times a player has logged into the server, you should use UUID because the next time the player logs in, it could be a different user name.

    However, UUID's are just as easy as player names, so why not use them always? Ideally the only reason you need to use the player name is when your sending it as a message or accepting it as a command argument (because the user should never see or have to type the UUID). The following section will show just how simple it is to use UUID.

    UUID vs Player Names - A real example

    Events
    Using UUID in Events for comparisons is just as easy as using a String (it's actually a bit easier because casing is not an issue in UUID). There is little difference except of course the differing Object.

    String Method:
    Code:java
    1. Set<String> players = getPlayerNameList();
    2.  
    3. @EventHandler
    4. void onPlayerMove(PlayerMoveEvent e) {
    5. boolean listed = players.contains(e.getPlayer().getName().toLowerCase());
    6. }


    UUID Method:
    Code:java
    1. Set<UUID> players = getPlayerUIDList();
    2.  
    3. @EventHandler
    4. void onPlayerMove(PlayerMoveEvent e) {
    5. boolean listed = players.contains(e.getPlayer().getUniqueID());
    6. }


    Getting a Player by UUID
    This is the one area where UUID currently is a little harder. Bukkit has a convenience method for retrieving a player by name, at the time of this writing, no such method exists for UUID. However, you can make one yourself which is exactly the same as how Bukkit does it by name.

    String Method:
    Code:java
    1. String player = "Alshain01";
    2. Bukkit.getServer().getPlayer(player); //Note: Only looks for online players


    UUID Method:
    Code:java
    1.  
    2. UUID pID = UUID.fromString("067e6162-3b6f-4ae2-a171-2470b63dff00");
    3. Player foundPlayer;
    4.  
    5. for(Player p : Bukkit.getServer().getOnlinePlayers()) {
    6. if(p.getUniqueID().equals(pID)) {
    7. foundPlayer = p;
    8. break;
    9. }
    10. }
    11. // Be sure to check this
    12. // just like when using the String method
    13. if(foundPlayer != null) {


    UPDATE: As of 1.7.5 it is now possible to retrieve a player by UUID
    Code:java
    1. UUID pID = UUID.fromString("067e6162-3b6f-4ae2-a171-2470b63dff00");
    2. Bukkit.getServer().getPlayer(pID);


    Storing a UUID
    For long term storage, UUID can be easily convert using the toString() Method. This is the simplest way to store it long term in YAML or SQL (note: Because it is 128 bit, a UUID will always be 32 characters with 4 dashes, so CHAR(36) will always hold your ID). Obviously there is no String version of this, since a String is already a String.
    Code:java
    1. UUID pID= UUID.fromString("067e6162-3b6f-4ae2-a171-2470b63dff00");
    2. String pIdString = pID.toString();


    Converting it back from a string requires a UUID static method in java.util.
    Code:java
    1. UUID pID = UUID.fromString(pIdString);


    Offline Players
    As of 1.7.5, Bukkit has added a method of fetching UUID's for offline players. This should be used sparingly and only as a migration path.

    Code:java
    1. Bukkit.getServer().getOfflinePlayer("Alshain01").getUniqueID();


    Code:java
    1. Bukkit.getServer().getOfflinePlayer(UUID.fromString("067e6162-3b6f-4ae2-a171-2470b63dff00")).getName();


    Questions?

    Can I use UUID for anything else?
    Yes! Bukkit has server-local UUID's for all entities and worlds. World names can change too, it's not easy but it can be done! So if you store your World UUID instead of the name, that's one less annoyance the operator has to put up with if they want to change the world name.

    Can I add UUID to my own objects for API use?
    Absolutely, in fact I highly recommend this! Java has a method for generating random UUID. Remember there is an infinitesimally small probability for generating the same UUID, so if you wish you can check your stores to see if it exists before using it. Be sure to add a getter to retrieve it.
    Code:java
    1. UUID newUID = UUID.randomUUID();
     
  2. Offline

    bigteddy98

    Nice job on this, already found a few quick tutorials but not as good as this one, thanks :).
     
    Phasesaber and Alshain01 like this.
  3. Offline

    Garris0n

    You should add to your post that if the server is not in online mode the UUID is based off of the player's name instead of the actual Mojang UUID (it doesn't request it because the player isn't authenticated). If, for some reason, you were in offline mode but still had an internet connection (I used to test stuff with nonexistent alts in offline mode, though I have paid-for alts now and they're generally sufficient), you can query the UUID from Mojang using this. If you are using BungeeCord, you can query the UUID from it using its plugin-messaging API, however I can not link to the tutorial for that on these forums (just google BungeeCord plugin messaging).
     
    Alshain01 likes this.
  4. Offline

    Gamecube762

    Interesting, lots of plugins relies on name for storage, this will help identify them if the change their username (one features Mojang is planning to add).
     
    Alshain01 likes this.
  5. Offline

    Bart

    This probably isn't useful to anyone but I wrote a Node.js implementation that uses Mojang's API

    Code:java
    1.  
    2. function getUUID(name, callback) {
    3. request.post(
    4. {
    5. headers: {'content-type': 'application/json'},
    6. url: '[url]https://api.mojang.com/profiles/page/1[/url]',
    7. body: '{"name":"' + name + '","agent":"minecraft"}'
    8. },
    9. function(error, response, body)
    10. {
    11. body = JSON.parse(body);
    12.  
    13. callback(name, body.profiles[0].id);
    14. }
    15. );
    16. }


    Usage:

    Code:java
    1.  
    2. getUUID("BartBarrow", function(name, uuid) {
    3. console.log(name + "'s UUID is ' + uuid");
    4. });
    5.  


    Relies on https://github.com/mikeal/request Make sure you implement your own caching system so you can get faster request times :)
     
    Garris0n and Alshain01 like this.
  6. Offline

    mblanchet75

    Nice! I like this tuto!
     
  7. Offline

    Alshain01

    Updated based on 1.7.5 builds
     
  8. Offline

    xDeeKay

    Alshain01 If the method for getting an offline players UUID is only temporary, how will you get it in the later updates?
     
  9. Offline

    RawCode

    you dont need, bukkit have embedded methods to get UUID for player from mojang database at runtime.
     
  10. Offline

    Alshain01

    They are only blocking in 1.7.5. In 1.7.6 or higher they are the standard and OfflinePlayer.getName() becomes blocking instead.
     
  11. Offline

    xDeeKay

    I see, thanks.
     
  12. Offline

    Alshain01

    I did some further examination and I don't think getName did become blocking. It just is less efficient because players are indexed by UUID now.
     
  13. Alshain01
    What about scoreboards? They require you to grab a FAKE offline player to display info. How would we do this now(without deprecation warnings)?
     
  14. Offline

    Alshain01

    I'm not sure, I've not really worked with scoreboards. However, the deprecations are temporary as a warning to developers that something is changing and it may need attention. The deprecation will be removed in 1.8. If all your doing is using the player name to display information, it's pretty safe. The concern comes when your using a player name to grant some kind of permission that the player wouldn't otherwise have.
     
  15. Offline

    Rems19

    Hi, can anybody help me please ?
    I have this error when using getPlayer(UUID) :
    Code:
    [14:57:30] [Thread-8/WARN]: java.lang.NoSuchMethodError: org.bukkit.Server.getPlayer(Ljava/util/UUID;)Lorg/bukkit/entity/Player;
    [14:57:30] [Thread-8/WARN]:    at fr.rems19.game.Game.start(Game.java:206)
    [14:57:30] [Thread-8/WARN]:    at fr.rems19.game.StartingTimer$1.run(StartingTimer.java:59)
    [14:57:30] [Thread-8/WARN]:    at java.lang.Thread.run(Thread.java:724)
     
  16. Offline

    Alshain01

    Rems19 Are you sure your running it on 1.7.5 or higher?
     
  17. Offline

    mblanchet75

    The problem I have is to retrieve the OfflinePlayer and UUID from the player name. The only way I found is here :

    Code:java
    1. public static OfflinePlayer getOfflinePlayer(String playerName) {
    2.  
    3. for(OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) {
    4. if(offlinePlayer.getName().equalsIgnoreCase(playerName)) {
    5. return offlinePlayer;
    6. }
    7. }
    8.  
    9. return null;
    10. }
    11.  


    The problem with this method, it is making several lags when you have more than 1000 players.

    I don't know if I need to start a new thread or if there is an other way?
     
  18. Offline

    Alshain01

    mblanchet75 That is already written into Bukkit.

    Code:java
    1. OfflinePlayer player = Bukkit.getServer().getOfflinePlayer(String playerName);
    2. UUID id = player.getUniqueId();


    This still works but it's not as efficient as it used to be, but you should be migrating away from using that anyway. Really the only time you should be using that method is when a player needs to run a command (or something similar) that uses user names. Commands happen so infrequently (as far as computer speeds go) that it will be of little impact.

    Now, if your actually trying to do a migration. You shouldn't use this at all, you should use UUIDFetcher so you can provide the entire list of names and get a map back.
     
  19. Offline

    mblanchet75

    Here is the problem I found. The method The method :
    Code:java
    1. Bukkit.getServer().getOfflinePlayer(String playerName);

    Does not validate if the player exist in the server and if not, it will return a new generated UUID. We can not validate if the player exist or not.

    What I see with UUIDFetcher, it look in Minecraft site to see if the player exist. It is a good Idea but I think we have to wait for server reply?
     
  20. Offline

    Alshain01

    Hmm, I was not aware of this. I'll have to look at it closer.

    Well, it's intended for a one time poll. The theory is you get the UUID of all players already in your database. When new players log in, you record their UUID from the Player object directly. So the UUIDFetcher is a onetime operation on server load the first time after they upgrade.
     
  21. Offline

    mblanchet75

    If I understand, UUIDFetcher keeps a store of the name and UUID? If it is the case, it is a good idea.
     
  22. Offline

    breezeyboy

    If I have a UUID without dashs (bfa4add398f54c458cf793672d71a1f1) in java.util.UUID format and use the bukkit methods to get the name, will it fail?
     
  23. Offline

    Alshain01

    I'm not sure what you mean. Bukkit only uses java.util.UUID and I'm not aware of any methods directly in UUID that would strip the dashes.

    I would imagine if you can get UUID.fromString() to accept it then it should work just fine once it's in a UUID object.
     
  24. Offline

    Drkmaster83

    This returns the will-be UUID for a given Player name:
    Code:
    java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + playerName).getBytes(Charsets.UTF_8));
    
     
  25. Offline

    Alshain01

    Only for Offline Mode, it's not guaranteed for Online mode. It is possible that is how the UUID's are initially created for Mojang's database (I really just don't know) but once players start changing their names, the UUID's won't change with them so it won't work anymore.
     
  26. Offline

    Drkmaster83

    Hence the 'given Player name', but it is true that this won't be reliable in the future due to name-changing.
     
  27. ok but how do you accerss the files to check them or alter them now that they are .dat files like lets say i needed the ip adress of a player how can i find that without waiting for them to log in my server
     
Thread Status:
Not open for further replies.

Share This Page