package com.nicewuerfel.blockown; import com.google.common.base.Optional; import com.nicewuerfel.blockown.protection.ProtectionCause; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.entity.Player; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.WeakHashMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * Represents a player on the Server. Users are identified by a {@link UUID}.<br> * * @author Pheasn */ public final class User implements Serializable { /** * Returned by {@link #getName()} if {@link #getOfflinePlayer()} returns null. */ public static final String UNKNOWN_USER_NAME = "UNKNOWN_USER"; private static final long serialVersionUID = -9170381319637300740L; private static final Map<UUID, User> users = new WeakHashMap<>(128); private static BlockOwn plugin = null; @Nonnull private final UUID id; @Nonnull private transient WeakReference<OfflinePlayer> playerRef = new WeakReference<>(null); /** * Retrieves a User instance for the given {@link UUID}.<br> * Same result as calling {@link #getInstance(UUID, OfflinePlayer) getInstance(id, null)} * * @param id the {@link UUID} * @return the {@link User} */ @Nonnull public static User getInstance(@Nonnull UUID id) { return getInstance(id, null); } /** * Retrieves a User instance for the given {@link UUID} and stores a {@link WeakReference} for the * associated {@link OfflinePlayer}. * * @param id the {@link UUID} * @param player the {@link OfflinePlayer} * @return the {@link User} */ @Nonnull public static User getInstance(@Nonnull UUID id, @Nullable OfflinePlayer player) { Objects.requireNonNull(id); User user = users.get(id); if (user == null) { user = new User(id, player); synchronized (users) { users.put(id, user); } } else if (player != null) { user.playerRef = new WeakReference<>(player); } return user; } private User(@Nonnull UUID id, @Nullable OfflinePlayer player) { this.id = id; this.playerRef = new WeakReference<>(player); } /** * Initializes the {@link BlockOwn} reference, which is needed for almost every non-static method. * * @param plugin the {@link BlockOwn} instance */ static synchronized void initialize(@Nonnull BlockOwn plugin) { User.plugin = Objects.requireNonNull(plugin); } /** * Returns the {@link UUID} of this user. * * @return the UUID */ @Nonnull public UUID getUniqueId() { return id; } /** * Determines whether this player is currently online.<br> * Returns false if no associated {@link OfflinePlayer} could be found. * * @return whether the player is online */ public boolean isOnline() { OfflinePlayer offlinePlayer = getOfflinePlayer(); return (offlinePlayer != null) && offlinePlayer.isOnline(); } /** * Determines whether this player is a server operator.<br> * Returns false if no associated {@link OfflinePlayer} could be found. * * @return whether the {@link User} is an op */ public boolean isOp() { OfflinePlayer offlinePlayer = getOfflinePlayer(); return (offlinePlayer != null) && offlinePlayer.isOp(); } /** * Gets the last known name of the player represented by this User.<br> * Also returns {@link User#UNKNOWN_USER_NAME} if no associated {@link OfflinePlayer} could be * found. * * @return the name */ @Nonnull public String getName() { OfflinePlayer offlinePlayer = getOfflinePlayer(); return (offlinePlayer == null) ? UNKNOWN_USER_NAME : offlinePlayer.getName(); } /** * Gets the associated {@link OfflinePlayer} for this user.<br> * This method may have to look up the player by calling {@link org.bukkit.Server#getPlayer(UUID) * Server.getPlayer(UUID)}. If that call returns null, this method returns null. * * @return the {@link OfflinePlayer} */ @Nullable public OfflinePlayer getOfflinePlayer() { OfflinePlayer offlinePlayer = playerRef.get(); if (offlinePlayer == null) { try { offlinePlayer = getPlugin().getServer().getOfflinePlayer(id); playerRef = new WeakReference<>(offlinePlayer); } catch (NullPointerException e) { return null; } } return offlinePlayer; } /** * Gets the {@link World} this {@link User} is currently in.<br> * <h2>Returns null, if:</h2> * <ul> * <li>{@link #getOfflinePlayer()} returned null</li> * <li>{@link OfflinePlayer#getPlayer()} returned null (player is offline)</li> * </ul> * * @return the {@link World} */ @Nullable public World getWorld() { OfflinePlayer offlinePlayer = getOfflinePlayer(); if (offlinePlayer == null) { return null; } Player player = offlinePlayer.getPlayer(); if (player == null) { return null; } return player.getWorld(); } private BlockOwn getPlugin() { if (plugin == null) { initialize((BlockOwn) Bukkit.getPluginManager().getPlugin(BlockOwn.class.getSimpleName())); } return plugin; } /** * Determines the {@link Material} and owner of the given {@link Ownable} and calls * {@link User#hasAccess(Material, User)} * * @param ownable the {@link Ownable} * @return whether this {@link User} has access to the given {@link Ownable} * @throws InvalidWorldNameException if the {@link Ownable} references a {@link World} that * doesn't exist */ public boolean hasAccess(@Nonnull Ownable ownable) throws InvalidWorldNameException { Optional<User> owner = getPlugin().getOwningDatabase().getOwner(ownable); if (owner.isPresent()) { return hasAccess(ownable.getMaterial(), owner.get()); } else { return true; } } /** * Determines whether this user has access to an {@link Ownable} of the given {@link Material} * owned by the given owner. * * @param material the {@link Material} * @param owner the owner * @return whether this {@link User} has access to the given {@link Material} of the given owner */ @Deprecated public boolean hasAccess(@Nonnull Material material, @Nonnull User owner) { return getPlugin().getProtection().hasAccess(owner, material, this); } public ProtectionCause getAccess(@Nonnull Material material, @Nonnull User owner) { return getPlugin().getProtection().getAccess(owner, material, this); } /** * Determines whether this {@link User} has protected or locked the given {@link Material}. * * @param material the {@link Material} * @return whether the Material is protected */ public boolean hasListed(@Nonnull Material material) { return getPlugin().getProtection().isListed(this, material); } /** * Determines whether this {@link User User}'s friends list contains the given user. * * @param user the {@link User} * @return whether the given user is a friend of this user */ public boolean hasFriend(@Nonnull User user) { return getPlugin().getProtection().isFriend(this, user); } /** * Determines whether this {@link User} is currently in "ignore mode" and by that ignores any * protection. * * @return whether this user is ignoring */ public boolean isIgnoring() { return getPlugin().getSettings().isIgnoring(this); } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); playerRef = new WeakReference<>(null); } @Override public int hashCode() { return id.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof User)) { return false; } User other = (User) obj; if (!id.equals(other.id)) { return false; } return true; } @Override public String toString() { return getUniqueId().toString(); } }