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