package com.nicewuerfel.blockown.protection;
import com.nicewuerfel.blockown.Material;
import com.nicewuerfel.blockown.Setting;
import com.nicewuerfel.blockown.User;
import com.nicewuerfel.blockown.output.Output;
import org.bukkit.Bukkit;
import org.bukkit.entity.Animals;
import org.bukkit.entity.LivingEntity;
import org.bukkit.plugin.Plugin;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.StreamCorruptedException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull;
public final class Protection {
@Deprecated
private static final String OLD_PROTECTION_FILE_NAME = "protection.dat";
private final Setting setting;
private final File dataFolder;
private final int initialUserCapacity;
private final int initialMaterialCapacity = 50;
private final Map<User, Set<Material>> protections;
private final Map<User, Set<Material>> locks;
private final Map<User, Set<User>> friends;
private ProtectionDecayer decayer = null;
@SuppressWarnings("unchecked")
public Protection(Setting setting, File pluginFolder) throws IOException {
this.setting = setting;
this.dataFolder = pluginFolder;
int initialCapacity;
try {
initialCapacity = Bukkit.getOfflinePlayers().length * 2;
} catch (NullPointerException e) {
// Neccessary for tests
initialCapacity = 0;
}
if (initialCapacity == 0) {
initialCapacity = 64;
}
initialUserCapacity = initialCapacity;
File oldFile = getOldProtectionFile();
if (!oldFile.exists()) {
// Load new protection file
ProtectionLoader loader = new ProtectionLoader(pluginFolder);
protections = loader.loadProtections(initialUserCapacity);
locks = loader.loadLocked(initialUserCapacity);
friends = loader.loadFriends(initialUserCapacity);
} else {
// Load old protection file
Map<User, Set<Material>> loadedProtections = null;
Map<User, Set<Material>> loadedLocks = null;
Map<User, Set<User>> loadedFriends = null;
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(oldFile));
try {
Object object = ois.readObject();
assert object instanceof HashMap<?, ?>;
loadedProtections = (Map<User, Set<Material>>) object;
object = ois.readObject();
assert object instanceof HashMap<?, ?>;
loadedLocks = (Map<User, Set<Material>>) object;
object = ois.readObject();
assert object instanceof HashMap<?, ?>;
loadedFriends = (Map<User, Set<User>>) object;
ois.close();
} catch (ClassNotFoundException | StreamCorruptedException | EOFException e) {
getOutput().printError(
"Corrupted protection file. All manually protected / locked materials are lost.", e);
ois.close();
File corruptFile = new File(pluginFolder, "protection.dat.corrupt");
if ((corruptFile.exists() && !corruptFile.delete()) || !oldFile.renameTo(corruptFile)) {
getOutput().printConsole(
"Protection file couldn't be deleted. Please delete the 'protection.dat' file manually");
}
loadedProtections = (loadedProtections != null) ? loadedProtections
: new ConcurrentHashMap<User, Set<Material>>(initialUserCapacity);
loadedLocks = (loadedLocks != null) ? loadedLocks
: new ConcurrentHashMap<User, Set<Material>>(initialUserCapacity);
loadedFriends = (loadedFriends != null) ? loadedFriends
: new ConcurrentHashMap<User, Set<User>>(initialUserCapacity);
} finally {
protections = loadedProtections;
locks = loadedLocks;
friends = loadedFriends;
}
oldFile.delete();
}
}
private File getDataFolder() {
return dataFolder;
}
private File getOldProtectionFile() {
return new File(getDataFolder(), OLD_PROTECTION_FILE_NAME);
}
private Output getOutput() {
return setting.getOutput();
}
private void save() {
ProtectionLoader loader = new ProtectionLoader(getDataFolder());
try {
loader.storeLocked(locks);
loader.storeProtections(protections);
loader.storeFriends(friends);
} catch (IOException e) {
getOutput().printError("Couldn't save protections!", e);
}
}
private void doAction(@Nonnull ProtectAction protectAction) {
switch (protectAction.getActionType()) {
case PROTECT:
protect(protectAction.getOwner(), protectAction.getMaterial());
return;
case UNPROTECT:
unprotect(protectAction.getOwner(), protectAction.getMaterial());
return;
case LOCK:
lock(protectAction.getOwner(), protectAction.getMaterial());
return;
case UNLOCK:
unlock(protectAction.getOwner(), protectAction.getMaterial());
return;
case FRIEND:
friend(protectAction.getOwner(), protectAction.getUser());
return;
case UNFRIEND:
unfriend(protectAction.getOwner(), protectAction.getUser());
return;
case DROP:
dropUserData(protectAction.getOwner());
return;
default:
throw new IllegalArgumentException();
}
}
private void dropUserData(User user) {
protections.remove(user);
locks.remove(user);
friends.remove(user);
Iterator<Set<User>> iterator = friends.values().iterator();
Set<User> list;
while (iterator.hasNext()) {
list = iterator.next();
list.remove(user);
}
}
private void protect(User owner, Material material) {
if (protections.get(owner) == null) {
protections.put(owner, new HashSet<Material>(initialMaterialCapacity));
}
protections.get(owner).add(material);
}
private void unprotect(User owner, Material material) {
if (protections.get(owner) == null) {
protections.put(owner, new HashSet<Material>(initialMaterialCapacity));
}
protections.get(owner).remove(material);
}
private void lock(User owner, Material material) {
if (locks.get(owner) == null) {
locks.put(owner, new HashSet<Material>(initialMaterialCapacity));
}
locks.get(owner).add(material);
}
private void unlock(User owner, Material material) {
if (locks.get(owner) == null) {
locks.put(owner, new HashSet<Material>(initialMaterialCapacity));
}
locks.get(owner).remove(material);
}
private void friend(User owner, User user) {
if (friends.get(owner) == null) {
friends.put(owner, new HashSet<User>(initialUserCapacity));
}
friends.get(owner).add(user);
}
private void unfriend(User owner, User user) {
if (friends.get(owner) == null) {
friends.put(owner, new HashSet<User>(initialUserCapacity));
}
friends.get(owner).remove(user);
}
private boolean isAllowed(Material material) {
if (material.isAny()) {
return true;
}
return setting.isProtectEnabled(material);
}
private boolean isAutoProtected(Material material) {
if (setting.PROTECTION.AUTO_PROTECT_MATERIALS.contains(material)) {
return true;
}
if (setting.PROTECTION.AUTO_PROTECT_MATERIALS.contains(Material.ANY)) {
return true;
}
if (!material.isBlock()) {
if (Animals.class.isAssignableFrom(material.getEntityType().getEntityClass())) {
return setting.PROTECTION.AUTO_PROTECT_OWNED_ANIMALS;
} else if (!LivingEntity.class.isAssignableFrom(material.getEntityType().getEntityClass())) {
return setting.PROTECTION.AUTO_PROTECT_OWNED_ENTITIES;
} else {
return false;
}
} else {
return setting.PROTECTION.AUTO_PROTECT_OWNED_BLOCKS;
}
}
/**
* Checks whether a user can access an Ownable consisting of the specified material that is owned
* by the specified owner.
*
* @param owner the owner
* @param material the material
* @param user the user
* @return the cause of protection, none if unprotected
*/
public ProtectionCause getAccess(@Nonnull User owner, @Nonnull Material material,
@Nonnull User user) {
Objects.requireNonNull(owner);
Objects.requireNonNull(material);
Objects.requireNonNull(user);
if (owner.equals(user)) {
return ProtectionCause.NONE;
}
if (!isAllowed(material)) {
return ProtectionCause.NONE;
}
if (isListLocked(owner, material)) {
return ProtectionCause.LOCKED;
}
if (isFriend(owner, user)) {
return ProtectionCause.NONE;
}
if (isAutoProtected(material)) {
return ProtectionCause.AUTO_PROTECTED;
}
if (isListProtected(owner, material)) {
return ProtectionCause.PROTECTED;
}
return ProtectionCause.NONE;
}
/**
* Checks whether a user can access an Ownable consisting of the specified material that is owned
* by the specified owner.
*
* @param owner the owner
* @param material the material
* @param user the user
* @return true, if allowed
* @deprecated Use {@link #getAccess(User, Material, User)} instead.
*/
@Deprecated
public boolean hasAccess(@Nonnull User owner, @Nonnull Material material, @Nonnull User user) {
return getAccess(owner, material, user) == ProtectionCause.NONE;
}
private boolean isListLocked(User owner, Material material) {
Set<Material> lockList = locks.get(owner);
if (lockList == null) {
return false;
}
if (lockList.contains(material)) {
return true;
}
return lockList.contains(Material.ANY);
}
/**
* Checks whether owner has declared user as a friend. Please note that "user is friend of owner"
* doesn't imply "owner is friend of user"
*
* @param owner the owner
* @param user the user
* @return true, if is friend
*/
public boolean isFriend(User owner, User user) {
Set<User> friendList = friends.get(owner);
if (friendList == null) {
return false;
}
return (friendList.contains(user));
}
private boolean isListProtected(User owner, Material material) {
Set<Material> protectionList = protections.get(owner);
if (protectionList == null) {
return false;
}
if (protectionList.contains(material)) {
return true;
}
return protectionList.contains(Material.ANY);
}
public boolean isListed(@Nonnull User owner, @Nonnull Material material) {
if (!isAllowed(material)) {
return false;
}
return isAutoProtected(material) || isListProtected(owner, material)
|| isListLocked(owner, material);
}
/**
* Gets an immutable Set of friends for a user.
*
* @param owner the User
* @return immutable Set, never null
*/
@Nonnull
public Set<User> getFriends(User owner) {
Set<User> result = friends.get(owner);
if (result == null) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(result);
}
/**
* Gets an immutable Set of locked materials for a user.
*
* @param owner the User
* @return immutable Set, never null
*/
@Nonnull
public Set<Material> getLocks(User owner) {
Set<Material> result = locks.get(owner);
if (result == null) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(result);
}
/**
* Gets an immutable Set of protected materials for a user.
*
* @param owner the User
* @return immutable Set, never null
*/
@Nonnull
public Set<Material> getProtections(User owner) {
Set<Material> result = protections.get(owner);
if (result == null) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(result);
}
/**
* Enqueue a ProtectAction.
*
* @param protectAction the ProtectAction
*/
public final void enqueue(@Nonnull final ProtectAction protectAction) {
doAction(protectAction);
}
/**
* Needs to be called to make protection thread empty the queue before program ends.
*/
public void disable() {
save();
}
public ProtectionDecayer getDecayer(Plugin plugin) {
if (this.decayer == null) {
this.decayer = new ProtectionDecayer(setting, this, plugin);
}
return this.decayer;
}
}