[LIB] GhostFactory - Make players look like ghosts

Discussion in 'Resources' started by lenis0012, May 24, 2013.

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

    lenis0012

    Hey there, its lenis0012 again.

    I have made you a simple libary to make a player look like a ghost.
    Image:
    [​IMG]

    All you have to do is add the java class i will put down below in your plugin and follow this example code:
    Code:java
    1. //On top of your main class
    2. public GhostFactory ghostFactory;
    3.  
    4. //In your onEnable function
    5. this.ghostFactory = new GhostFactory(this, true); //true - Show ghosts to all players
    6. //if false will only show to ghost busters (add/removeGhostBuster)
    7. ghostFactory.create(); //Create factory
    8.  
    9. //In an event (example PlayerJoinEvent)
    10. Player player = event.getPlayer();
    11. ghostFactory.addGhost(player);
    12.  
    13. //Anywhere you want to undisguise a ghost
    14. ghostFactory.removeGhost(player);


    The class autopmaticly takes care of players joining and leaving.
    When they leave the game it will remove them from the ghost list.
    You can also use these extra functions:
    • ghostFacory.getGhosts(); - Returns a string array of all ghosts
    • ghostFactory.clearGhosts(); - Clears all ghosts
    Here is the code you need to put in your plugin somewhere:
    ** @Comphenix's version **
    Code:
    package com.yourpackage.utils;
     
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;
     
    import org.bukkit.Bukkit;
    import org.bukkit.OfflinePlayer;
    import org.bukkit.entity.Player;
    import org.bukkit.plugin.Plugin;
    import org.bukkit.potion.PotionEffect;
    import org.bukkit.potion.PotionEffectType;
    import org.bukkit.scheduler.BukkitTask;
    import org.bukkit.scoreboard.Scoreboard;
    import org.bukkit.scoreboard.Team;
     
    public class GhostFactory {
        /**
        * Team of ghosts and people who can see ghosts.
        */
        private static final String GHOST_TEAM_NAME = "Ghosts";
        private static final long UPDATE_DELAY = 20L;
     
        // No players in the ghost factory
        private static final OfflinePlayer[] EMPTY_PLAYERS = new OfflinePlayer[0];
        private Team ghostTeam;
     
        // Task that must be cleaned up
        private BukkitTask task;
        private boolean closed;
     
        // Players that are actually ghosts
        private Set<String> ghosts = new HashSet<String>();
     
        public GhostFactory(Plugin plugin) {
            // Initialize
            createTask(plugin);
            createGetTeam();
        }
     
        private void createGetTeam() {
            Scoreboard board = Bukkit.getServer().getScoreboardManager().getMainScoreboard();
         
            ghostTeam = board.getTeam(GHOST_TEAM_NAME);
         
            // Create a new ghost team if needed
            if (ghostTeam == null) {
                ghostTeam = board.registerNewTeam(GHOST_TEAM_NAME);
            }
            // Thanks to Rprrr for noticing a bug here
            ghostTeam.setCanSeeFriendlyInvisibles(true);
        }
     
        private void createTask(Plugin plugin) {
            task = Bukkit.getScheduler().runTaskTimer(plugin, new Runnable() {
                @Override
                public void run() {
                    for (OfflinePlayer member : getMembers()) {
                        Player player = member.getPlayer();
     
                        if (player != null) {
                            // Update invisibility effect
                            setGhost(player, isGhost(player));
                        } else {
                            ghosts.remove(member.getName());
                            ghostTeam.removePlayer(member);
                        }
                    }
                }
            }, UPDATE_DELAY, UPDATE_DELAY);
        }
     
        /**
        * Remove all existing player members and ghosts.
        */
        public void clearMembers() {
            if (ghostTeam != null) {
                for (OfflinePlayer player : getMembers()) {
                    ghostTeam.removePlayer(player);
                }
            }
        }
     
        /**
        * Add the given player to this ghost manager. This ensures that it can see ghosts, and later become one.
        * @param player - the player to add to the ghost manager.
        */
        public void addPlayer(Player player) {
            validateState();
            if (!ghostTeam.hasPlayer(player)) {
                ghostTeam.addPlayer(player);
                player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 15));
            }
        }
     
        /**
        * Determine if the given player is tracked by this ghost manager and is a ghost.
        * @param player - the player to test.
        * @return TRUE if it is, FALSE otherwise.
        */
        public boolean isGhost(Player player) {
            return player != null && hasPlayer(player) && ghosts.contains(player.getName());
        }
     
        /**
        * Determine if the current player is tracked by this ghost manager, or is a ghost.
        * @param player - the player to check.
        * @return TRUE if it is, FALSE otherwise.
        */
        public boolean hasPlayer(Player player) {
            validateState();
            return ghostTeam.hasPlayer(player);
        }
     
        /**
        * Set wheter or not a given player is a ghost.
        * @param player - the player to set as a ghost.
        * @param isGhost - TRUE to make the given player into a ghost, FALSE otherwise.
        */
        public void setGhost(Player player, boolean isGhost) {
            // Make sure the player is tracked by this manager
            if (!hasPlayer(player))
                addPlayer(player);
     
            if (isGhost) {
                ghosts.add(player.getName());
                player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 15));
            } else if (!isGhost) {
                ghosts.remove(player.getName());
                player.removePotionEffect(PotionEffectType.INVISIBILITY);
            }
        }
     
        /**
        * Remove the given player from the manager, turning it back into the living and making it unable to see ghosts.
        * @param player - the player to remove from the ghost manager.
        */
        public void removePlayer(Player player) {
            validateState();
            if (ghostTeam.removePlayer(player)) {
                player.removePotionEffect(PotionEffectType.INVISIBILITY);
            }
        }
     
        /**
        * Retrieve every ghost currently tracked by this manager.
        * @return Every tracked ghost.
        */
        public OfflinePlayer[] getGhosts() {
            validateState();
            Set<OfflinePlayer> players = new HashSet<OfflinePlayer>(ghostTeam.getPlayers());
         
            // Remove all non-ghost players
            for (Iterator<OfflinePlayer> it = players.iterator(); it.hasNext(); ) {
                if (!ghosts.contains(it.next().getName())) {
                    it.remove();
                }
            }
            return toArray(players);
        }
     
        /**
        * Retrieve every ghost and every player that can see ghosts.
        * @return Every ghost or every observer.
        */
        public OfflinePlayer[] getMembers() {
            validateState();
            return toArray(ghostTeam.getPlayers());
        }
     
        private OfflinePlayer[] toArray(Set<OfflinePlayer> players) {
            if (players != null) {
                return players.toArray(new OfflinePlayer[0]);
            } else {
                return EMPTY_PLAYERS;
            }
        }
     
        public void close() {
            if (!closed) {
                task.cancel();
                ghostTeam.unregister();
                closed = true;
            }
        }
     
        public boolean isClosed() {
            return closed;
        }
     
        private void validateState() {
            if (closed) {
                throw new IllegalStateException("Ghost factory has closed. Cannot reuse instances.");
            }
        }
    }
     
    
     
  2. Offline

    C0nsole

    Wow this is so useful! Thanks so much!
     
    hawkfalcon likes this.
  3. Offline

    Tzeentchful

    lenis0012
    Why are you using packets to send the scoreboard team?
    You could use the bukkit API and remove a lot of the overhead this library would create.
     
  4. Offline

    lenis0012

    I dont think that bukkit's API allows you to customize it that much.

    And i coded this at the time there was no scoreboard API yet.
     
  5. Offline

    Tzeentchful

    lenis0012
    Yes it allows you do everything you can do with packets.
    I would recommend moving to the bukkit API it would remove the overhead of reflection.
    Code:java
    1. Scoreboard sb = getServer().getScoreboardManager().getMainScoreboard();
    2. Team team = sb.registerNewTeam("ghosts");
    3. team.setDisplayName("Ghosts");
    4.  
    5. team.addPlayer(Bukkit.getOfflinePlayer("player"));
     
  6. Offline

    Comphenix

    Here's how you can do this without having to resort to packets or NMS, making this compatible with all future updates (download):
    Code:java
    1. // Based on lenis0012's GhostFactory
    2. package com.comphenix.example;
    3.  
    4. import java.util.Set;
    5.  
    6. import org.bukkit.Bukkit;
    7. import org.bukkit.OfflinePlayer;
    8. import org.bukkit.entity.Player;
    9. import org.bukkit.plugin.Plugin;
    10. import org.bukkit.potion.PotionEffect;
    11. import org.bukkit.potion.PotionEffectType;
    12. import org.bukkit.scheduler.BukkitTask;
    13. import org.bukkit.scoreboard.Scoreboard;
    14. import org.bukkit.scoreboard.Team;
    15.  
    16. public class GhostManager {
    17. private static final String GHOST_TEAM_NAME = "Ghosts";
    18. private static final long UPDATE_DELAY = 5L;
    19.  
    20. // No players in the ghost factory
    21. private static final OfflinePlayer[] EMPTY_PLAYERS = new OfflinePlayer[0];
    22.  
    23. private Team ghostTeam;
    24.  
    25. // Task that must be cleaned up
    26. private BukkitTask task;
    27. private boolean closed;
    28.  
    29. public GhostManager(Plugin plugin) {
    30. // Initialize
    31. createTask(plugin);
    32. createGetTeam();
    33. }
    34.  
    35. private void createGetTeam() {
    36. Scoreboard board = Bukkit.getServer().getScoreboardManager().getMainScoreboard();
    37.  
    38. ghostTeam = board.getTeam(GHOST_TEAM_NAME);
    39.  
    40. // Create a new ghost team if needed
    41. if (ghostTeam == null) {
    42. ghostTeam = board.registerNewTeam(GHOST_TEAM_NAME);
    43. ghostTeam.setCanSeeFriendlyInvisibles(true);
    44. }
    45. }
    46.  
    47. private void createTask(Plugin plugin) {
    48. task = Bukkit.getScheduler().runTaskTimer(plugin, new Runnable() {
    49. @Override
    50. public void run() {
    51. for (OfflinePlayer ghostPlayer : getGhosts()) {
    52. Player player = ghostPlayer.getPlayer();
    53.  
    54. if (player != null) {
    55. if(!player.hasPotionEffect(PotionEffectType.INVISIBILITY)) {
    56. player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 15));
    57. }
    58. } else {
    59. ghostTeam.removePlayer(ghostPlayer);
    60. }
    61. }
    62. }
    63. }, UPDATE_DELAY, UPDATE_DELAY);
    64. }
    65.  
    66. public void clearGhosts() {
    67. if (ghostTeam != null) {
    68. for (OfflinePlayer player : getGhosts()) {
    69. ghostTeam.removePlayer(player);
    70. }
    71. }
    72. }
    73.  
    74. public void addGhost(Player player) {
    75. validateState();
    76. if (!ghostTeam.hasPlayer(player)) {
    77. ghostTeam.addPlayer(player);
    78. player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 15));
    79. }
    80. }
    81.  
    82. public boolean hasGhost(Player player) {
    83. validateState();
    84. return ghostTeam.hasPlayer(player);
    85. }
    86.  
    87. public void removeGhost(Player player) {
    88. validateState();
    89. if (ghostTeam.removePlayer(player)) {
    90. player.removePotionEffect(PotionEffectType.INVISIBILITY);
    91. }
    92. }
    93.  
    94. public OfflinePlayer[] getGhosts() {
    95. validateState();
    96. Set<OfflinePlayer> players = ghostTeam.getPlayers();
    97.  
    98. if (players != null) {
    99. return players.toArray(new OfflinePlayer[0]);
    100. } else {
    101. return EMPTY_PLAYERS;
    102. }
    103. }
    104.  
    105. public void close() {
    106. if (!closed) {
    107. task.cancel();
    108. ghostTeam.unregister();
    109. closed = true;
    110. }
    111. }
    112.  
    113. public boolean isClosed() {
    114. return closed;
    115. }
    116.  
    117. private void validateState() {
    118. if (closed) {
    119. throw new IllegalStateException("Ghost factory has closed. Cannot reuse instances.");
    120. }
    121. }
    122. }


    Here's an example of how to use it:
    Code:java
    1. public class ExampleMod extends JavaPlugin implements Listener {
    2. private GhostManager manager;
    3.  
    4. @Override
    5. public void onEnable() {
    6. getServer().getPluginManager().registerEvents(this, this);
    7.  
    8. manager = new GhostManager(this);
    9. }
    10.  
    11. @EventHandler
    12. public void onPlayerJoinEvent(PlayerJoinEvent e) {
    13. manager.addGhost(e.getPlayer());
    14. }
    15.  
    16. @Override
    17. public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
    18. if (sender instanceof Player) {
    19. Player player = (Player) sender;
    20.  
    21. if (manager.hasGhost(player))
    22. manager.removeGhost(player);
    23. else
    24. manager.addGhost(player);
    25. }
    26. return true;
    27. }
    28. }
     
  7. Offline

    AstramG

    How could I let the other players see the player in Ghost form without the other player being 100% invisible to them.
     
  8. Offline

    lenis0012

    Does it make the player 100% invicible?
    It shouldn't
     
  9. Offline

    AstramG

    It shows the player as completely invisible, except for on the player's client he's transparent.
     
  10. Offline

    Comphenix

    Try this version then (download):
    Code:java
    1. package com.comphenix.example;
    2.  
    3. import java.util.HashSet;
    4. import java.util.Iterator;
    5. import java.util.Set;
    6.  
    7. import org.bukkit.Bukkit;
    8. import org.bukkit.OfflinePlayer;
    9. import org.bukkit.entity.Player;
    10. import org.bukkit.plugin.Plugin;
    11. import org.bukkit.potion.PotionEffect;
    12. import org.bukkit.potion.PotionEffectType;
    13. import org.bukkit.scheduler.BukkitTask;
    14. import org.bukkit.scoreboard.Scoreboard;
    15. import org.bukkit.scoreboard.Team;
    16.  
    17. public class GhostManager {
    18. /**
    19.   * Team of ghosts and people who can see ghosts.
    20.   */
    21. private static final String GHOST_TEAM_NAME = "Ghosts";
    22. private static final long UPDATE_DELAY = 20L;
    23.  
    24. // No players in the ghost factory
    25. private static final OfflinePlayer[] EMPTY_PLAYERS = new OfflinePlayer[0];
    26. private Team ghostTeam;
    27.  
    28. // Task that must be cleaned up
    29. private BukkitTask task;
    30. private boolean closed;
    31.  
    32. // Players that are actually ghosts
    33. private Set<String> ghosts = new HashSet<String>();
    34.  
    35. public GhostManager(Plugin plugin) {
    36. // Initialize
    37. createTask(plugin);
    38. createGetTeam();
    39. }
    40.  
    41. private void createGetTeam() {
    42. Scoreboard board = Bukkit.getServer().getScoreboardManager().getMainScoreboard();
    43.  
    44. ghostTeam = board.getTeam(GHOST_TEAM_NAME);
    45.  
    46. // Create a new ghost team if needed
    47. if (ghostTeam == null) {
    48. ghostTeam = board.registerNewTeam(GHOST_TEAM_NAME);
    49. ghostTeam.setCanSeeFriendlyInvisibles(true);
    50. }
    51.  
    52.  
    53. }
    54.  
    55. private void createTask(Plugin plugin) {
    56. task = Bukkit.getScheduler().runTaskTimer(plugin, new Runnable() {
    57. @Override
    58. public void run() {
    59. for (OfflinePlayer member : getMembers()) {
    60. Player player = member.getPlayer();
    61.  
    62. if (player != null) {
    63. // Update invisibility effect
    64. setGhost(player, isGhost(player));
    65. } else {
    66. ghosts.remove(member.getName());
    67. ghostTeam.removePlayer(member);
    68. }
    69. }
    70. }
    71. }, UPDATE_DELAY, UPDATE_DELAY);
    72. }
    73.  
    74. /**
    75.   * Remove all existing player members and ghosts.
    76.   */
    77. public void clearMembers() {
    78. if (ghostTeam != null) {
    79. for (OfflinePlayer player : getMembers()) {
    80. ghostTeam.removePlayer(player);
    81. }
    82. }
    83. }
    84.  
    85. /**
    86.   * Add the given player to this ghost manager. This ensures that it can see ghosts, and later become one.
    87.   * @param player - the player to add to the ghost manager.
    88.   */
    89. public void addPlayer(Player player) {
    90. validateState();
    91. if (!ghostTeam.hasPlayer(player)) {
    92. ghostTeam.addPlayer(player);
    93. player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 15));
    94. }
    95. }
    96.  
    97. /**
    98.   * Determine if the given player is tracked by this ghost manager and is a ghost.
    99.   * @param player - the player to test.
    100.   * @return TRUE if it is, FALSE otherwise.
    101.   */
    102. public boolean isGhost(Player player) {
    103. return player != null && hasPlayer(player) && ghosts.contains(player.getName());
    104. }
    105.  
    106. /**
    107.   * Determine if the current player is tracked by this ghost manager, or is a ghost.
    108.   * @param player - the player to check.
    109.   * @return TRUE if it is, FALSE otherwise.
    110.   */
    111. public boolean hasPlayer(Player player) {
    112. validateState();
    113. return ghostTeam.hasPlayer(player);
    114. }
    115.  
    116. /**
    117.   * Set wheter or not a given player is a ghost.
    118.   * @param player - the player to set as a ghost.
    119.   * @param isGhost - TRUE to make the given player into a ghost, FALSE otherwise.
    120.   */
    121. public void setGhost(Player player, boolean isGhost) {
    122. // Make sure the player is tracked by this manager
    123. if (!hasPlayer(player))
    124. addPlayer(player);
    125.  
    126. if (isGhost) {
    127. ghosts.add(player.getName());
    128. player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 15));
    129. } else if (!isGhost) {
    130. ghosts.remove(player.getName());
    131. player.removePotionEffect(PotionEffectType.INVISIBILITY);
    132. }
    133. }
    134.  
    135. /**
    136.   * Remove the given player from the manager, turning it back into the living and making it unable to see ghosts.
    137.   * @param player - the player to remove from the ghost manager.
    138.   */
    139. public void removePlayer(Player player) {
    140. validateState();
    141. if (ghostTeam.removePlayer(player)) {
    142. player.removePotionEffect(PotionEffectType.INVISIBILITY);
    143. }
    144. }
    145.  
    146. /**
    147.   * Retrieve every ghost currently tracked by this manager.
    148.   * @return Every tracked ghost.
    149.   */
    150. public OfflinePlayer[] getGhosts() {
    151. validateState();
    152. Set<OfflinePlayer> players = new HashSet<OfflinePlayer>(ghostTeam.getPlayers());
    153.  
    154. // Remove all non-ghost players
    155. for (Iterator<OfflinePlayer> it = players.iterator(); it.hasNext(); ) {
    156. if (!ghosts.contains(it.next().getName())) {
    157. it.remove();
    158. }
    159. }
    160. return toArray(players);
    161. }
    162.  
    163. /**
    164.   * Retrieve every ghost and every player that can see ghosts.
    165.   * @return Every ghost or every observer.
    166.   */
    167. public OfflinePlayer[] getMembers() {
    168. validateState();
    169. return toArray(ghostTeam.getPlayers());
    170. }
    171.  
    172. private OfflinePlayer[] toArray(Set<OfflinePlayer> players) {
    173. if (players != null) {
    174. return players.toArray(new OfflinePlayer[0]);
    175. } else {
    176. return EMPTY_PLAYERS;
    177. }
    178. }
    179.  
    180. public void close() {
    181. if (!closed) {
    182. task.cancel();
    183. ghostTeam.unregister();
    184. closed = true;
    185. }
    186. }
    187.  
    188. public boolean isClosed() {
    189. return closed;
    190. }
    191.  
    192. private void validateState() {
    193. if (closed) {
    194. throw new IllegalStateException("Ghost factory has closed. Cannot reuse instances.");
    195. }
    196. }
    197. }
    198.  
     
    LegoPal92, IDragonfire and Skyost like this.
  11. Offline

    DjMacmo

    This is awesome! Thanks for this! :)

    EDIT by Gravity: Removed excessive profanity
     
    KingFaris11, C0nsole and lenis0012 like this.
  12. Offline

    lenis0012

    Comphenix

    Oh, i thought it was my code that wasn't working xD
     
  13. Offline

    Comphenix

    No, I don't think your version worked that way either. Only players added as ghosts will see themselves and others as ghosts.
     
  14. Offline

    lenis0012

    Thats not true.
    In the picture on the top i used two accounts to make a screenshot from the 2nd account :p
     
  15. Offline

    Comphenix

    Yes, but I believe that the player on that other account was a ghost as well. If not, you don't get the ghost effect, as only team members can see invisible players due to getCanSeeFriendlyInvisibles:
    [​IMG]
     
  16. Offline

    lenis0012

    Oh, that makes it alot more complicated :p
    I gues you would have to add all players to the team.
    And the invicibility potion effect enables the ghost effect

    Are you gonna answer my pm on BukkitDev btw?
     
  17. Offline

    Comphenix

    Yup. That's what I did in version 2.

    Ah, sorry, must have marked it as read by accident. I'll take a look.

    EDIT: Okay, I've sent your a response. Quite the conversation we have there, gotten to 60 messages already.
     
    lenis0012 likes this.
  18. Offline

    Paper

    Wow thanks lenis0012! Also, is there any way that I can make the person look like a ghost to certain players? Because it looks like this code makes the player look like a ghost to everyone.
     
  19. Offline

    lenis0012

    Im gonna have to patch some things in it soon
     
  20. Offline

    ftbastler

    Cool, let us know when you got it working. :)
     
  21. That is really cool.
     
  22. Offline

    Kainzo

    That's nifty.

    You got time to try and integrate this into a Heroes skill with me? I'm having a hard time understanding some things.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 1, 2016
  23. Offline

    Comphenix

    Or you could try explaining what you don't understand in a forum post here, or even in a different thread. That might also help others who are confused.
     
  24. Offline

    Kainzo

    well... after looking into this... to integrate this into a plugin will be a bit harder than I had first expected.

    We'll have to create a scoreboard API in the plugin and then sort of make some of these calls.
    I was hoping for a quick and easy implementation.
     
  25. Offline

    Comphenix

    I would say my current implementation is pretty easy to use though - basically, you have a set of players that are managed by the ghost manager. They're the only ones able to see ghosts or become one, so it is possible to turn off the ghost effect by removing a player from the manager.

    Of course, because the scoreboard system is so inflexible (it's mostly client-side), all of these players must be on the same team for the effect to work. This may indeed conflict with custom maps or other uses of the scoreboard system, and I don't think there's a way around it. Blame Dinnerbone for that one. :p
     
  26. Holy cow, this is sick! :O
    I'll be sure to use this, thanks!
     
  27. Offline

    lenis0012

    I could get you a simpel code to implements it.
    Just pm me what methodss you want in the class.
     
  28. Offline

    gabizou

    So far, I'm understanding though that players can be added to multiple scoreboards at the same time, correct?
    So a HeroesSkill that uses the same setup of GhostManager would have to onPlayerJoin add them to the team for that scoreboard, and this wouldn't interfere with scoreboards used by things like BattleArena?
     
  29. Offline

    Comphenix

    No, you can only be a member of a single team - just try it out yourself. This is even hard coded in the client.

    This is the big problem with the scoreboard system. It apparently was never designed for Bukkit or mods in mind.
     
  30. Offline

    gabizou

    Grr. so, after a plugin releases a player from the team, the team they were involved in prior is nullified. Well, this makes things very difficult indeed.
     
Thread Status:
Not open for further replies.

Share This Page