package com.bergerkiller.bukkit.common.utils; import java.util.List; import net.minecraft.server.EntityPlayer; import net.minecraft.server.WorldServer; import net.minecraft.util.com.mojang.authlib.GameProfile; import org.bukkit.Chunk; import org.bukkit.entity.Player; import com.bergerkiller.bukkit.common.Common; import com.bergerkiller.bukkit.common.bases.IntVector2; import com.bergerkiller.bukkit.common.conversion.Conversion; import com.bergerkiller.bukkit.common.conversion.ConversionPairs; import com.bergerkiller.bukkit.common.conversion.util.ConvertingList; import com.bergerkiller.bukkit.common.internal.CommonNMS; import com.bergerkiller.bukkit.common.internal.CommonPlugin; import com.bergerkiller.bukkit.common.reflection.CBClassTemplate; import com.bergerkiller.bukkit.common.reflection.ClassTemplate; import com.bergerkiller.bukkit.common.reflection.FieldAccessor; import com.bergerkiller.bukkit.common.reflection.MethodAccessor; import com.bergerkiller.bukkit.common.reflection.classes.EntityHumanRef; import com.bergerkiller.bukkit.common.reflection.classes.EntityPlayerRef; import com.bergerkiller.bukkit.common.reflection.classes.NetworkManagerRef; import com.bergerkiller.bukkit.common.reflection.classes.PlayerConnectionRef; import com.bergerkiller.bukkit.common.reflection.classes.VectorRef; /** * Player - specific operations and tools */ public class PlayerUtil extends EntityUtil { private static final ClassTemplate<?> CRAFTPLAYER = CBClassTemplate.create("entity.CraftPlayer"); private static final MethodAccessor<Void> setFirstPlayed = CRAFTPLAYER.getMethod("setFirstPlayed", long.class); private static final FieldAccessor<Boolean> hasPlayedBefore = CRAFTPLAYER.getField("hasPlayedBefore"); /** * Gets whether a player is disconnected from the server * * @param player to check * @return True if the player is disconnected, False if not */ public static boolean isDisconnected(Player player) { final Object handle = Conversion.toEntityHandle.convert(player); if (handle == null) { return true; } final Object connection = EntityPlayerRef.playerConnection.get(handle); if (connection == null) { return true; } final Object network = PlayerConnectionRef.networkManager.get(connection); return network == null || !NetworkManagerRef.getIsOpen.invoke(network); } /** * Adds the chunk coordinates of the chunk specified to the player chunk sending queue * * @param player * @param chunk */ public static void queueChunkSend(Player player, Chunk chunk) { queueChunkSend(player, chunk.getX(), chunk.getZ()); } /** * Gets a (referenced) list of all players nearby another Player * * @param player to get the nearby players of * @param radius to look around the player for other playrs * @return list of nearby players */ public static List<Player> getNearbyPlayers(Player player, double radius) { EntityPlayer handle = CommonNMS.getNative(player); List<?> nearbyPlayerHandles = handle.world.a(EntityPlayer.class, handle.boundingBox.grow(radius, radius, radius)); return new ConvertingList<Player>(nearbyPlayerHandles, ConversionPairs.player); } /** * Adds the chunk coordinates to the player chunk sending queue * * @param player * @param coordinates */ public static void queueChunkSend(Player player, IntVector2 coordinates) { queueChunkSend(player, coordinates.x, coordinates.z); } /** * Adds the chunk coordinates to the player chunk sending queue * * @param player * @param chunkX - coordinate * @param chunkZ - coordinate */ @SuppressWarnings("unchecked") public static void queueChunkSend(Player player, int chunkX, int chunkZ) { CommonNMS.getNative(player).chunkCoordIntPairQueue.add(VectorRef.newPair(chunkX, chunkZ)); } /** * Removes the chunk coordinates from the player chunk sending queue * * @param player * @param chunk */ public static void cancelChunkSend(Player player, Chunk chunk) { cancelChunkSend(player, chunk.getX(), chunk.getZ()); } /** * Removes the chunk coordinates from the player chunk sending queue * * @param player * @param coordinates */ public static void cancelChunkSend(Player player, IntVector2 coordinates) { cancelChunkSend(player, coordinates.x, coordinates.z); } /** * Removes the chunk coordinates from the player chunk sending queue * * @param player * @param chunkX - coordinate * @param chunkZ - coordinate */ public static void cancelChunkSend(Player player, int chunkX, int chunkZ) { CommonNMS.getNative(player).chunkCoordIntPairQueue.remove(VectorRef.newPair(chunkX, chunkZ)); } /** * Sets the first time a player played on a server or world * * @param player to set it for * @param firstPlayed time */ public static void setFirstPlayed(org.bukkit.entity.Player player, long firstPlayed) { setFirstPlayed.invoke(player, firstPlayed); } /** * Sets whether the player has played before on this server * * @param player to set it for * @param playedBefore state */ public static void setHasPlayedBefore(Player player, boolean playedBefore) { hasPlayedBefore.set(player, playedBefore); } /** * Get the ping fomr a player * * @param player to get ping from * @return Ping (in ms) */ public static int getPing(Player player) { return CommonNMS.getNative(player).ping; } /** * Change the pinf form a player * * @param player to change ping for * @param ping to replace with (in ms) */ public static void setPing(Player player, int ping) { CommonNMS.getNative(player).ping = ping; } /** * Gets the players game profile * * @param player to get game profile from * @return The player's GameProfile */ public static GameProfile getGameProfile(Player player) { return EntityHumanRef.gameProfile.get(Conversion.toEntityHandle.convert(player)); } /** * Checks whether a given chunk is visible to the client of a player. * This actually checks whether the chunk data had been sent, it doesn't do a distance check. * * @param player to check * @param chunk to check * @return True if the chunk is visible to the player, False if not */ public static boolean isChunkVisible(Player player, Chunk chunk) { return isChunkVisible(player, chunk.getX(), chunk.getZ()); } /** * Checks whether a given chunk is visible to the client of a player. * This actually checks whether the chunk data had been sent, it doesn't do a distance check. * * @param player to check * @param chunkX of the chunk to check * @param chunkZ of the chunk to check * @return True if the chunk is visible to the player, False if not */ public static boolean isChunkVisible(Player player, int chunkX, int chunkZ) { return CommonPlugin.getInstance().getPlayerMeta(player).isChunkVisible(chunkX, chunkZ); } /** * Checks whether a given chunk has been 'entered' by a player. * An entered chunk is liable for updates to the client. * Note that this does not check whether the chunk is actually sent. * * @param player to check * @param chunkX of the chunk to check * @param chunkZ of the chunk to check * @return True if the player entered the chunk, False if not */ public static boolean isChunkEntered(Player player, int chunkX, int chunkZ) { final EntityPlayer ep = CommonNMS.getNative(player); return ((WorldServer) ep.world).getPlayerChunkMap().a(ep, chunkX, chunkZ); } /** * Checks whether a given chunk has been 'entered' by a player. * An entered chunk is liable for updates to the client. * Note that this does not check whether the chunk is actually sent. * * @param player to check * @param chunk to check * @return True if the player entered the chunk, False if not */ public static boolean isChunkEntered(Player player, Chunk chunk) { return isChunkEntered(player, chunk.getX(), chunk.getZ()); } /** * Gets a modifiable List of Entity IDs that are queuing for Player Chunk Packets to be sent * * @param player to get it for * @return Entity Remove Queue */ public static List<Integer> getEntityRemoveQueue(Player player) { return Common.SERVER.getEntityRemoveQueue(player); } }