[Solution] Getting Player object from Entity object

Discussion in 'Resources' started by axel3200, Jan 15, 2011.

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

    axel3200

    I have seen several threads asking this for different purposes, so instead of answering them individually I decided to just make a thread.

    As most of you know there is a relatively simple way to find if an entity is a player:

    Code:
    if (entity instanceof Player) {
        //Do something
    }
    But there was no known way to get the actual Player object from the Entity object.

    Solution?
    Search the list of online players for a player with the same entity ID as the given entity:

    Code:
    Player player;
    
    int eid = entity.getEntityId();
    
    Player[] players = plugin.getServer().getOnlinePlayers();
    
    for(int i=0; i<players.length; i++) {
    
        if (eid == players[i].getEntityId()) {
    
            player = players[i];
    
            //do something
    
        }
    
    }


    Casting.
     
  2. Offline

    darknesschaos

    Thanks! so helpful
     
  3. Offline

    LRFLEW

    Thank You VERY VERY much. Now if only there is some way that the Bukkit Team will implement a solution into the source that doesn't require searching every single entity, as this could save a lot of processing time :p.
     
  4. Offline

    flameman404

    This will be most helpful in my combat mod thanks!
     
  5. Offline

    jahwag

    Here's a better solution. In your plugin main class, add listeners for player join and quit:

    Code:
    // Register player join/quit listeners
    pm.registerEvent(Event.Type.PLAYER_JOIN, playerListener, Priority.Monitor, this);
    pm.registerEvent(Event.Type.PLAYER_QUIT, playerListener, Priority.Monitor, this);
    In your PlayerListener define:
    Code:
    /** Stores all players by their entityIDs. */
    private HashMap<Integer, Player> playerMap = new HashMap<Integer, Player>();
    
      @Override
        public void onPlayerJoin(PlayerEvent event) {
            // Add player to hashmap
            playerMap.put(event.getPlayer().getEntityId(), event.getPlayer());
            super.onPlayerJoin(event);
        }
    
        @Override
        public void onPlayerQuit(PlayerEvent event) {
            // Remove player from hashmap
            playerMap.remove(event.getPlayer().getEntityId());
            super.onPlayerQuit(event);
        }
    
    /**
         * Should be called inside the plugin's onEnable()-method to fill up the playerMap.
         */
        protected void fillPlayerMap() {
            for(Player p : plugin.getServer().getOnlinePlayers()) {
                playerMap.put(p.getEntityId(), p);
            }
        }
    
    /**
         * Lets us check if an entity is a player in O(1) constant time.
         * @param entityId the id of the entity to check
         * @return true if the entity is a player, otherwise false.
         */
        private boolean isEntityAPlayer(int entityId) {
            return playerMap.containsKey(entityId);
        }
    This gives you the result in O(1) constant time instead of O(N) time as the earlier solution. This will have a considerable impact on performance if you have many players and need to perform this check often.
     
  6. Offline

    Raphfrk

    I would suggest if you are calling that function often that you create a cache. Something like

    Code:
    HashMap<Integer,Player> playerCache = new HashMap<Integer,Player>();
    
    Player getPlayer(int eid) {
    
        if( playerCache.contains(eid) ) {
            return playerCache.get(eid);
        } else {
            Player[] players = plugin.getServer().getOnlinePlayers();
            for(Player player : players ) {
                if(player.getEntityId() == eid) {
                    return player;
                }
            }
        return null;
    }
    
    --- merged: Jan 16, 2011 3:41 AM ---
    Wow, two posts almost at exactly the same time, and the previous one was 30 mins previously :).
     
  7. Offline

    jahwag

    Yeah, took me about half an hour to get my confirmation email when I registered on the boards.

    Your above posted code has one issue, if an entity is not a player, it will always run the loop. This means in the worst case it is still O(N).
     
  8. Offline

    LRFLEW

    Although this technically calls in your code just O(1) times, HashMap goes through the list until it finds the value you're looking for just like the other way does :p.
    This is a better implementation, though, and I think I'll use this anyways.
     
  9. Offline

    axel3200

    The problem with this solution is that if the plugin is disabled and someone joins (or leaves) and then the plugin is re-enabled then you will have a record of a player who's actually offline or won't have a record for one that is online.
     
  10. Offline

    jahwag

    No, that is not how a hashmap works. A hashmap is not a list but an array(arrays as you know are O(1) lookup) where every element contains either a list or another array, resulting in O(log N) worst case if the hash function is bad.

    In an ideal hashmap, each lookup is guaranteed to be done in O(1) time. I recommend you to have a look at the wikipedia article http://en.wikipedia.org/wiki/Hash_table because hashmap is a very useful data structure.
    --- merged: Jan 16, 2011 3:55 AM ---
    Yes that is a very good point. To prevent that you could add another method that is called upon onEnable(), which iterates through the player list and adds all the players. That's only performed once for each time you enable the plugin, so it's still very cheap.
     
  11. Offline

    LRFLEW

    I like the original way because it doesn't require storing everybody's ID in the ram, but it would help if there was an escape for if it finds it, like:
    Code:
    Player player;
    boolean found = false;
    
    int eid = entity.getEntityId();
    
    Player[] players = plugin.getServer().getOnlinePlayers();
    
    for(int i=0; i<players.length && !found; i++) {
    
        if (eid == players[i].getEntityId()) {
    
            player = players[i];
    
            //do something
    
            found = true;
        }
    
    }
    what does "super.on*(event)" do?
     
  12. Offline

    axel3200

    use
    Code:
    boolean found = false;
    int i=0;
    
    
    while(i<players.length && !found) {
    
    
        if (eid == players[i].getEntityId()) {
    
            player = players[i];
    
            found = true;
        }
    
        i++;
    
    }
    instead of the for loop.
     
  13. Offline

    jahwag

    It's always a question of space vs. processing time. Since what the hashmap is storing is just references, each entry in it would cost 2*~4 bytes per player, if you have say 32 players that is only 256 bytes to keep in RAM. Whereas running that loop might use a lot of CPU-time on the server.

    Super.on[event] just calls the superclass method we are overriding. It's not really necessary because those are actually empty for now, but say you were subclassing it, you might want to run that. Also we don't know if the API might add things in there in the future.
     
  14. Offline

    LRFLEW

    Isn't that the exact same thing as what's done in the for loop :p
     
  15. Offline

    jahwag

    Here's a slightly shorter variant of your for-loop solution(code is perfect when there is nothing left to remove):
    Code:
    Player result = null;
            for (Player p : plugin.getServer().getOnlinePlayers()) {
                if (p.getEntityId() == soughtID) {
                    result = p;
                    break;
                }
           }
     
  16. Offline

    axel3200

    Well, yes, but the additional condition... in a for loop? Ew. Maybe it's just the way I learned it but that bothers me :p

    EDIT: Break?

    /me shakes head

    And storing a variable for plugin.getServer().getOnlinePlayers() is a better idea, otherwise you're calling the method for each iteration of the loop.
     
  17. Offline

    jahwag

    Well I think you should study some asymptotic analysis before you shake your head at break in a loop. :p

    What you always want to be doing is limiting how many iterations you do. If you have already found the player that matches the entityid, you don't want to continue iterating through the loop until it is finished, because you already have the answer and so that would just be wasting time.
     
  18. Offline

    axel3200

    Which is why you have a boolean that ends the loop for you, instead of having that ugly break in there.
     
  19. Offline

    jahwag

    Hm why do you dislike break? It's not usually considered bad style to use.

    If you think about it, when you declare a boolean instead, what you are doing is allocating space on the heap for that variable. Then you are writing to it once, and then reading it in every iteration. That's two instructions plus one per every player you have.

    Compare that to just saying stop the loop right here, which is what break does. :)
     
  20. Offline

    Afforess


    Well, I hate to tell you guys, but you all just failed programming 101. Here's the right way to do it.
    Code:
    if (entity instanceof Player) {
        Player player = (Player)entity;
        //do stuff
    }
    That will work perfectly.
     
  21. Offline

    LRFLEW

    Where were you 19 posts ago :p
     
  22. Offline

    Afforess

    Playing Minecraft and watching Stargate SG-1, actually.
     
    Europia79 likes this.
  23. Offline

    LRFLEW

    lol
     
  24. Offline

    jahwag

    [pig] In my defense though, I have been up the entire night supposedly working..

    PS. Although we'd never have had the nice back-and-forth with that posted in the beginning. :)
     
  25. Offline

    LRFLEW

    And now I know more about how HashMaps work [diamond]!
     
  26. Offline

    axel3200

    Oh, duh. [sheep]
     
  27. Offline

    DjDCH

    This is remember something ... Yeah, procrastinating ! See:

    [​IMG]

    And, I quite true. (I'm programmer too :p)
     
  28. Offline

    Afforess

    I think the pie chart doesn't take effect of having 2 monitors. The time spent procrastinating watching Netflix Instant is doubled.
     
  29. Offline

    DjDCH

    Oh damn. I think somebody should make a new version of this pie chart considering that point. (But, nice reply :D)
     
  30. Offline

    Plague

    Umm, this does not work for me in damage events. Even though I jump into lava the condition
    Code:
    if (event.getEntity() instanceof Player)
    is still false.
     
Thread Status:
Not open for further replies.

Share This Page