bentobox/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java

954 lines
36 KiB
Java
Raw Normal View History

package world.bentobox.bentobox.managers;
2017-05-20 23:09:53 +02:00
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
2017-05-20 23:09:53 +02:00
import java.util.UUID;
import org.bukkit.Bukkit;
2017-08-06 01:09:58 +02:00
import org.bukkit.GameMode;
2017-05-20 23:09:53 +02:00
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Openable;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Monster;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
2019-01-12 19:31:25 +01:00
import world.bentobox.bentobox.api.events.IslandBaseEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
2018-08-06 18:00:47 +02:00
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.Database;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.database.objects.IslandDeletion;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.island.IslandCache;
import world.bentobox.bentobox.util.DeleteIslandChunks;
import world.bentobox.bentobox.util.Util;
import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
2017-05-20 23:09:53 +02:00
/**
* The job of this class is manage all island related data.
2017-06-10 20:54:32 +02:00
* It also handles island ownership, including team, trustees, coops, etc.
* The data object that it uses is Island
* @author tastybento
*/
2017-05-20 23:09:53 +02:00
public class IslandsManager {
2018-07-29 22:21:46 +02:00
private BentoBox plugin;
/**
* One island can be spawn, this is the one - otherwise, this value is null
*/
@NonNull
private Map<@NonNull World, @Nullable Island> spawn;
@NonNull
private Database<Island> handler;
2017-05-31 16:32:36 +02:00
/**
* The last locations where an island were put.
* This is not stored persistently and resets when the server starts
*/
private Map<World, Location> last;
// Island Cache
@NonNull
private IslandCache islandCache;
/**
* Islands Manager
2018-06-10 01:40:38 +02:00
* @param plugin - plugin
*/
2018-07-29 22:21:46 +02:00
public IslandsManager(BentoBox plugin){
2017-05-20 23:09:53 +02:00
this.plugin = plugin;
2017-06-10 20:54:32 +02:00
// Set up the database handler to store and retrieve Island classes
handler = new Database<>(plugin, Island.class);
islandCache = new IslandCache();
spawn = new HashMap<>();
last = new HashMap<>();
2017-05-20 23:09:53 +02:00
}
/**
* This is a generic scan that can work in the overworld or the nether
* @param l - location around which to scan
2018-02-18 02:01:25 +01:00
* @param i - the range to scan for a location less than 0 means the full island.
* @return - safe location, or null if none can be found
*/
@Nullable
public Location bigScan(@NonNull Location l, int i) {
final int height;
final int depth;
if (i > 0) {
height = i;
depth = i;
} else {
Optional<Island> island = getIslandAt(l);
if (!island.isPresent()) {
return null;
}
i = island.get().getProtectionRange();
height = l.getWorld().getMaxHeight() - l.getBlockY();
depth = l.getBlockY();
2017-05-20 23:09:53 +02:00
}
// Work outwards from l until the closest safe location is found.
int minXradius = 0;
int maxXradius = 0;
int minZradius = 0;
int maxZradius = 0;
int minYradius = 0;
int maxYradius = 0;
do {
int minX = l.getBlockX()-minXradius;
int minZ = l.getBlockZ()-minZradius;
int minY = l.getBlockY()-minYradius;
int maxX = l.getBlockX()+maxXradius;
int maxZ = l.getBlockZ()+maxZradius;
int maxY = l.getBlockY()+maxYradius;
for (int x = minX; x<= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
for (int y = minY; y <= maxY; y++) {
if (!((x > minX && x < maxX) && (z > minZ && z < maxZ) && (y > minY && y < maxY))) {
Location ultimate = new Location(l.getWorld(), x + 0.5D, y, z + 0.5D);
if (isSafeLocation(ultimate)) {
return ultimate;
}
}
}
}
}
if (minXradius < i) {
minXradius++;
}
if (maxXradius < i) {
maxXradius++;
}
if (minZradius < i) {
minZradius++;
}
if (maxZradius < i) {
maxZradius++;
}
if (minYradius < depth) {
minYradius++;
}
if (maxYradius < height) {
maxYradius++;
}
} while (minXradius < i || maxXradius < i || minZradius < i || maxZradius < i || minYradius < depth
|| maxYradius < height);
// Nothing worked
return null;
2017-05-20 23:09:53 +02:00
}
2018-07-20 14:37:11 +02:00
/**
* Checks if this location is safe for a player to teleport to. Used by
* warps and boat exits Unsafe is any liquid or air and also if there's no
* space
*
* @param l Location to be checked, not null.
2018-07-20 14:37:11 +02:00
* @return true if safe, otherwise false
*/
public boolean isSafeLocation(@NonNull Location l) {
if (l.getWorld() == null) {
2018-07-20 14:37:11 +02:00
return false;
}
Block ground = l.getBlock().getRelative(BlockFace.DOWN);
Block space1 = l.getBlock();
Block space2 = l.getBlock().getRelative(BlockFace.UP);
// Ground must be solid
if (!ground.getType().isSolid()) {
return false;
}
2018-10-20 10:59:19 +02:00
// Cannot be submerged or water cannot be dangerous
if (space1.isLiquid() && (space2.isLiquid() || plugin.getIWM().isWaterNotSafe(l.getWorld()))) {
2018-07-20 14:37:11 +02:00
return false;
}
// Portals are not "safe"
2018-08-01 10:18:37 +02:00
if (space1.getType() == Material.NETHER_PORTAL || ground.getType() == Material.NETHER_PORTAL || space2.getType() == Material.NETHER_PORTAL
|| space1.getType() == Material.END_PORTAL || ground.getType() == Material.END_PORTAL || space2.getType() == Material.END_PORTAL) {
2018-07-20 14:37:11 +02:00
return false;
}
2018-08-01 10:18:37 +02:00
if (ground.getType().equals(Material.LAVA)
|| space1.getType().equals(Material.LAVA)
|| space2.getType().equals(Material.LAVA)) {
2018-07-20 14:37:11 +02:00
return false;
}
// Check for trapdoors
BlockData bd = ground.getBlockData();
if (bd instanceof Openable) {
return !((Openable)bd).isOpen();
2018-07-20 14:37:11 +02:00
}
if (ground.getType().equals(Material.CACTUS) || ground.getType().toString().contains("BOAT") || ground.getType().toString().contains("FENCE")
|| ground.getType().equals(Material.SIGN) || ground.getType().equals(Material.WALL_SIGN)) {
2018-07-20 14:37:11 +02:00
return false;
}
// Check that the space is not solid
2018-10-20 10:59:19 +02:00
// The isSolid function is not fully accurate (yet) so we have to check a few other items
2018-07-20 14:37:11 +02:00
// isSolid thinks that PLATEs and SIGNS are solid, but they are not
return (!space1.getType().isSolid() || space1.getType().equals(Material.SIGN) || space1.getType().equals(Material.WALL_SIGN)) && (!space2.getType().isSolid() || space2.getType().equals(Material.SIGN) || space2.getType().equals(Material.WALL_SIGN));
2018-07-20 14:37:11 +02:00
}
/**
* Create an island with no owner at location
* @param location the location, not null
* @return Island or null if the island could not be created for some reason
*/
@Nullable
public Island createIsland(Location location){
return createIsland(location, null);
}
/**
2017-07-05 17:41:12 +02:00
* Create an island with owner. Note this does not create the schematic. It just creates the island data object.
* @param location the location, not null
* @param owner the island owner UUID, may be null
* @return Island or null if the island could not be created for some reason
*/
@Nullable
public Island createIsland(@NonNull Location location, @Nullable UUID owner){
Island island = new Island(location, owner, plugin.getIWM().getIslandProtectionRange(location.getWorld()));
while (handler.objectExists(island.getUniqueId())) {
// This should never happen, so although this is a potential infinite loop I'm going to leave it here because
// it will be bad if this does occur and the server should crash.
plugin.logWarning("Duplicate island UUID occurred");
island.setUniqueId(UUID.randomUUID().toString());
}
if (islandCache.addIsland(island)) {
return island;
}
return null;
}
/**
* Deletes island.
* @param island island to delete, not null
* @param removeBlocks whether the island blocks should be removed or not
*/
public void deleteIsland(@NonNull Island island, boolean removeBlocks) {
2019-01-12 19:31:25 +01:00
// Fire event
IslandBaseEvent event = IslandEvent.builder().island(island).reason(Reason.DELETE).build();
if (event.isCancelled()) {
return;
}
// Set the owner of the island to no one.
island.setOwner(null);
island.setFlag(Flags.LOCK, RanksManager.VISITOR_RANK);
if (removeBlocks) {
// Remove island from the cache
islandCache.deleteIslandFromCache(island);
// Remove the island from the database
handler.deleteObject(island);
2018-08-18 19:39:07 +02:00
// Remove players from island
removePlayersFromIsland(island);
// Remove blocks from world
new DeleteIslandChunks(plugin, new IslandDeletion(island));
}
2017-05-20 23:09:53 +02:00
}
public int getIslandCount() {
return islandCache.size();
}
public int getIslandCount(World world) {
return islandCache.size(world);
}
/**
* Gets the island for this player.
* If they are in a team, the team island is returned.
* @param world world to check
* @param user user
2018-02-18 02:01:25 +01:00
* @return Island or null
*/
@Nullable
public Island getIsland(World world, User user){
return islandCache.get(world, user.getUniqueId());
}
/**
* Gets the island for this player. If they are in a team, the team island is returned.
* @param world world to check. Includes nether and end worlds.
* @param uuid user's uuid
* @return Island or null
*/
public Island getIsland(World world, UUID uuid){
return islandCache.get(world, uuid);
}
/**
* Returns the island at the location or Optional empty if there is none.
* This includes the full island space, not just the protected area.
* Use {@link #getProtectedIslandAt(Location)} for only the protected island space.
*
2018-02-18 02:01:25 +01:00
* @param location - the location
2018-04-24 23:22:33 +02:00
* @return Optional Island object
*/
public Optional<Island> getIslandAt(Location location) {
// If this is not an Island World or a standard Nether or End, skip
if (!plugin.getIWM().inWorld(location)
|| (plugin.getIWM().isNether(location.getWorld()) && !plugin.getIWM().isNetherIslands(location.getWorld()))
|| (plugin.getIWM().isEnd(location.getWorld()) && !plugin.getIWM().isEndIslands(location.getWorld()))
) {
return Optional.empty();
}
// Do not return an island if there is no nether or end or islands in them
if ((location.getWorld().getEnvironment().equals(World.Environment.NETHER) &&
(!plugin.getIWM().isNetherGenerate(location.getWorld()) || !plugin.getIWM().isNetherIslands(location.getWorld())))
|| (location.getWorld().getEnvironment().equals(World.Environment.THE_END) &&
(!plugin.getIWM().isEndGenerate(location.getWorld()) || !plugin.getIWM().isEndIslands(location.getWorld())))) {
return Optional.empty();
}
return Optional.ofNullable(islandCache.getIslandAt(location));
}
2019-01-20 08:57:19 +01:00
/**
* Returns an <strong>unmodifiable collection</strong> of all the islands (even those who may be unowned).
* @return unmodifiable collection containing every island.
* @since 1.1
*/
@NonNull
public Collection<Island> getIslands() {
return islandCache.getIslands();
}
/**
* Used for testing only to inject the islandCache mock object
2018-08-05 06:50:10 +02:00
* @param islandCache - island cache
*/
2018-08-04 18:37:21 +02:00
public void setIslandCache(IslandCache islandCache) {
this.islandCache = islandCache;
}
/**
* Returns the player's island location in World
* Returns an island location OR a team island location
*
* @param world - world to check
* @param uuid - the player's UUID
* @return Location of player's island or null if one does not exist
*/
public Location getIslandLocation(World world, UUID uuid) {
Island island = getIsland(world, uuid);
return island != null ? island.getCenter() : null;
}
2017-05-31 16:32:36 +02:00
public Location getLast(World world) {
return last.get(world);
}
/**
* Returns a set of island member UUID's for the island of playerUUID
2018-07-28 22:39:18 +02:00
* This includes the owner of the island. If there is no island, this set will be empty.
*
* @param world - world to check
2018-02-18 02:01:25 +01:00
* @param playerUUID - the player's UUID
* @return Set of team UUIDs
*/
public Set<UUID> getMembers(World world, UUID playerUUID) {
return islandCache.getMembers(world, playerUUID);
}
/**
* Returns the island at the location or Optional empty if there is none.
* This includes only the protected area. Use {@link #getIslandAt(Location)}
* for the full island space.
*
2018-02-18 02:01:25 +01:00
* @param location - the location
* @return Optional Island object
*/
public Optional<Island> getProtectedIslandAt(Location location) {
return getIslandAt(location).filter(i -> i.onIsland(location));
}
/**
* Determines a safe teleport spot on player's island or the team island
* they belong to.
*
* @param world - world to check
* @param user - the player
2018-02-18 02:01:25 +01:00
* @param number - a number - starting home location e.g., 1
* @return Location of a safe teleport spot or null if one cannot be found
*/
public Location getSafeHomeLocation(World world, User user, int number) {
// Try the numbered home location first
Location l = plugin.getPlayers().getHomeLocation(world, user, number);
if (l == null) {
// Get the default home, which may be null too, but that's okay
number = 1;
l = plugin.getPlayers().getHomeLocation(world, user, number);
}
// Check if it is safe
if (l != null) {
if (isSafeLocation(l)) {
return l;
}
// To cover slabs, stairs and other half blocks, try one block above
Location lPlusOne = l.clone();
lPlusOne.add(new Vector(0, 1, 0));
if (isSafeLocation(lPlusOne)) {
// Adjust the home location accordingly
plugin.getPlayers().setHomeLocation(user, lPlusOne, number);
return lPlusOne;
}
}
// Home location either isn't safe, or does not exist so try the island
// location
if (plugin.getIslands().inTeam(world, user.getUniqueId())) {
l = plugin.getIslands().getIslandLocation(world, user.getUniqueId());
if (isSafeLocation(l)) {
plugin.getPlayers().setHomeLocation(user, l, number);
return l;
} else {
// try owner's home
Location tlh = plugin.getPlayers().getHomeLocation(world, plugin.getIslands().getOwner(world, user.getUniqueId()));
2018-06-03 10:23:45 +02:00
if (tlh != null && isSafeLocation(tlh)) {
plugin.getPlayers().setHomeLocation(user, tlh, number);
return tlh;
}
}
} else {
l = plugin.getIslands().getIslandLocation(world, user.getUniqueId());
if (isSafeLocation(l)) {
plugin.getPlayers().setHomeLocation(user, l, number);
return l.clone().add(new Vector(0.5D,0,0.5D));
}
}
if (l == null) {
plugin.logWarning(user.getName() + " player has no island in world " + world.getName() + "!");
return null;
}
// If these island locations are not safe, then we need to get creative
// Try the default location
Location dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 2.5D, 0F, 30F);
if (isSafeLocation(dl)) {
plugin.getPlayers().setHomeLocation(user, dl, number);
return dl;
}
// Try just above the bedrock
dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 0.5D, 0F, 30F);
if (isSafeLocation(dl)) {
plugin.getPlayers().setHomeLocation(user, dl, number);
return dl;
}
// Try all the way up to the sky
for (int y = l.getBlockY(); y < 255; y++) {
final Location n = new Location(l.getWorld(), l.getX() + 0.5D, y, l.getZ() + 0.5D);
if (isSafeLocation(n)) {
plugin.getPlayers().setHomeLocation(user, n, number);
return n;
}
}
// Unsuccessful
return null;
}
/**
* Gets the island that is defined as spawn in this world
* @param world world
* @return optional island, may be empty
*/
@NonNull
public Optional<Island> getSpawn(@NonNull World world){
return Optional.ofNullable(spawn.get(world));
}
/**
* Get the spawn point on the spawn island if it exists
2018-06-10 01:40:38 +02:00
* @param world - world
* @return the spawnPoint or null if spawn does not exist
*/
public Location getSpawnPoint(World world) {
return spawn.containsKey(world) ? spawn.get(world).getSpawnPoint(world.getEnvironment()) : null;
}
/**
* Provides UUID of this player's island owner or null if it does not exist
* @param world world to check
* @param playerUUID the player's UUID
* @return island owner's UUID or null if player has no island
*/
@Nullable
public UUID getOwner(@NonNull World world, @NonNull UUID playerUUID) {
return islandCache.getOwner(world, playerUUID);
}
/**
* Checks if a player has an island in the world
* @param world - world to check
* @param user - the user
* @return true if player has island and owns it
*/
public boolean hasIsland(World world, User user) {
return islandCache.hasIsland(world, user.getUniqueId());
}
/**
* Checks if a player has an island in the world and owns it
* @param world - world to check
* @param uuid - the user's uuid
* @return true if player has island and owns it
*/
public boolean hasIsland(World world, UUID uuid) {
return islandCache.hasIsland(world, uuid);
}
/**
* This teleports player to their island. If not safe place can be found
* then the player is sent to spawn via /spawn command
*
* @param world - world to check
2018-02-18 02:01:25 +01:00
* @param player - the player
*/
public void homeTeleport(World world, Player player) {
2018-06-26 01:40:28 +02:00
homeTeleport(world, player, 1, false);
}
2017-07-05 17:41:12 +02:00
/**
* Teleport player to a home location. If one cannot be found a search is done to
* find a safe place.
*
2018-06-26 01:40:28 +02:00
* @param world - world to check
2018-02-18 02:01:25 +01:00
* @param player - the player
* @param number - a number - home location to do to
2017-07-05 17:41:12 +02:00
*/
public void homeTeleport(World world, Player player, int number) {
2018-06-26 01:40:28 +02:00
homeTeleport(world, player, number, false);
}
/**
* This teleports player to their island. If not safe place can be found
* then the player is sent to spawn via /spawn command
*
* @param world - world to check
* @param player - the player
* @param newIsland - true if this is a new island teleport
*/
public void homeTeleport(World world, Player player, boolean newIsland) {
homeTeleport(world, player, 1, newIsland);
}
/**
* Teleport player to a home location. If one cannot be found a search is done to
* find a safe place.
*
2018-06-26 01:40:28 +02:00
* @param world - world to check
* @param player - the player
* @param number - a number - home location to do to
* @param newIsland - true if this is a new island teleport
*/
public void homeTeleport(World world, Player player, int number, boolean newIsland) {
User user = User.getInstance(player);
Location home = getSafeHomeLocation(world, user, number);
// Stop any gliding
player.setGliding(false);
// Check if the player is a passenger in a boat
if (player.isInsideVehicle()) {
Entity boat = player.getVehicle();
if (boat instanceof Boat) {
player.leaveVehicle();
// Remove the boat so they don't lie around everywhere
boat.remove();
player.getInventory().addItem(new ItemStack(Material.getMaterial(((Boat) boat).getWoodType().toString() + "_BOAT"), 1));
player.updateInventory();
}
}
if (home == null) {
2018-03-12 01:21:15 +01:00
// Try to fix this teleport location and teleport the player if possible
new SafeSpotTeleport.Builder(plugin)
.entity(player)
.island(plugin.getIslands().getIsland(world, user))
.homeNumber(number)
.build();
return;
}
player.teleport(home);
if (number == 1) {
user.sendMessage("commands.island.go.teleport");
} else {
2018-08-06 18:00:47 +02:00
user.sendMessage("commands.island.go.teleported", TextVariables.NUMBER, String.valueOf(number));
}
// Exit spectator mode if in it
if (player.getGameMode().equals(GameMode.SPECTATOR)) {
player.setGameMode(plugin.getIWM().getDefaultGameMode(world));
}
2018-06-26 01:40:28 +02:00
// If this is a new island, then run commands and do resets
if (newIsland) {
// TODO add command running
2018-06-26 01:40:28 +02:00
// Remove money inventory etc.
if (plugin.getIWM().isOnJoinResetEnderChest(world)) {
user.getPlayer().getEnderChest().clear();
}
if (plugin.getIWM().isOnJoinResetInventory(world)) {
user.getPlayer().getInventory().clear();
}
if (plugin.getSettings().isUseEconomy() && plugin.getIWM().isOnJoinResetMoney(world)) {
plugin.getVault().ifPresent(vault -> vault.withdraw(user, vault.getBalance(user)));
2018-06-26 01:40:28 +02:00
}
}
}
/**
* Teleports the player to the spawn location for this world
* @param world world
* @param player player to teleport
* @since 1.1
*/
public void spawnTeleport(@NonNull World world, @NonNull Player player) {
User user = User.getInstance(player);
2019-01-20 18:21:46 +01:00
Optional<Island> spawnIsland = getSpawn(world);
2019-01-20 18:21:46 +01:00
if (!spawnIsland.isPresent()) {
// There is no spawn here.
user.sendMessage("commands.island.spawn.no-spawn");
} else {
// Teleport the player to the spawn
// Stop any gliding
player.setGliding(false);
// Check if the player is a passenger in a boat
if (player.isInsideVehicle()) {
Entity boat = player.getVehicle();
if (boat instanceof Boat) {
player.leaveVehicle();
// Remove the boat so they don't lie around everywhere
boat.remove();
player.getInventory().addItem(new ItemStack(Material.getMaterial(((Boat) boat).getWoodType().toString() + "_BOAT"), 1));
player.updateInventory();
}
}
user.sendMessage("commands.island.spawn.teleporting");
2019-01-20 18:21:46 +01:00
player.teleport(spawnIsland.get().getSpawnPoint(World.Environment.NORMAL));
// If the player is in SPECTATOR gamemode, reset it to default
if (player.getGameMode().equals(GameMode.SPECTATOR)) {
player.setGameMode(plugin.getIWM().getDefaultGameMode(world));
}
}
}
/**
* Indicates whether a player is at an island spawn or not
*
2018-06-10 01:40:38 +02:00
* @param playerLoc - player's location
* @return true if they are, false if they are not, or spawn does not exist
*/
public boolean isAtSpawn(Location playerLoc) {
2018-06-01 03:52:05 +02:00
return spawn.containsKey(playerLoc.getWorld()) && spawn.get(playerLoc.getWorld()).onIsland(playerLoc);
}
/**
* Sets an Island to be the spawn of its World. It will become an unowned Island.
* <br/>
* If there was already a spawn set for this World, it will no longer be the spawn but it will remain unowned.
* @param spawn the Island to set as spawn.
* Must not be null.
*/
public void setSpawn(@NonNull Island spawn) {
// Checking if there is already a spawn set for this world
if (this.spawn.containsKey(spawn.getWorld()) && this.spawn.get(spawn.getWorld()) != null) {
Island oldSpawn = this.spawn.get(spawn.getWorld());
if (oldSpawn.equals(spawn)) {
return; // The spawn is already the current spawn - no need to update anything.
} else {
oldSpawn.setSpawn(false);
}
}
this.spawn.put(spawn.getWorld(), spawn);
spawn.setSpawn(true);
}
2017-07-07 07:00:21 +02:00
/**
2018-02-18 02:01:25 +01:00
* @param uniqueId - unique ID
* @return true if the player is the owner of their island.
2017-07-07 07:00:21 +02:00
*/
public boolean isOwner(@NonNull World world, @NonNull UUID uniqueId) {
return hasIsland(world, uniqueId) && uniqueId.equals(getIsland(world, uniqueId).getOwner());
}
/**
* Clear and reload all islands from database
*/
public void load(){
islandCache.clear();
List<Island> toQuarantine = new ArrayList<>();
// Only load non-quarantined island
// TODO: write a purge admin command to delete these records
handler.loadObjects().stream().filter(i -> !i.isDoNotLoad()).forEach(island -> {
if (!islandCache.addIsland(island)) {
// Quarantine the offending island
toQuarantine.add(island);
} else if (island.isSpawn()) {
this.setSpawn(island);
}
});
if (!toQuarantine.isEmpty()) {
plugin.logError(toQuarantine.size() + " islands could not be loaded successfully; quarantining.");
toQuarantine.forEach(i -> {
i.setDoNotLoad(true);
handler.saveObject(i);
});
}
2017-07-07 07:00:21 +02:00
}
/**
* Checks if a specific location is within the protected range of an island
* that the player is a member of (owner or member)
*
2018-02-18 02:01:25 +01:00
* @param player - the player
* @param loc - location
2017-07-07 07:00:21 +02:00
* @return true if location is on island of player
*/
public boolean locationIsOnIsland(Player player, Location loc) {
2017-07-07 07:00:21 +02:00
if (player == null) {
return false;
}
// Get the player's island
return getIslandAt(loc).filter(i -> i.onIsland(loc)).map(i -> i.getMemberSet().contains(player.getUniqueId())).orElse(false);
2017-07-07 07:00:21 +02:00
}
/**
* Checks if an online player is in the protected area of an island he owns or he is part of.
*
* @param world the World to check. Typically this is the user's world. Does not check nether or end worlds. If null the method will always return {@code false}.
* @param user the User to check, if null or if this is not a Player the method will always return {@code false}.
*
* @return {@code true} if this User is located within the protected area of an island he owns or he is part of,
* {@code false} otherwise or if this User is not located in this World.
2017-07-07 07:00:21 +02:00
*/
public boolean userIsOnIsland(World world, User user) {
if (user == null || !user.isPlayer() || world == null) {
return false;
}
return (user.getLocation().getWorld() == world)
&& getProtectedIslandAt(user.getLocation())
.map(i -> i.getMembers().entrySet().stream()
.anyMatch(en -> en.getKey().equals(user.getUniqueId()) && en.getValue() > RanksManager.VISITOR_RANK))
.orElse(false);
2017-07-07 07:00:21 +02:00
}
/**
* Removes this player from any and all islands in world
* @param world - world
* @param user - user
*/
public void removePlayer(World world, User user) {
removePlayer(world, user.getUniqueId());
}
/**
* Removes this player from any and all islands in world
* @param world - world
* @param uuid - user's uuid
2017-07-07 07:00:21 +02:00
*/
public void removePlayer(World world, UUID uuid) {
Island island = islandCache.removePlayer(world, uuid);
if (island != null) {
handler.saveObject(island);
}
2017-07-07 07:00:21 +02:00
}
/**
* This removes players from an island overworld and nether - used when reseting or deleting an island
* Mobs are killed when the chunks are refreshed.
* @param island to remove players from
2017-07-07 07:00:21 +02:00
*/
public void removePlayersFromIsland(Island island) {
World w = island.getWorld();
Bukkit.getOnlinePlayers().stream()
.filter(p -> p.getGameMode().equals(plugin.getIWM().getDefaultGameMode(island.getWorld())))
.filter(p -> island.onIsland(p.getLocation())).forEach(p -> {
// Teleport island players to their island home
if (!island.getMemberSet().contains(p.getUniqueId()) && (hasIsland(w, p.getUniqueId()) || inTeam(w, p.getUniqueId()))) {
homeTeleport(w, p);
} else {
// Move player to spawn
if (spawn.containsKey(w)) {
// go to island spawn
p.teleport(spawn.get(w).getSpawnPoint(w.getEnvironment()));
} else {
plugin.logWarning("During island deletion player " + p.getName() + " could not be sent home so was placed into spectator mode.");
p.setGameMode(GameMode.SPECTATOR);
p.setFlying(true);
}
}
});
}
/**
* Save the all the islands to the database
*/
public void saveAll(){
Collection<Island> collection = islandCache.getIslands();
for(Island island : collection){
try {
handler.saveObject(island);
} catch (Exception e) {
plugin.logError("Could not save island to database when running sync! " + e.getMessage());
2017-07-07 07:00:21 +02:00
}
}
2017-07-07 07:00:21 +02:00
}
/**
* Puts a player in a team. Removes them from their old island if required.
2018-06-10 01:40:38 +02:00
* @param teamIsland - team island
2018-02-18 02:01:25 +01:00
* @param playerUUID - the player's UUID
*/
2018-06-01 03:52:05 +02:00
public void setJoinTeam(Island teamIsland, UUID playerUUID) {
// Add player to new island
teamIsland.addMember(playerUUID);
islandCache.addPlayer(playerUUID, teamIsland);
// Save the island
handler.saveObject(teamIsland);
}
public void setLast(Location last) {
this.last.put(last.getWorld(), last);
}
/**
* Called when a player leaves a team
* @param world - world
* @param uuid - the player's UUID
*/
public void setLeaveTeam(World world, UUID uuid) {
plugin.getPlayers().clearHomeLocations(world, uuid);
removePlayer(world, uuid);
}
public void shutdown(){
// Remove all coop associations
islandCache.getIslands().stream().forEach(i -> i.getMembers().values().removeIf(p -> p == RanksManager.COOP_RANK));
saveAll();
islandCache.clear();
handler.close();
}
/**
* Checks if a player is in a team
* @param world - world
* @param playerUUID - player's UUID
* @return true if in team, false if not
*/
public boolean inTeam(World world, UUID playerUUID) {
return getMembers(world, playerUUID).size() > 1;
}
/**
* Sets this target as the owner for this island
* @param world world
* @param user the user who is issuing the command
* @param targetUUID the current island member who is going to become the new owner
*/
public void setOwner(World world, User user, UUID targetUUID) {
setOwner(user, targetUUID, getIsland(world, targetUUID));
}
/**
* Sets this target as the owner for this island
* @param user requester
* @param targetUUID new owner
* @param island island to register
*/
public void setOwner(User user, UUID targetUUID, Island island) {
islandCache.setOwner(island, targetUUID);
user.sendMessage("commands.island.team.setowner.name-is-the-owner", "[name]", plugin.getPlayers().getName(targetUUID));
2018-12-11 04:22:07 +01:00
plugin.getIWM().getAddon(island.getWorld()).ifPresent(addon -> {
User target = User.getInstance(targetUUID);
// Tell target. If they are offline, then they may receive a message when they login
target.sendMessage("commands.island.team.setowner.you-are-the-owner");
// Permission checks for range changes only work when the target is online
if (target.isOnline()) {
2018-12-11 04:22:07 +01:00
// Check if new owner has a different range permission than the island size
int range = target.getPermissionValue(
addon.getPermissionPrefix() + "island.range",
plugin.getIWM().getIslandProtectionRange(Util.getWorld(island.getWorld())));
// Range can go up or down
if (range != island.getProtectionRange()) {
user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range));
target.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range));
plugin.log("Setowner: Island protection range changed from " + island.getProtectionRange() + " to "
+ range + " for " + user.getName() + " due to permission.");
}
island.setProtectionRange(range);
}
2018-12-11 04:22:07 +01:00
});
}
/**
* Clear an area of mobs as per world rules. Radius is 5 blocks in every direction.
* @param loc - location to clear
*/
public void clearArea(Location loc) {
loc.getWorld().getNearbyEntities(loc, 5D, 5D, 5D).stream()
.filter(en -> (en instanceof Monster))
.filter(en -> !plugin.getIWM().getRemoveMobsWhitelist(loc.getWorld()).contains(en.getType()))
.forEach(Entity::remove);
}
/**
* Removes a player from any island where they hold the indicated rank.
* Typically this is to remove temporary ranks such as coop.
* Removal is done in all worlds.
* @param rank - rank to clear
* @param uniqueId - UUID of player
*/
public void clearRank(int rank, UUID uniqueId) {
islandCache.getIslands().stream().forEach(i -> i.getMembers().entrySet().removeIf(e -> e.getKey().equals(uniqueId) && e.getValue() == rank));
}
/**
* Save the island to the database
* @param island - island
*/
public void save(Island island) {
handler.saveObject(island);
}
/**
* Try to get an island by its unique id
* @param uniqueId - unique id string
* @return optional island
*/
public Optional<Island> getIslandById(String uniqueId) {
return Optional.ofNullable(islandCache.getIslandById(uniqueId));
}
/**
* Resets all flags to gamemode config.yml default
* @param world - world
* @since 1.3.0
*/
public void resetAllFlags(World world) {
islandCache.resetAllFlags(world);
this.saveAll();
}
2017-05-20 23:09:53 +02:00
}