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();
}
}