2018-07-31 18:03:32 +02:00
|
|
|
package world.bentobox.bentobox.managers;
|
2017-05-20 23:09:53 +02:00
|
|
|
|
2020-07-16 03:09:32 +02:00
|
|
|
import java.io.IOException;
|
2020-12-27 19:35:49 +01:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2021-03-01 19:42:08 +01:00
|
|
|
import java.util.Map.Entry;
|
2020-12-27 19:35:49 +01:00
|
|
|
import java.util.Objects;
|
|
|
|
import java.util.Optional;
|
|
|
|
import java.util.Queue;
|
|
|
|
import java.util.Set;
|
|
|
|
import java.util.UUID;
|
2020-06-02 10:22:35 +02:00
|
|
|
import java.util.concurrent.CompletableFuture;
|
2019-04-17 18:35:55 +02:00
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
2018-05-06 07:26:25 +02:00
|
|
|
import org.bukkit.Bukkit;
|
2019-08-28 14:44:55 +02:00
|
|
|
import org.bukkit.ChatColor;
|
2017-05-20 23:09:53 +02:00
|
|
|
import org.bukkit.Location;
|
2017-06-11 01:08:21 +02:00
|
|
|
import org.bukkit.Material;
|
2019-03-13 07:52:05 +01:00
|
|
|
import org.bukkit.TreeSpecies;
|
2018-05-18 06:25:12 +02:00
|
|
|
import org.bukkit.World;
|
2017-06-11 01:08:21 +02:00
|
|
|
import org.bukkit.block.Block;
|
|
|
|
import org.bukkit.block.BlockFace;
|
|
|
|
import org.bukkit.entity.Boat;
|
|
|
|
import org.bukkit.entity.Entity;
|
2021-01-05 07:50:16 +01:00
|
|
|
import org.bukkit.entity.LivingEntity;
|
2017-06-11 01:08:21 +02:00
|
|
|
import org.bukkit.entity.Player;
|
2019-03-10 01:54:54 +01:00
|
|
|
import org.bukkit.entity.PufferFish;
|
2017-06-11 01:08:21 +02:00
|
|
|
import org.bukkit.inventory.ItemStack;
|
2020-03-21 11:29:33 +01:00
|
|
|
import org.bukkit.permissions.PermissionAttachmentInfo;
|
2020-11-27 19:12:10 +01:00
|
|
|
import org.bukkit.scheduler.BukkitRunnable;
|
2017-06-11 01:08:21 +02:00
|
|
|
import org.bukkit.util.Vector;
|
2019-01-18 11:33:29 +01:00
|
|
|
import org.eclipse.jdt.annotation.NonNull;
|
|
|
|
import org.eclipse.jdt.annotation.Nullable;
|
2019-04-17 18:35:55 +02:00
|
|
|
|
|
|
|
import com.google.common.collect.ImmutableMap;
|
|
|
|
|
2019-11-14 00:10:33 +01:00
|
|
|
import io.papermc.lib.PaperLib;
|
2018-07-31 18:03:32 +02:00
|
|
|
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;
|
2019-09-29 23:35:15 +02:00
|
|
|
import world.bentobox.bentobox.api.flags.Flag;
|
2018-08-06 18:00:47 +02:00
|
|
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
2019-03-11 08:41:41 +01:00
|
|
|
import world.bentobox.bentobox.api.logs.LogEntry;
|
2018-07-31 18:03:32 +02:00
|
|
|
import world.bentobox.bentobox.api.user.User;
|
2018-08-06 15:19:06 +02:00
|
|
|
import world.bentobox.bentobox.database.Database;
|
2018-07-31 18:03:32 +02:00
|
|
|
import world.bentobox.bentobox.database.objects.Island;
|
2019-01-31 01:46:51 +01:00
|
|
|
import world.bentobox.bentobox.database.objects.IslandDeletion;
|
2018-07-31 18:03:32 +02:00
|
|
|
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;
|
2018-09-15 12:38:51 +02:00
|
|
|
import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
|
2017-05-20 23:09:53 +02:00
|
|
|
|
2017-05-26 07:54:04 +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
|
2017-05-26 07:54:04 +02:00
|
|
|
* @author tastybento
|
|
|
|
*/
|
2017-05-20 23:09:53 +02:00
|
|
|
public class IslandsManager {
|
|
|
|
|
2018-07-29 22:21:46 +02:00
|
|
|
private BentoBox plugin;
|
2017-11-21 01:05:52 +01:00
|
|
|
|
2019-03-13 07:52:05 +01:00
|
|
|
// Tree species to boat material map
|
2019-03-24 15:47:25 +01:00
|
|
|
private static final Map<TreeSpecies, Material> TREE_TO_BOAT = ImmutableMap.<TreeSpecies, Material>builder().
|
2019-03-13 07:52:05 +01:00
|
|
|
put(TreeSpecies.ACACIA, Material.ACACIA_BOAT).
|
|
|
|
put(TreeSpecies.BIRCH, Material.BIRCH_BOAT).
|
|
|
|
put(TreeSpecies.DARK_OAK, Material.DARK_OAK_BOAT).
|
|
|
|
put(TreeSpecies.JUNGLE, Material.JUNGLE_BOAT).
|
|
|
|
put(TreeSpecies.GENERIC, Material.OAK_BOAT).
|
|
|
|
put(TreeSpecies.REDWOOD, Material.SPRUCE_BOAT).build();
|
|
|
|
|
2017-05-26 07:54:04 +02:00
|
|
|
/**
|
|
|
|
* One island can be spawn, this is the one - otherwise, this value is null
|
|
|
|
*/
|
2019-01-18 11:33:29 +01:00
|
|
|
@NonNull
|
|
|
|
private Map<@NonNull World, @Nullable Island> spawn;
|
2018-04-16 02:30:42 +02:00
|
|
|
|
2019-01-18 11:33:29 +01:00
|
|
|
@NonNull
|
2018-08-06 15:19:06 +02:00
|
|
|
private Database<Island> handler;
|
2017-05-31 16:32:36 +02:00
|
|
|
|
2018-07-06 02:25:21 +02:00
|
|
|
/**
|
|
|
|
* The last locations where an island were put.
|
|
|
|
* This is not stored persistently and resets when the server starts
|
|
|
|
*/
|
2019-01-18 11:33:29 +01:00
|
|
|
private Map<World, Location> last;
|
2017-08-18 16:05:35 +02:00
|
|
|
|
2017-11-21 01:05:52 +01:00
|
|
|
// Island Cache
|
2019-01-18 11:33:29 +01:00
|
|
|
@NonNull
|
2017-11-21 01:05:52 +01:00
|
|
|
private IslandCache islandCache;
|
2019-03-11 08:41:41 +01:00
|
|
|
// Quarantined islands
|
2019-02-17 13:32:05 +01:00
|
|
|
@NonNull
|
|
|
|
private Map<UUID, List<Island>> quarantineCache;
|
2019-03-11 08:41:41 +01:00
|
|
|
// Deleted islands
|
|
|
|
@NonNull
|
|
|
|
private List<String> deletedIslands;
|
2017-11-21 01:05:52 +01:00
|
|
|
|
2020-11-27 19:21:39 +01:00
|
|
|
private boolean isSaveTaskRunning;
|
|
|
|
|
2018-05-06 07:26:25 +02:00
|
|
|
/**
|
|
|
|
* Islands Manager
|
2018-06-10 01:40:38 +02:00
|
|
|
* @param plugin - plugin
|
2018-05-06 07:26:25 +02:00
|
|
|
*/
|
2019-12-21 01:51:21 +01:00
|
|
|
public IslandsManager(@NonNull 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
|
2018-08-06 15:19:06 +02:00
|
|
|
handler = new Database<>(plugin, Island.class);
|
2018-05-18 06:25:12 +02:00
|
|
|
islandCache = new IslandCache();
|
2019-02-17 13:32:05 +01:00
|
|
|
quarantineCache = new HashMap<>();
|
2018-05-18 06:25:12 +02:00
|
|
|
spawn = new HashMap<>();
|
|
|
|
last = new HashMap<>();
|
2019-03-11 08:41:41 +01:00
|
|
|
// This list should always be empty unless database deletion failed
|
|
|
|
// In that case a purge utility may be required in the future
|
|
|
|
deletedIslands = new ArrayList<>();
|
2017-05-20 23:09:53 +02:00
|
|
|
}
|
2017-05-21 08:53:03 +02:00
|
|
|
|
2019-07-06 05:11:06 +02:00
|
|
|
/**
|
|
|
|
* Used only for testing. Sets the database to a mock database.
|
|
|
|
* @param handler - handler
|
|
|
|
*/
|
|
|
|
public void setHandler(Database<Island> handler) {
|
|
|
|
this.handler = handler;
|
|
|
|
}
|
|
|
|
|
2017-08-28 01:17:45 +02:00
|
|
|
/**
|
2017-11-21 01:05:52 +01: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.
|
2017-11-21 01:05:52 +01:00
|
|
|
* @return - safe location, or null if none can be found
|
2017-08-28 01:17:45 +02:00
|
|
|
*/
|
2019-01-18 11:33:29 +01:00
|
|
|
@Nullable
|
|
|
|
public Location bigScan(@NonNull Location l, int i) {
|
2017-11-21 01:05:52 +01:00
|
|
|
final int height;
|
|
|
|
final int depth;
|
|
|
|
if (i > 0) {
|
|
|
|
height = i;
|
|
|
|
depth = i;
|
2017-08-18 16:05:35 +02:00
|
|
|
} else {
|
2018-01-31 01:59:10 +01:00
|
|
|
Optional<Island> island = getIslandAt(l);
|
|
|
|
if (!island.isPresent()) {
|
2017-11-21 01:05:52 +01:00
|
|
|
return null;
|
|
|
|
}
|
2018-01-31 01:59:10 +01:00
|
|
|
i = island.get().getProtectionRange();
|
2017-11-21 01:05:52 +01:00
|
|
|
height = l.getWorld().getMaxHeight() - l.getBlockY();
|
|
|
|
depth = l.getBlockY();
|
2017-05-20 23:09:53 +02:00
|
|
|
}
|
2017-05-21 08:53:03 +02:00
|
|
|
|
2017-11-21 01:05:52 +01: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;
|
2017-05-21 08:53:03 +02:00
|
|
|
|
2017-11-21 01:05:52 +01:00
|
|
|
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
|
|
|
}
|
2017-05-21 08:53:03 +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
|
|
|
|
*
|
2019-01-18 11:33:29 +01:00
|
|
|
* @param l Location to be checked, not null.
|
2018-07-20 14:37:11 +02:00
|
|
|
* @return true if safe, otherwise false
|
|
|
|
*/
|
2019-01-18 11:33:29 +01:00
|
|
|
public boolean isSafeLocation(@NonNull Location l) {
|
2018-07-20 14:37:11 +02:00
|
|
|
Block ground = l.getBlock().getRelative(BlockFace.DOWN);
|
|
|
|
Block space1 = l.getBlock();
|
|
|
|
Block space2 = l.getBlock().getRelative(BlockFace.UP);
|
2020-04-23 03:25:10 +02:00
|
|
|
return checkIfSafe(l.getWorld(), ground.getType(), space1.getType(), space2.getType());
|
|
|
|
}
|
2018-07-20 14:37:11 +02:00
|
|
|
|
2020-06-02 10:22:35 +02:00
|
|
|
/**
|
|
|
|
* Checks if this location is safe for a player to teleport to and loads chunks async to check.
|
|
|
|
*
|
|
|
|
* @param l Location to be checked, not null.
|
|
|
|
* @return a completable future that will be true if safe, otherwise false
|
|
|
|
* @since 1.14.0
|
|
|
|
*/
|
|
|
|
public CompletableFuture<Boolean> isSafeLocationAsync(@NonNull Location l) {
|
|
|
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
|
|
|
Util.getChunkAtAsync(l).thenRun(() -> {
|
|
|
|
Block ground = l.getBlock().getRelative(BlockFace.DOWN);
|
|
|
|
Block space1 = l.getBlock();
|
|
|
|
Block space2 = l.getBlock().getRelative(BlockFace.UP);
|
|
|
|
result.complete(checkIfSafe(l.getWorld(), ground.getType(), space1.getType(), space2.getType()));
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-04-23 03:25:10 +02:00
|
|
|
/**
|
|
|
|
* Check if a location is safe for teleporting
|
|
|
|
* @param world - world
|
2020-06-02 14:22:46 +02:00
|
|
|
* @param ground Material of the block that is going to be the ground
|
|
|
|
* @param space1 Material of the block above the ground
|
|
|
|
* @param space2 Material of the block that is two blocks above the ground
|
|
|
|
* @return {@code true} if the location is considered safe, {@code false} otherwise.
|
2020-04-23 03:25:10 +02:00
|
|
|
*/
|
|
|
|
public boolean checkIfSafe(@Nullable World world, @NonNull Material ground, @NonNull Material space1, @NonNull Material space2) {
|
|
|
|
// Ground must be solid, space 1 and 2 must not be solid
|
|
|
|
if (world == null || !ground.isSolid()
|
|
|
|
|| (space1.isSolid() && !space1.name().contains("SIGN"))
|
|
|
|
|| (space2.isSolid() && !space2.name().contains("SIGN"))) {
|
2018-07-20 14:37:11 +02:00
|
|
|
return false;
|
|
|
|
}
|
2018-10-20 10:59:19 +02:00
|
|
|
// Cannot be submerged or water cannot be dangerous
|
2020-04-23 03:25:10 +02:00
|
|
|
if (space1.equals(Material.WATER) && (space2.equals(Material.WATER) || plugin.getIWM().isWaterNotSafe(world))) {
|
2018-07-20 14:37:11 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-09-07 01:20:48 +02:00
|
|
|
// Unsafe
|
2020-04-23 03:25:10 +02:00
|
|
|
if (ground.equals(Material.LAVA)
|
|
|
|
|| space1.equals(Material.LAVA)
|
2020-09-07 01:20:48 +02:00
|
|
|
|| space2.equals(Material.LAVA)
|
|
|
|
|| ground.name().contains("FENCE")
|
|
|
|
|| ground.name().contains("DOOR")
|
|
|
|
|| ground.name().contains("GATE")
|
|
|
|
|| ground.name().contains("PLATE")
|
|
|
|
|| ground.name().contains("SIGN")
|
|
|
|
|| ground.name().contains("BANNER")
|
|
|
|
|| ground.name().contains("BUTTON")
|
|
|
|
|| ground.name().contains("BOAT")) {
|
2020-09-07 01:16:06 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-04-23 03:25:10 +02:00
|
|
|
// Known unsafe blocks
|
|
|
|
switch (ground) {
|
|
|
|
// Unsafe
|
|
|
|
case ANVIL:
|
|
|
|
case BARRIER:
|
|
|
|
case CACTUS:
|
|
|
|
case END_PORTAL:
|
|
|
|
case END_ROD:
|
|
|
|
case FIRE:
|
|
|
|
case FLOWER_POT:
|
|
|
|
case LADDER:
|
|
|
|
case LEVER:
|
|
|
|
case TALL_GRASS:
|
|
|
|
case PISTON_HEAD:
|
|
|
|
case MOVING_PISTON:
|
|
|
|
case TORCH:
|
|
|
|
case WALL_TORCH:
|
|
|
|
case TRIPWIRE:
|
|
|
|
case WATER:
|
|
|
|
case COBWEB:
|
|
|
|
case NETHER_PORTAL:
|
|
|
|
case MAGMA_BLOCK:
|
2018-07-20 14:37:11 +02:00
|
|
|
return false;
|
2020-04-23 03:25:10 +02:00
|
|
|
default:
|
|
|
|
return true;
|
2018-07-20 14:37:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-21 08:53:03 +02:00
|
|
|
/**
|
|
|
|
* Create an island with no owner at location
|
2019-01-18 11:33:29 +01:00
|
|
|
* @param location the location, not null
|
|
|
|
* @return Island or null if the island could not be created for some reason
|
2017-05-21 08:53:03 +02:00
|
|
|
*/
|
2019-01-18 11:33:29 +01:00
|
|
|
@Nullable
|
2019-12-21 01:51:21 +01:00
|
|
|
public Island createIsland(@NonNull Location location){
|
2017-05-21 08:53:03 +02:00
|
|
|
return createIsland(location, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-05-15 20:16:41 +02:00
|
|
|
* Create an island with owner. Note this does not paste blocks. It just creates the island data object.
|
2019-01-18 11:33:29 +01:00
|
|
|
* @param location the location, not null
|
|
|
|
* @param owner the island owner UUID, may be null
|
2018-04-24 22:18:22 +02:00
|
|
|
* @return Island or null if the island could not be created for some reason
|
2017-05-21 08:53:03 +02:00
|
|
|
*/
|
2019-01-18 11:33:29 +01:00
|
|
|
@Nullable
|
2019-02-17 13:32:05 +01:00
|
|
|
public Island createIsland(@NonNull Location location, @Nullable UUID owner) {
|
2018-05-25 21:19:46 +02:00
|
|
|
Island island = new Island(location, owner, plugin.getIWM().getIslandProtectionRange(location.getWorld()));
|
2019-02-17 13:32:05 +01:00
|
|
|
// Game the gamemode name and prefix the uniqueId
|
|
|
|
String gmName = plugin.getIWM().getAddon(location.getWorld()).map(gm -> gm.getDescription().getName()).orElse("");
|
2019-04-13 14:44:31 +02:00
|
|
|
island.setGameMode(gmName);
|
2019-02-17 13:32:05 +01:00
|
|
|
island.setUniqueId(gmName + island.getUniqueId());
|
2019-01-07 17:03:38 +01:00
|
|
|
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");
|
2019-02-17 13:32:05 +01:00
|
|
|
island.setUniqueId(gmName + UUID.randomUUID().toString());
|
2019-01-07 17:03:38 +01:00
|
|
|
}
|
2018-04-24 22:18:22 +02:00
|
|
|
if (islandCache.addIsland(island)) {
|
|
|
|
return island;
|
|
|
|
}
|
|
|
|
return null;
|
2017-07-07 01:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-01-18 11:33:29 +01:00
|
|
|
* Deletes island.
|
|
|
|
* @param island island to delete, not null
|
|
|
|
* @param removeBlocks whether the island blocks should be removed or not
|
2019-06-27 04:03:56 +02:00
|
|
|
* @param involvedPlayer - player related to the island deletion, if any
|
2017-07-07 01:51:40 +02:00
|
|
|
*/
|
2019-12-21 01:51:21 +01:00
|
|
|
public void deleteIsland(@NonNull Island island, boolean removeBlocks, @Nullable UUID involvedPlayer) {
|
2019-01-12 19:31:25 +01:00
|
|
|
// Fire event
|
2019-06-27 04:03:56 +02:00
|
|
|
IslandBaseEvent event = IslandEvent.builder().island(island).involvedPlayer(involvedPlayer).reason(Reason.DELETE).build();
|
2020-12-27 19:35:49 +01:00
|
|
|
if (event.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(event.isCancelled())) {
|
2019-01-12 19:31:25 +01:00
|
|
|
return;
|
|
|
|
}
|
2017-11-21 01:05:52 +01:00
|
|
|
// Set the owner of the island to no one.
|
|
|
|
island.setOwner(null);
|
2018-04-30 10:12:32 +02:00
|
|
|
island.setFlag(Flags.LOCK, RanksManager.VISITOR_RANK);
|
2017-11-21 01:05:52 +01:00
|
|
|
if (removeBlocks) {
|
|
|
|
// Remove island from the cache
|
|
|
|
islandCache.deleteIslandFromCache(island);
|
2019-03-11 08:41:41 +01:00
|
|
|
// Log the deletion (it shouldn't matter but may be useful)
|
|
|
|
island.log(new LogEntry.Builder("DELETED").build());
|
|
|
|
// Set the delete flag which will prevent it from being loaded even if database deletion fails
|
|
|
|
island.setDeleted(true);
|
|
|
|
// Save the island
|
2020-04-26 01:00:49 +02:00
|
|
|
handler.saveObjectAsync(island);
|
2019-03-11 08:41:41 +01:00
|
|
|
// Delete the island
|
2018-04-16 02:30:42 +02:00
|
|
|
handler.deleteObject(island);
|
2018-08-18 19:39:07 +02:00
|
|
|
// Remove players from island
|
|
|
|
removePlayersFromIsland(island);
|
2017-11-21 01:05:52 +01:00
|
|
|
// Remove blocks from world
|
2019-01-13 10:21:06 +01:00
|
|
|
new DeleteIslandChunks(plugin, new IslandDeletion(island));
|
2017-07-07 01:51:40 +02:00
|
|
|
}
|
2017-05-20 23:09:53 +02:00
|
|
|
}
|
2017-05-21 08:53:03 +02:00
|
|
|
|
2018-12-09 14:19:26 +01:00
|
|
|
public int getIslandCount() {
|
2017-11-21 01:05:52 +01:00
|
|
|
return islandCache.size();
|
2017-05-21 08:53:03 +02:00
|
|
|
}
|
|
|
|
|
2019-12-21 01:51:21 +01:00
|
|
|
public int getIslandCount(@NonNull World world) {
|
2018-12-09 14:19:26 +01:00
|
|
|
return islandCache.size(world);
|
|
|
|
}
|
|
|
|
|
2017-05-21 08:53:03 +02:00
|
|
|
/**
|
2019-01-18 11:33:29 +01:00
|
|
|
* 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
|
2021-02-17 02:15:07 +01:00
|
|
|
* @return Island or null if not found or null user
|
2017-05-21 08:53:03 +02:00
|
|
|
*/
|
2019-01-18 11:33:29 +01:00
|
|
|
@Nullable
|
2021-02-17 02:15:07 +01:00
|
|
|
public Island getIsland(@NonNull World world, @Nullable User user){
|
|
|
|
return user == null || user.getUniqueId() == null ? null : getIsland(world, user.getUniqueId());
|
2017-05-21 08:53:03 +02:00
|
|
|
}
|
2018-05-30 01:59:52 +02:00
|
|
|
|
2017-05-21 08:53:03 +02:00
|
|
|
/**
|
2018-10-27 16:32:06 +02:00
|
|
|
* Gets the island for this player. If they are in a team, the team island is returned.
|
2019-02-11 23:46:54 +01:00
|
|
|
* @param world world to check. Includes nether and end worlds.
|
2019-01-18 11:33:29 +01:00
|
|
|
* @param uuid user's uuid
|
2017-11-21 01:05:52 +01:00
|
|
|
* @return Island or null
|
2017-05-21 08:53:03 +02:00
|
|
|
*/
|
2019-08-28 14:19:42 +02:00
|
|
|
@Nullable
|
2021-03-01 19:42:08 +01:00
|
|
|
public Island getIsland(@NonNull World world, @NonNull UUID uuid) {
|
2018-05-18 06:25:12 +02:00
|
|
|
return islandCache.get(world, uuid);
|
2017-05-21 08:53:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-31 01:59:10 +01:00
|
|
|
* Returns the island at the location or Optional empty if there is none.
|
2018-09-03 05:45:31 +02:00
|
|
|
* This includes the full island space, not just the protected area.
|
2018-10-27 16:32:06 +02:00
|
|
|
* Use {@link #getProtectedIslandAt(Location)} for only the protected island space.
|
2017-08-18 16:05:35 +02:00
|
|
|
*
|
2018-02-18 02:01:25 +01:00
|
|
|
* @param location - the location
|
2018-04-24 23:22:33 +02:00
|
|
|
* @return Optional Island object
|
2017-05-21 08:53:03 +02:00
|
|
|
*/
|
2019-12-21 01:51:21 +01:00
|
|
|
public Optional<Island> getIslandAt(@NonNull Location location) {
|
2019-05-24 05:09:35 +02:00
|
|
|
return plugin.getIWM().inWorld(location) ? Optional.ofNullable(islandCache.getIslandAt(location)) : Optional.empty();
|
2017-05-21 08:53:03 +02:00
|
|
|
}
|
|
|
|
|
2019-01-20 08:57:19 +01:00
|
|
|
/**
|
2019-08-28 14:21:08 +02:00
|
|
|
* Returns an <strong>unmodifiable collection</strong> of all existing islands (even those who may be unowned).
|
2019-01-20 08:57:19 +01:00
|
|
|
* @return unmodifiable collection containing every island.
|
|
|
|
* @since 1.1
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
public Collection<Island> getIslands() {
|
|
|
|
return islandCache.getIslands();
|
|
|
|
}
|
|
|
|
|
2019-08-28 14:21:08 +02:00
|
|
|
/**
|
|
|
|
* Returns an <strong>unmodifiable collection</strong> of all the islands (even those who may be unowned) in the specified world.
|
|
|
|
* @param world World of the gamemode.
|
|
|
|
* @return unmodifiable collection containing all the islands in the specified world.
|
|
|
|
* @since 1.7.0
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
public Collection<Island> getIslands(@NonNull World world) {
|
|
|
|
return islandCache.getIslands(world);
|
|
|
|
}
|
|
|
|
|
2019-04-29 02:57:01 +02:00
|
|
|
/**
|
2019-04-29 14:43:24 +02:00
|
|
|
* Returns the IslandCache instance.
|
2019-04-29 02:57:01 +02:00
|
|
|
* @return the islandCache
|
2019-04-29 14:43:24 +02:00
|
|
|
* @since 1.5.0
|
2019-04-29 02:57:01 +02:00
|
|
|
*/
|
2019-08-28 14:19:42 +02:00
|
|
|
@NonNull
|
2019-04-29 02:57:01 +02:00
|
|
|
public IslandCache getIslandCache() {
|
|
|
|
return islandCache;
|
|
|
|
}
|
|
|
|
|
2018-08-04 19:13:08 +02:00
|
|
|
/**
|
|
|
|
* 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 19:13:08 +02:00
|
|
|
*/
|
2019-08-28 14:21:31 +02:00
|
|
|
public void setIslandCache(@NonNull IslandCache islandCache) {
|
2018-08-04 18:37:21 +02:00
|
|
|
this.islandCache = islandCache;
|
|
|
|
}
|
2018-08-04 19:13:08 +02:00
|
|
|
|
2017-05-21 08:53:03 +02:00
|
|
|
/**
|
2018-05-18 06:25:12 +02:00
|
|
|
* Returns the player's island location in World
|
2017-05-21 08:53:03 +02:00
|
|
|
* Returns an island location OR a team island location
|
2017-08-18 16:05:35 +02:00
|
|
|
*
|
2018-05-18 06:25:12 +02:00
|
|
|
* @param world - world to check
|
|
|
|
* @param uuid - the player's UUID
|
2017-05-21 08:53:03 +02:00
|
|
|
* @return Location of player's island or null if one does not exist
|
|
|
|
*/
|
2019-08-28 14:19:42 +02:00
|
|
|
@Nullable
|
2019-12-21 01:51:21 +01:00
|
|
|
public Location getIslandLocation(@NonNull World world, @NonNull UUID uuid) {
|
2018-10-08 03:15:18 +02:00
|
|
|
Island island = getIsland(world, uuid);
|
|
|
|
return island != null ? island.getCenter() : null;
|
2017-05-21 08:53:03 +02:00
|
|
|
}
|
2017-05-31 16:32:36 +02:00
|
|
|
|
2019-12-21 01:51:21 +01:00
|
|
|
public Location getLast(@NonNull World world) {
|
2018-05-18 06:25:12 +02:00
|
|
|
return last.get(world);
|
2017-05-21 08:53:03 +02:00
|
|
|
}
|
2017-06-11 01:08:21 +02:00
|
|
|
|
|
|
|
/**
|
2019-04-10 15:40:54 +02:00
|
|
|
* Returns a set of island member UUID's for the island of playerUUID of rank <tt>minimumRank</tt>
|
|
|
|
* and above.
|
|
|
|
* This includes the owner of the island. If there is no island, this set will be empty.
|
|
|
|
*
|
|
|
|
* @param world - world to check
|
|
|
|
* @param playerUUID - the player's UUID
|
|
|
|
* @param minimumRank - the minimum rank to be included in the set.
|
|
|
|
* @return Set of team UUIDs
|
|
|
|
*/
|
2019-12-21 01:51:21 +01:00
|
|
|
public Set<UUID> getMembers(@NonNull World world, @NonNull UUID playerUUID, int minimumRank) {
|
2019-04-10 15:40:54 +02:00
|
|
|
return islandCache.getMembers(world, playerUUID, minimumRank);
|
|
|
|
}
|
2019-04-17 18:35:55 +02:00
|
|
|
|
2019-04-10 15:40:54 +02:00
|
|
|
/**
|
|
|
|
* Returns a set of island member UUID's for the island of playerUUID.
|
2019-04-13 14:44:31 +02:00
|
|
|
* Only includes players of rank {@link RanksManager#MEMBER_RANK} and above.
|
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.
|
2017-08-18 16:05:35 +02:00
|
|
|
*
|
2018-05-18 06:25:12 +02:00
|
|
|
* @param world - world to check
|
2018-02-18 02:01:25 +01:00
|
|
|
* @param playerUUID - the player's UUID
|
2017-11-21 01:05:52 +01:00
|
|
|
* @return Set of team UUIDs
|
2017-06-11 01:08:21 +02:00
|
|
|
*/
|
2019-12-21 01:51:21 +01:00
|
|
|
public Set<UUID> getMembers(@NonNull World world, @NonNull UUID playerUUID) {
|
2019-04-10 15:40:54 +02:00
|
|
|
return islandCache.getMembers(world, playerUUID, RanksManager.MEMBER_RANK);
|
2017-06-11 01:08:21 +02:00
|
|
|
}
|
2017-11-21 01:05:52 +01:00
|
|
|
|
2021-02-27 20:19:02 +01:00
|
|
|
/**
|
|
|
|
* Gets the maximum number of island members allowed on this island.
|
|
|
|
* Will update the value based on world settings or island owner permissions (if online).
|
|
|
|
* If the island is unowned, then this value will be 0.
|
|
|
|
* @param island - island
|
|
|
|
* @param rank {@link RanksManager.MEMBER_RANK}, {@link RanksManager.COOP_RANK}, or {@link RanksManager.TRUSTED_RANK}
|
|
|
|
* @return max number of members. If negative, then this means unlimited.
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
public int getMaxMembers(@NonNull Island island, int rank) {
|
|
|
|
if (island.getOwner() == null) {
|
|
|
|
// No owner, no rank settings
|
|
|
|
island.setMaxMembers(null);
|
|
|
|
this.save(island);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// Island max is either the world default or specified amount for this island
|
|
|
|
int worldDefault = plugin.getIWM().getMaxTeamSize(island.getWorld());
|
|
|
|
String perm = "team.maxsize";
|
|
|
|
if (rank == RanksManager.COOP_RANK) {
|
|
|
|
worldDefault = plugin.getIWM().getMaxCoopSize(island.getWorld());
|
|
|
|
perm = "coop.maxsize";
|
|
|
|
} else if (rank == RanksManager.TRUSTED_RANK) {
|
|
|
|
worldDefault = plugin.getIWM().getMaxTrustSize(island.getWorld());
|
|
|
|
perm = "trust.maxsize";
|
|
|
|
}
|
|
|
|
|
2021-02-28 04:18:45 +01:00
|
|
|
int islandMax = island.getMaxMembers(rank) == null ? worldDefault : island.getMaxMembers(rank);
|
2021-02-27 20:19:02 +01:00
|
|
|
// Update based on owner permissions if online
|
|
|
|
if (Bukkit.getPlayer(island.getOwner()) != null) {
|
|
|
|
User owner = User.getInstance(island.getOwner());
|
|
|
|
islandMax = owner.getPermissionValue(plugin.getIWM().getPermissionPrefix(island.getWorld())
|
|
|
|
+ perm, islandMax);
|
|
|
|
}
|
|
|
|
island.setMaxMembers(rank, islandMax == worldDefault ? null : islandMax);
|
|
|
|
this.save(island);
|
|
|
|
return islandMax;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the island max member size.
|
|
|
|
* @param island - island
|
|
|
|
* @param rank {@link RanksManager.MEMBER_RANK}, {@link RanksManager.COOP_RANK}, or {@link RanksManager.TRUSTED_RANK}
|
|
|
|
* @param maxMembers - max number of members. If negative, then this means unlimited. Null means the world
|
|
|
|
* default will be used.
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
public void setMaxMembers(@NonNull Island island, int rank, Integer maxMembers) {
|
|
|
|
island.setMaxMembers(rank, maxMembers);
|
|
|
|
}
|
2021-03-01 19:42:08 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the maximum number of homes allowed on this island. Will be updated with the owner's permission settings if
|
|
|
|
* they exist and the owner is online
|
|
|
|
* @param island - island
|
|
|
|
* @return maximum number of homes
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
public int getMaxHomes(@NonNull Island island) {
|
|
|
|
int islandMax = island.getMaxHomes() == null ? plugin.getIWM().getMaxHomes(island.getWorld()) : island.getMaxHomes();
|
|
|
|
// Update based on owner permissions if online
|
|
|
|
if (Bukkit.getPlayer(island.getOwner()) != null) {
|
|
|
|
User owner = User.getInstance(island.getOwner());
|
|
|
|
islandMax = owner.getPermissionValue(plugin.getIWM().getPermissionPrefix(island.getWorld())
|
|
|
|
+ "island.maxhomes", islandMax);
|
|
|
|
}
|
|
|
|
island.setMaxHomes(islandMax == plugin.getIWM().getMaxHomes(island.getWorld()) ? null : islandMax);
|
|
|
|
this.save(island);
|
|
|
|
return islandMax;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the maximum numbber of homes allowed on this island
|
|
|
|
* @param island - island
|
|
|
|
* @param maxHomes - max number of homes allowed, or null if the world default should be used
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
public void setMaxHomes(@NonNull Island island, @Nullable Integer maxHomes) {
|
|
|
|
island.setMaxHomes(maxHomes);
|
|
|
|
}
|
2021-02-27 20:19:02 +01:00
|
|
|
|
2017-06-11 01:08:21 +02:00
|
|
|
/**
|
2018-09-03 05:45:31 +02:00
|
|
|
* 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.
|
2017-11-21 01:05:52 +01:00
|
|
|
*
|
2018-02-18 02:01:25 +01:00
|
|
|
* @param location - the location
|
2018-01-31 01:59:10 +01:00
|
|
|
* @return Optional Island object
|
2017-06-11 01:08:21 +02:00
|
|
|
*/
|
2019-12-21 01:51:21 +01:00
|
|
|
public Optional<Island> getProtectedIslandAt(@NonNull Location location) {
|
2018-05-06 07:26:25 +02:00
|
|
|
return getIslandAt(location).filter(i -> i.onIsland(location));
|
2017-06-11 01:08:21 +02:00
|
|
|
}
|
2017-06-18 03:46:16 +02:00
|
|
|
|
2020-06-02 10:22:35 +02:00
|
|
|
/**
|
|
|
|
* Get a safe home location using async chunk loading and set the home location
|
|
|
|
* @param world - world
|
|
|
|
* @param user - user
|
2021-03-01 19:42:08 +01:00
|
|
|
* @param name - home name
|
2020-06-02 10:22:35 +02:00
|
|
|
* @return CompletableFuture with the location found, or null
|
|
|
|
* @since 1.14.0
|
|
|
|
*/
|
2021-03-01 19:42:08 +01:00
|
|
|
private CompletableFuture<Location> getAsyncSafeHomeLocation(@NonNull World world, @NonNull User user, String name) {
|
2020-06-02 10:22:35 +02:00
|
|
|
CompletableFuture<Location> result = new CompletableFuture<>();
|
|
|
|
// Check if the world is a gamemode world and the player has an island
|
|
|
|
Location islandLoc = getIslandLocation(world, user.getUniqueId());
|
|
|
|
if (!plugin.getIWM().inWorld(world) || islandLoc == null) {
|
|
|
|
result.complete(null);
|
|
|
|
return result;
|
|
|
|
}
|
2021-03-01 19:42:08 +01:00
|
|
|
// Try the home location first
|
|
|
|
Location defaultHome = getHomeLocation(world, user);
|
|
|
|
Location namedHome = getHomeLocation(world, user, name);
|
|
|
|
Location l = namedHome != null ? namedHome : defaultHome;
|
2020-06-02 10:22:35 +02:00
|
|
|
if (l != null) {
|
|
|
|
Util.getChunkAtAsync(l).thenRun(() -> {
|
|
|
|
// Check if it is safe
|
|
|
|
if (isSafeLocation(l)) {
|
|
|
|
result.complete(l);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// To cover slabs, stairs and other half blocks, try one block above
|
|
|
|
Location lPlusOne = l.clone().add(new Vector(0, 1, 0));
|
|
|
|
if (isSafeLocation(lPlusOne)) {
|
|
|
|
// Adjust the home location accordingly
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, lPlusOne, name);
|
2020-06-02 10:22:35 +02:00
|
|
|
result.complete(lPlusOne);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Try island
|
2021-03-01 19:42:08 +01:00
|
|
|
tryIsland(result, islandLoc, user, name);
|
2020-06-02 10:22:35 +02:00
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
// Try island
|
2021-03-01 19:42:08 +01:00
|
|
|
tryIsland(result, islandLoc, user, name);
|
2020-06-02 10:22:35 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-03-01 19:42:08 +01:00
|
|
|
private void tryIsland(CompletableFuture<Location> result, Location islandLoc, @NonNull User user, String number) {
|
2020-06-02 10:22:35 +02:00
|
|
|
Util.getChunkAtAsync(islandLoc).thenRun(() -> {
|
|
|
|
World w = islandLoc.getWorld();
|
|
|
|
if (isSafeLocation(islandLoc)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, islandLoc, number);
|
2020-06-02 10:22:35 +02:00
|
|
|
result.complete(islandLoc.clone().add(new Vector(0.5D,0,0.5D)));
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
// If these island locations are not safe, then we need to get creative
|
|
|
|
// Try the default location
|
|
|
|
Location dl = islandLoc.clone().add(new Vector(0.5D, 5D, 2.5D));
|
|
|
|
if (isSafeLocation(dl)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, dl, number);
|
2020-06-02 10:22:35 +02:00
|
|
|
result.complete(dl);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Try just above the bedrock
|
|
|
|
dl = islandLoc.clone().add(new Vector(0.5D, 5D, 0.5D));
|
|
|
|
if (isSafeLocation(dl)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, dl, number);
|
2020-06-02 10:22:35 +02:00
|
|
|
result.complete(dl);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Try all the way up to the sky
|
|
|
|
for (int y = islandLoc.getBlockY(); y < w.getMaxHeight(); y++) {
|
|
|
|
dl = new Location(w, islandLoc.getX() + 0.5D, y, islandLoc.getZ() + 0.5D);
|
|
|
|
if (isSafeLocation(dl)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, dl, number);
|
2020-06-02 10:22:35 +02:00
|
|
|
result.complete(dl);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result.complete(null);
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-06-11 01:08:21 +02:00
|
|
|
/**
|
|
|
|
* Determines a safe teleport spot on player's island or the team island
|
|
|
|
* they belong to.
|
2018-06-25 01:22:44 +02:00
|
|
|
*
|
2020-04-25 14:40:47 +02:00
|
|
|
* @param world - world to check, not null
|
|
|
|
* @param user - the player, not null
|
2021-03-01 19:42:08 +01:00
|
|
|
* @param name - named home location. Blank means default.
|
2020-04-25 14:40:47 +02:00
|
|
|
* @return Location of a safe teleport spot or {@code null} if one cannot be found or if the world is not an island world.
|
2017-06-11 01:08:21 +02:00
|
|
|
*/
|
2021-03-01 19:42:08 +01:00
|
|
|
public Location getSafeHomeLocation(@NonNull World world, @NonNull User user, String name) {
|
2020-04-25 14:40:47 +02:00
|
|
|
// Check if the world is a gamemode world
|
|
|
|
if (!plugin.getIWM().inWorld(world)) {
|
|
|
|
return null;
|
|
|
|
}
|
2021-03-01 19:42:08 +01:00
|
|
|
// Try the named home location first
|
|
|
|
Location l = getHomeLocation(world, user, name);
|
2017-06-11 01:08:21 +02:00
|
|
|
if (l == null) {
|
|
|
|
// Get the default home, which may be null too, but that's okay
|
2021-03-01 19:42:08 +01:00
|
|
|
name = "";
|
|
|
|
l = getHomeLocation(world, user, name);
|
2017-06-11 01:08:21 +02:00
|
|
|
}
|
|
|
|
// Check if it is safe
|
|
|
|
if (l != null) {
|
|
|
|
if (isSafeLocation(l)) {
|
2017-10-30 01:48:18 +01:00
|
|
|
return l;
|
2017-06-11 01:08:21 +02:00
|
|
|
}
|
|
|
|
// To cover slabs, stairs and other half blocks, try one block above
|
|
|
|
Location lPlusOne = l.clone();
|
|
|
|
lPlusOne.add(new Vector(0, 1, 0));
|
2018-06-25 01:22:44 +02:00
|
|
|
if (isSafeLocation(lPlusOne)) {
|
|
|
|
// Adjust the home location accordingly
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, lPlusOne, name);
|
2018-06-25 01:22:44 +02:00
|
|
|
return lPlusOne;
|
|
|
|
}
|
2017-06-11 01:08:21 +02:00
|
|
|
}
|
|
|
|
// Home location either isn't safe, or does not exist so try the island
|
|
|
|
// location
|
2021-01-21 01:01:06 +01:00
|
|
|
if (inTeam(world, user.getUniqueId())) {
|
|
|
|
l = getIslandLocation(world, user.getUniqueId());
|
2021-01-21 00:48:22 +01:00
|
|
|
if (l != null && isSafeLocation(l)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, l, name);
|
2017-10-30 01:48:18 +01:00
|
|
|
return l;
|
2017-06-11 01:08:21 +02:00
|
|
|
} else {
|
2018-12-09 11:28:31 +01:00
|
|
|
// try owner's home
|
2021-03-01 19:42:08 +01:00
|
|
|
Location tlh = getHomeLocation(world, getOwner(world, user.getUniqueId()));
|
2018-06-03 10:23:45 +02:00
|
|
|
if (tlh != null && isSafeLocation(tlh)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, tlh, name);
|
2018-06-03 10:23:45 +02:00
|
|
|
return tlh;
|
2017-06-11 01:08:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2021-01-21 01:01:06 +01:00
|
|
|
l = getIslandLocation(world, user.getUniqueId());
|
2021-01-21 00:48:22 +01:00
|
|
|
if (l != null && isSafeLocation(l)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, l, name);
|
2017-06-11 01:08:21 +02:00
|
|
|
return l.clone().add(new Vector(0.5D,0,0.5D));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (l == null) {
|
2018-05-18 06:25:12 +02:00
|
|
|
plugin.logWarning(user.getName() + " player has no island in world " + world.getName() + "!");
|
2017-06-11 01:08:21 +02:00
|
|
|
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)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, dl, name);
|
2017-06-11 01:08:21 +02:00
|
|
|
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)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, dl, name);
|
2017-06-11 01:08:21 +02:00
|
|
|
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)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
setHomeLocation(user, n, name);
|
2017-06-11 01:08:21 +02:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Unsuccessful
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-03-01 19:42:08 +01:00
|
|
|
/**
|
|
|
|
* Sets a home location on user's island. Replaces previous location if the same name is used
|
|
|
|
* @param user - user
|
|
|
|
* @param location - location on island
|
|
|
|
* @param name - name of home, or blank for default home
|
|
|
|
* @return true if home location was set. False if this location is not on the island.
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
public boolean setHomeLocation(@NonNull User user, Location location, String name) {
|
|
|
|
return setHomeLocation(user.getUniqueId(), location, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets a home location on user's island. Replaces previous location if the same name is used
|
|
|
|
* @param uuid - user uuid
|
|
|
|
* @param location - location on island
|
|
|
|
* @param name - name of home, or blank for default home
|
|
|
|
* @return true if home location was set. False if this location is not on the island.
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
public boolean setHomeLocation(@NonNull UUID uuid, Location location, String name) {
|
|
|
|
return setHomeLocation(this.getIsland(location.getWorld(), uuid), location, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set a default home location for user on their island
|
|
|
|
* @param uuid - user uuid
|
|
|
|
* @param location - location on island
|
|
|
|
* @return true if home location was set. False if this location is not on the island.
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
public boolean setHomeLocation(@NonNull UUID uuid, Location location) {
|
|
|
|
return setHomeLocation(uuid, location, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set a home location for island
|
|
|
|
* @param island - island
|
|
|
|
* @param location - location
|
|
|
|
* @param name - name of home, or blank for default home
|
|
|
|
* @return true if home location was set. False if this location is not on the island.
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
public boolean setHomeLocation(@Nullable Island island, Location location, String name) {
|
|
|
|
if (island != null) {
|
|
|
|
island.addHome(name, location);
|
|
|
|
this.save(island);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the home location for user in world
|
|
|
|
* @param world - world
|
|
|
|
* @param user - user
|
|
|
|
* @return home location
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
public Location getHomeLocation(@NonNull World world, @NonNull User user) {
|
|
|
|
return getHomeLocation(world, user, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the home location for player's UUID in world
|
|
|
|
* @param world - world
|
|
|
|
* @param uuid - uuid of player
|
|
|
|
* @return home location
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
public Location getHomeLocation(@NonNull World world, @NonNull UUID uuid) {
|
|
|
|
return getHomeLocation(world, uuid, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the named home location for user in world
|
|
|
|
* @param world - world
|
|
|
|
* @param user - user
|
|
|
|
* @param name - name of home, or blank for default
|
|
|
|
* @return home location
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
public Location getHomeLocation(@NonNull World world, @NonNull User user, String name) {
|
|
|
|
return getHomeLocation(world, user.getUniqueId(), name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the named home location for user in world
|
|
|
|
* @param world - world
|
|
|
|
* @param uuid - uuid of player
|
|
|
|
* @param name - name of home, or blank for default
|
|
|
|
* @return home location
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
public Location getHomeLocation(@NonNull World world, @NonNull UUID uuid, String name) {
|
|
|
|
// Migrate from player homes to island homes
|
|
|
|
Island island = this.getIsland(world, uuid);
|
|
|
|
migrateHomes(world, uuid, name, island);
|
|
|
|
return getHomeLocation(island, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void migrateHomes(@NonNull World world, @NonNull UUID uuid, String name, Island island) {
|
|
|
|
Map<Location, Integer> homes = plugin
|
|
|
|
.getPlayers()
|
|
|
|
.getHomeLocations(world, uuid);
|
|
|
|
if (homes.isEmpty()) {
|
|
|
|
// No migration required
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (island.getOwner().equals(uuid)) {
|
|
|
|
// Owner
|
|
|
|
island.setHomes(homes.entrySet().stream().collect(Collectors.toMap(this::getHomeName, Map.Entry::getKey)));
|
|
|
|
plugin.getPlayers().clearHomeLocations(world, uuid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getHomeName(Entry<Location, Integer> e) {
|
|
|
|
// Home 1 has an empty name
|
|
|
|
if (e.getValue() == 1) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
return String.valueOf(e.getValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the default home location for this island
|
|
|
|
* @param island - island
|
|
|
|
* @return home location
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
public Location getHomeLocation(@Nullable Island island) {
|
|
|
|
return getHomeLocation(island, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the named home location for this island
|
|
|
|
* @param island - island
|
|
|
|
* @param name - name of home, or blank for default
|
|
|
|
* @return home location
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
public Location getHomeLocation(@Nullable Island island, String name) {
|
|
|
|
return island == null ? null : island.getHome(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove the named home location from this island
|
|
|
|
* @param island - island
|
|
|
|
* @param name - name of home, or blank for default
|
|
|
|
* @return true if successful, false if not
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
public boolean removeHomeLocation(@Nullable Island island, String name) {
|
|
|
|
return island == null ? false : island.removeHome(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rename a home
|
|
|
|
* @param island - island
|
|
|
|
* @param oldName - old name
|
|
|
|
* @param newName - new name
|
|
|
|
* @return true if successful, false if not
|
|
|
|
*/
|
|
|
|
public boolean renameHomeLocation(@Nullable Island island, String oldName, String newName) {
|
|
|
|
return island == null ? false : island.renameHome(oldName, newName);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the all the home locations for this island
|
|
|
|
* @param island - island
|
|
|
|
* @return map of home locations with the name as the key
|
|
|
|
* @since 1.16.0
|
|
|
|
*/
|
|
|
|
@NonNull
|
|
|
|
public Map<String, Location> getHomeLocations(@NonNull Island island) {
|
|
|
|
return island.getHomes();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a home name exists or not
|
|
|
|
* @param island - island
|
|
|
|
* @param name - name being checked
|
|
|
|
* @return true if it exists or not
|
|
|
|
*/
|
|
|
|
public boolean isHomeLocation(@NonNull Island island, String name) {
|
|
|
|
return island.getHomes().containsKey(name.toLowerCase());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the number of homes on this island if this home were added
|
|
|
|
* @param island - island
|
|
|
|
* @param name - name
|
|
|
|
* @return number of homes after adding this one
|
|
|
|
*/
|
|
|
|
public int getNumberOfHomesIfAdded(@NonNull Island island, String name) {
|
|
|
|
return isHomeLocation(island, name) ? getHomeLocations(island).size() : getHomeLocations(island).size() + 1;
|
|
|
|
}
|
|
|
|
|
2018-05-18 06:25:12 +02:00
|
|
|
/**
|
2019-01-20 09:22:26 +01:00
|
|
|
* Gets the island that is defined as spawn in this world
|
2019-01-04 21:33:02 +01:00
|
|
|
* @param world world
|
2019-01-20 09:22:26 +01:00
|
|
|
* @return optional island, may be empty
|
2018-05-18 06:25:12 +02:00
|
|
|
*/
|
2019-01-20 09:22:26 +01:00
|
|
|
@NonNull
|
|
|
|
public Optional<Island> getSpawn(@NonNull World world){
|
|
|
|
return Optional.ofNullable(spawn.get(world));
|
2017-11-21 01:05:52 +01:00
|
|
|
}
|
|
|
|
|
2017-06-11 01:08:21 +02:00
|
|
|
/**
|
2018-05-18 06:25:12 +02:00
|
|
|
* Get the spawn point on the spawn island if it exists
|
2018-06-10 01:40:38 +02:00
|
|
|
* @param world - world
|
2017-11-21 01:05:52 +01:00
|
|
|
* @return the spawnPoint or null if spawn does not exist
|
2017-06-11 01:08:21 +02:00
|
|
|
*/
|
2019-12-21 01:51:21 +01:00
|
|
|
public Location getSpawnPoint(@NonNull World world) {
|
2018-07-02 20:21:10 +02:00
|
|
|
return spawn.containsKey(world) ? spawn.get(world).getSpawnPoint(world.getEnvironment()) : null;
|
2017-11-21 01:05:52 +01:00
|
|
|
}
|
2017-06-11 01:08:21 +02:00
|
|
|
|
2018-11-18 11:12:54 +01:00
|
|
|
/**
|
|
|
|
* 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
|
2017-11-21 01:05:52 +01:00
|
|
|
*/
|
2019-01-27 09:31:35 +01:00
|
|
|
@Nullable
|
|
|
|
public UUID getOwner(@NonNull World world, @NonNull UUID playerUUID) {
|
2018-11-18 11:12:54 +01:00
|
|
|
return islandCache.getOwner(world, playerUUID);
|
2017-11-21 01:05:52 +01:00
|
|
|
}
|
2017-06-11 01:08:21 +02:00
|
|
|
|
2017-11-21 01:05:52 +01:00
|
|
|
/**
|
2019-07-22 00:36:14 +02:00
|
|
|
* Checks if a player has an island in the world and owns it
|
2018-05-18 06:25:12 +02:00
|
|
|
* @param world - world to check
|
|
|
|
* @param user - the user
|
2017-11-21 01:05:52 +01:00
|
|
|
* @return true if player has island and owns it
|
|
|
|
*/
|
2019-12-21 01:51:21 +01:00
|
|
|
public boolean hasIsland(@NonNull World world, @NonNull User user) {
|
2018-05-18 06:25:12 +02:00
|
|
|
return islandCache.hasIsland(world, user.getUniqueId());
|
2017-11-21 01:05:52 +01:00
|
|
|
}
|
2018-05-30 01:59:52 +02:00
|
|
|
|
2018-05-18 06:25:12 +02:00
|
|
|
/**
|
2018-10-08 03:15:18 +02:00
|
|
|
* Checks if a player has an island in the world and owns it
|
2018-05-18 06:25:12 +02:00
|
|
|
* @param world - world to check
|
|
|
|
* @param uuid - the user's uuid
|
|
|
|
* @return true if player has island and owns it
|
|
|
|
*/
|
2019-12-21 01:51:21 +01:00
|
|
|
public boolean hasIsland(@NonNull World world, @NonNull UUID uuid) {
|
2018-05-18 06:25:12 +02:00
|
|
|
return islandCache.hasIsland(world, uuid);
|
|
|
|
}
|
2018-05-30 01:59:52 +02:00
|
|
|
|
2017-11-21 01:05:52 +01:00
|
|
|
/**
|
|
|
|
* This teleports player to their island. If not safe place can be found
|
|
|
|
* then the player is sent to spawn via /spawn command
|
|
|
|
*
|
2018-05-18 06:25:12 +02:00
|
|
|
* @param world - world to check
|
2018-02-18 02:01:25 +01:00
|
|
|
* @param player - the player
|
2021-03-01 19:42:08 +01:00
|
|
|
* @return CompletableFuture true if successful, false if not
|
|
|
|
* @since 1.14.0
|
2017-11-21 01:05:52 +01:00
|
|
|
*/
|
2021-03-01 19:42:08 +01:00
|
|
|
public CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player) {
|
|
|
|
return homeTeleportAsync(world, player, "", false);
|
2017-11-21 01:05:52 +01:00
|
|
|
}
|
2017-07-07 01:51:40 +02:00
|
|
|
|
2017-07-05 17:41:12 +02:00
|
|
|
/**
|
2017-11-21 01:05:52 +01:00
|
|
|
* Teleport player to a home location. If one cannot be found a search is done to
|
|
|
|
* find a safe place.
|
2018-07-02 20:21:10 +02:00
|
|
|
*
|
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
|
2020-06-02 10:22:35 +02:00
|
|
|
* @return CompletableFuture true if successful, false if not
|
|
|
|
* @since 1.14.0
|
2021-03-01 19:42:08 +01:00
|
|
|
* @deprecated Use {@link #homeTeleportAsync(World, Player, String)}
|
2020-06-02 10:22:35 +02:00
|
|
|
*/
|
2021-03-01 19:42:08 +01:00
|
|
|
@Deprecated
|
|
|
|
public CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player, int number) {
|
|
|
|
return homeTeleportAsync(world, player, String.valueOf(number), false);
|
2020-06-02 10:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Teleport player to a home location. If one cannot be found a search is done to
|
|
|
|
* find a safe place.
|
|
|
|
*
|
|
|
|
* @param world - world to check
|
|
|
|
* @param player - the player
|
2021-03-01 19:42:08 +01:00
|
|
|
* @param name - a named home location. Blank means default.
|
2020-06-02 10:22:35 +02:00
|
|
|
* @return CompletableFuture true if successful, false if not
|
2021-03-01 19:42:08 +01:00
|
|
|
* @since 1.16.0
|
2020-06-02 10:22:35 +02:00
|
|
|
*/
|
2021-03-01 19:42:08 +01:00
|
|
|
public CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player, String name) {
|
|
|
|
return homeTeleportAsync(world, player, name, false);
|
2020-06-02 10:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-03-01 19:42:08 +01:00
|
|
|
* This teleports player to their island. If no safe place can be found
|
2020-06-02 10:22:35 +02:00
|
|
|
* 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
|
|
|
|
* @return CompletableFuture true if successful, false if not
|
|
|
|
* @since 1.14.0
|
|
|
|
*/
|
|
|
|
public CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player, boolean newIsland) {
|
2021-03-01 19:42:08 +01:00
|
|
|
return homeTeleportAsync(world, player, "", newIsland);
|
2020-06-02 10:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-01 19:42:08 +01:00
|
|
|
private CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player, String name, boolean newIsland) {
|
2020-06-02 10:22:35 +02:00
|
|
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
|
|
|
User user = User.getInstance(player);
|
|
|
|
user.sendMessage("commands.island.go.teleport");
|
|
|
|
// 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(TREE_TO_BOAT.getOrDefault(((Boat) boat).getWoodType(), Material.OAK_BOAT)));
|
|
|
|
player.updateInventory();
|
|
|
|
}
|
|
|
|
}
|
2021-03-01 19:42:08 +01:00
|
|
|
this.getAsyncSafeHomeLocation(world, user, name).thenAccept(home -> {
|
|
|
|
Island island = getIsland(world, user);
|
2020-06-02 10:22:35 +02:00
|
|
|
if (home == null) {
|
|
|
|
// Try to fix this teleport location and teleport the player if possible
|
|
|
|
new SafeSpotTeleport.Builder(plugin)
|
|
|
|
.entity(player)
|
2021-03-01 19:42:08 +01:00
|
|
|
.island(island)
|
|
|
|
.homeName(name)
|
|
|
|
.thenRun(() -> teleported(world, user, name, newIsland))
|
2020-06-02 10:22:35 +02:00
|
|
|
.buildFuture()
|
2020-06-02 14:22:46 +02:00
|
|
|
.thenAccept(result::complete);
|
2020-06-02 10:22:35 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Add home
|
2021-03-01 19:42:08 +01:00
|
|
|
if (getHomeLocations(island).isEmpty()) {
|
|
|
|
setHomeLocation(player.getUniqueId(), home);
|
2020-06-02 10:22:35 +02:00
|
|
|
}
|
|
|
|
PaperLib.teleportAsync(player, home).thenAccept(b -> {
|
|
|
|
// Only run the commands if the player is successfully teleported
|
2020-06-02 14:22:46 +02:00
|
|
|
if (Boolean.TRUE.equals(b)) {
|
2021-03-01 19:42:08 +01:00
|
|
|
teleported(world, user, name, newIsland);
|
2020-06-02 10:22:35 +02:00
|
|
|
result.complete(true);
|
|
|
|
} else {
|
|
|
|
result.complete(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-03-01 19:42:08 +01:00
|
|
|
private void teleported(World world, User user, String name, boolean newIsland) {
|
|
|
|
if (!name.isEmpty()) {
|
|
|
|
user.sendMessage("commands.island.go.teleported", TextVariables.NUMBER, name);
|
2017-06-11 01:08:21 +02:00
|
|
|
}
|
2018-06-26 01:40:28 +02:00
|
|
|
// If this is a new island, then run commands and do resets
|
|
|
|
if (newIsland) {
|
2019-09-28 13:48:24 +02:00
|
|
|
// Execute commands
|
2020-06-14 03:51:05 +02:00
|
|
|
Util.runCommands(user, plugin.getIWM().getOnJoinCommands(world), "join");
|
2018-07-02 20:21:10 +02:00
|
|
|
|
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)) {
|
2018-10-31 15:35:40 +01:00
|
|
|
plugin.getVault().ifPresent(vault -> vault.withdraw(user, vault.getBalance(user)));
|
2018-06-26 01:40:28 +02:00
|
|
|
}
|
2019-10-13 10:29:32 +02:00
|
|
|
|
|
|
|
// Reset the health
|
|
|
|
if (plugin.getIWM().isOnJoinResetHealth(world)) {
|
2020-10-11 18:18:53 +02:00
|
|
|
Util.resetHealth(user.getPlayer());
|
2019-10-13 10:29:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the hunger
|
|
|
|
if (plugin.getIWM().isOnJoinResetHunger(world)) {
|
|
|
|
user.getPlayer().setFoodLevel(20);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the XP
|
|
|
|
if (plugin.getIWM().isOnJoinResetXP(world)) {
|
|
|
|
user.getPlayer().setTotalExperience(0);
|
|
|
|
}
|
2018-06-26 01:40:28 +02:00
|
|
|
}
|
2017-06-11 01:08:21 +02:00
|
|
|
}
|
2017-07-07 01:51:40 +02:00
|
|
|
|
2019-01-20 09:22:26 +01: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-02-21 19:46:29 +01:00
|
|
|
// If there's no spawn island or the spawn location is null for some reason, then error
|
|
|
|
Location spawnTo = getSpawn(world).map(i -> i.getSpawnPoint(World.Environment.NORMAL) == null ? i.getCenter() : i.getSpawnPoint(World.Environment.NORMAL))
|
|
|
|
.orElse(null);
|
|
|
|
if (spawnTo == null) {
|
2019-01-20 09:22:26 +01:00
|
|
|
// 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();
|
2019-07-11 22:55:17 +02:00
|
|
|
Material boatMat = Material.getMaterial(((Boat) boat).getWoodType().toString() + "_BOAT");
|
|
|
|
if (boatMat == null) {
|
|
|
|
boatMat = Material.OAK_BOAT;
|
|
|
|
}
|
|
|
|
player.getInventory().addItem(new ItemStack(boatMat, 1));
|
2019-01-20 09:22:26 +01:00
|
|
|
player.updateInventory();
|
|
|
|
}
|
|
|
|
}
|
2019-01-20 09:33:28 +01:00
|
|
|
|
2019-01-20 09:22:26 +01:00
|
|
|
user.sendMessage("commands.island.spawn.teleporting");
|
2019-02-21 19:46:29 +01:00
|
|
|
// Safe teleport
|
|
|
|
new SafeSpotTeleport.Builder(plugin).entity(player).location(spawnTo).build();
|
2019-01-20 09:22:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-18 03:46:16 +02:00
|
|
|
/**
|
2018-05-18 06:25:12 +02:00
|
|
|
* Indicates whether a player is at an island spawn or not
|
2017-11-21 01:05:52 +01:00
|
|
|
*
|
2018-06-10 01:40:38 +02:00
|
|
|
* @param playerLoc - player's location
|
2017-11-21 01:05:52 +01:00
|
|
|
* @return true if they are, false if they are not, or spawn does not exist
|
2017-06-18 03:46:16 +02:00
|
|
|
*/
|
2017-11-21 01:05:52 +01:00
|
|
|
public boolean isAtSpawn(Location playerLoc) {
|
2018-06-01 03:52:05 +02:00
|
|
|
return spawn.containsKey(playerLoc.getWorld()) && spawn.get(playerLoc.getWorld()).onIsland(playerLoc);
|
2017-06-18 03:46:16 +02:00
|
|
|
}
|
2017-06-11 01:08:21 +02:00
|
|
|
|
2018-05-06 07:26:25 +02:00
|
|
|
/**
|
2019-01-04 21:33:02 +01:00
|
|
|
* 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.
|
2018-05-06 07:26:25 +02:00
|
|
|
*/
|
2019-01-20 09:22:26 +01:00
|
|
|
public void setSpawn(@NonNull Island spawn) {
|
2019-01-04 21:33:02 +01:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
2018-05-18 06:25:12 +02:00
|
|
|
this.spawn.put(spawn.getWorld(), spawn);
|
2019-01-04 21:33:02 +01:00
|
|
|
spawn.setSpawn(true);
|
2018-05-06 07:26:25 +02:00
|
|
|
}
|
2019-11-14 00:10:33 +01:00
|
|
|
|
2019-10-19 02:32:54 +02:00
|
|
|
/**
|
|
|
|
* Clears the spawn island for this world
|
|
|
|
* @param world - world
|
|
|
|
* @since 1.8.0
|
|
|
|
*/
|
|
|
|
public void clearSpawn(World world) {
|
|
|
|
Island spawnIsland = spawn.get(Util.getWorld(world));
|
|
|
|
if (spawnIsland != null) {
|
|
|
|
spawnIsland.setSpawn(false);
|
|
|
|
}
|
|
|
|
this.spawn.remove(world);
|
|
|
|
}
|
2018-05-06 07:26:25 +02:00
|
|
|
|
2017-07-07 07:00:21 +02:00
|
|
|
/**
|
2018-02-18 02:01:25 +01:00
|
|
|
* @param uniqueId - unique ID
|
2018-10-27 16:32:06 +02:00
|
|
|
* @return true if the player is the owner of their island.
|
2017-07-07 07:00:21 +02:00
|
|
|
*/
|
2019-01-27 09:31:35 +01:00
|
|
|
public boolean isOwner(@NonNull World world, @NonNull UUID uniqueId) {
|
|
|
|
return hasIsland(world, uniqueId) && uniqueId.equals(getIsland(world, uniqueId).getOwner());
|
2017-11-21 01:05:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clear and reload all islands from database
|
2020-07-16 03:09:32 +02:00
|
|
|
* @throws IOException - if a loaded island distance does not match the expected distance in config.yml
|
2017-11-21 01:05:52 +01:00
|
|
|
*/
|
2020-07-16 03:09:32 +02:00
|
|
|
public void load() throws IOException {
|
2017-11-21 01:05:52 +01:00
|
|
|
islandCache.clear();
|
2019-02-17 13:32:05 +01:00
|
|
|
quarantineCache.clear();
|
2019-01-31 01:46:51 +01:00
|
|
|
List<Island> toQuarantine = new ArrayList<>();
|
2019-06-20 05:44:59 +02:00
|
|
|
int owned = 0;
|
|
|
|
int unowned = 0;
|
2019-03-11 08:41:41 +01:00
|
|
|
// Attempt to load islands
|
2019-06-20 05:44:59 +02:00
|
|
|
for (Island island : handler.loadObjects()) {
|
|
|
|
if (island == null) {
|
|
|
|
plugin.logWarning("Null island when loading...");
|
|
|
|
continue;
|
|
|
|
}
|
2020-07-16 03:09:32 +02:00
|
|
|
|
2019-03-11 08:41:41 +01:00
|
|
|
if (island.isDeleted()) {
|
|
|
|
// These will be deleted later
|
|
|
|
deletedIslands.add(island.getUniqueId());
|
|
|
|
} else if (island.isDoNotLoad() && island.getWorld() != null && island.getCenter() != null) {
|
2019-02-17 13:32:05 +01:00
|
|
|
// Add to quarantine cache
|
|
|
|
quarantineCache.computeIfAbsent(island.getOwner(), k -> new ArrayList<>()).add(island);
|
2020-07-16 03:09:32 +02:00
|
|
|
} // Check island distance and if incorrect stop BentoBox
|
2020-09-07 00:31:30 +02:00
|
|
|
else if (island.getWorld() != null
|
|
|
|
&& plugin.getIWM().inWorld(island.getWorld())
|
|
|
|
&& island.getRange() != plugin.getIWM().getIslandDistance(island.getWorld())) {
|
2020-07-16 03:09:32 +02:00
|
|
|
throw new IOException("Island distance mismatch!\n"
|
|
|
|
+ "World '" + island.getWorld().getName() + "' distance " + plugin.getIWM().getIslandDistance(island.getWorld()) + " != island range " + island.getRange() + "!\n"
|
|
|
|
+ "Island ID in database is " + island.getUniqueId() + ".\n"
|
|
|
|
+ "Island distance in config.yml cannot be changed mid-game! Fix config.yml or clean database.");
|
2019-02-17 13:32:05 +01:00
|
|
|
} else {
|
2019-02-21 06:42:43 +01:00
|
|
|
// Fix island center if it is off
|
|
|
|
fixIslandCenter(island);
|
2019-02-17 13:32:05 +01:00
|
|
|
if (!islandCache.addIsland(island)) {
|
|
|
|
// Quarantine the offending island
|
|
|
|
toQuarantine.add(island);
|
|
|
|
// Add to quarantine cache
|
|
|
|
island.setDoNotLoad(true);
|
|
|
|
quarantineCache.computeIfAbsent(island.getOwner(), k -> new ArrayList<>()).add(island);
|
2019-12-07 13:51:38 +01:00
|
|
|
if (island.isUnowned()) {
|
2019-06-20 05:44:59 +02:00
|
|
|
unowned++;
|
|
|
|
} else {
|
|
|
|
owned++;
|
|
|
|
}
|
2019-02-17 13:32:05 +01:00
|
|
|
} else if (island.isSpawn()) {
|
|
|
|
// Success, set spawn if this is the spawn island.
|
|
|
|
this.setSpawn(island);
|
2019-05-21 09:15:58 +02:00
|
|
|
} else {
|
|
|
|
// Successful load
|
|
|
|
// Clean any null flags out of the island - these can occur for various reasons
|
|
|
|
island.getFlags().keySet().removeIf(f -> f.getID().startsWith("NULL_FLAG"));
|
2019-02-17 13:32:05 +01:00
|
|
|
}
|
2019-02-11 07:00:50 +01:00
|
|
|
}
|
2019-04-13 14:44:31 +02:00
|
|
|
|
|
|
|
// Update some of their fields
|
|
|
|
if (island.getGameMode() == null) {
|
|
|
|
island.setGameMode(plugin.getIWM().getAddon(island.getWorld()).map(gm -> gm.getDescription().getName()).orElse(""));
|
|
|
|
}
|
2019-06-20 05:44:59 +02:00
|
|
|
}
|
2019-01-31 01:46:51 +01:00
|
|
|
if (!toQuarantine.isEmpty()) {
|
2019-02-17 13:32:05 +01:00
|
|
|
plugin.logError(toQuarantine.size() + " islands could not be loaded successfully; moving to trash bin.");
|
2019-06-20 05:44:59 +02:00
|
|
|
plugin.logError(unowned + " are unowned, " + owned + " are owned.");
|
|
|
|
|
2020-04-26 01:00:49 +02:00
|
|
|
toQuarantine.forEach(handler::saveObjectAsync);
|
2019-06-20 05:44:59 +02:00
|
|
|
// Check if there are any islands with duplicate islands
|
|
|
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
|
|
|
Set<UUID> duplicatedUUIDRemovedSet = new HashSet<>();
|
|
|
|
Set<UUID> duplicated = islandCache.getIslands().stream()
|
|
|
|
.map(Island::getOwner)
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
.filter(n -> !duplicatedUUIDRemovedSet.add(n))
|
|
|
|
.collect(Collectors.toSet());
|
2019-07-05 01:54:05 +02:00
|
|
|
if (!duplicated.isEmpty()) {
|
2019-06-20 05:44:59 +02:00
|
|
|
plugin.logError("**** Owners that have more than one island = " + duplicated.size());
|
|
|
|
for (UUID uuid : duplicated) {
|
|
|
|
Set<Island> set = islandCache.getIslands().stream().filter(i -> uuid.equals(i.getOwner())).collect(Collectors.toSet());
|
|
|
|
plugin.logError(plugin.getPlayers().getName(uuid) + "(" + uuid.toString() + ") has " + set.size() + " islands:");
|
|
|
|
set.forEach(i -> {
|
|
|
|
plugin.logError("Island at " + i.getCenter());
|
|
|
|
plugin.logError("Island unique ID = " + i.getUniqueId());
|
|
|
|
});
|
|
|
|
plugin.logError("You should find out which island is real and delete the uniqueID from the database for the bogus one.");
|
|
|
|
plugin.logError("");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2019-01-31 01:46:51 +01:00
|
|
|
}
|
2017-07-07 07:00:21 +02:00
|
|
|
}
|
|
|
|
|
2019-02-21 06:42:43 +01:00
|
|
|
/**
|
|
|
|
* Island coordinates should always be a multiple of the island distance x 2. If they are not, this method
|
|
|
|
* realigns the grid coordinates.
|
|
|
|
* @param island - island
|
2020-02-04 21:20:31 +01:00
|
|
|
* @return true if coordinate is altered
|
2019-02-21 10:00:59 +01:00
|
|
|
* @since 1.3.0
|
2019-02-21 06:42:43 +01:00
|
|
|
*/
|
2020-02-04 21:20:31 +01:00
|
|
|
boolean fixIslandCenter(Island island) {
|
2019-02-21 06:42:43 +01:00
|
|
|
World world = island.getWorld();
|
|
|
|
if (world == null || island.getCenter() == null || !plugin.getIWM().inWorld(world)) {
|
2020-02-04 21:20:31 +01:00
|
|
|
return false;
|
2019-02-21 06:42:43 +01:00
|
|
|
}
|
2019-05-03 06:23:46 +02:00
|
|
|
int distance = plugin.getIWM().getIslandDistance(island.getWorld()) * 2;
|
2020-02-04 21:20:31 +01:00
|
|
|
long x = ((long) island.getCenter().getBlockX()) - plugin.getIWM().getIslandXOffset(world) - plugin.getIWM().getIslandStartX(world);
|
|
|
|
long z = ((long) island.getCenter().getBlockZ()) - plugin.getIWM().getIslandZOffset(world) - plugin.getIWM().getIslandStartZ(world);
|
2019-02-21 06:42:43 +01:00
|
|
|
if (x % distance != 0 || z % distance != 0) {
|
|
|
|
// Island is off grid
|
2020-02-04 21:20:31 +01:00
|
|
|
x = Math.round((double) x / distance) * distance + plugin.getIWM().getIslandXOffset(world) + plugin.getIWM().getIslandStartX(world);
|
|
|
|
z = Math.round((double) z / distance) * distance + plugin.getIWM().getIslandZOffset(world) + plugin.getIWM().getIslandStartZ(world);
|
2020-01-08 00:30:21 +01:00
|
|
|
island.setCenter(new Location(world, x, island.getCenter().getBlockY(), z));
|
2020-02-04 21:20:31 +01:00
|
|
|
return true;
|
2019-02-21 06:42:43 +01:00
|
|
|
}
|
2020-02-04 21:20:31 +01:00
|
|
|
return false;
|
2019-02-21 06:42:43 +01:00
|
|
|
}
|
|
|
|
|
2017-07-07 07:00:21 +02:00
|
|
|
/**
|
|
|
|
* Checks if a specific location is within the protected range of an island
|
2018-06-10 08:06:59 +02:00
|
|
|
* that the player is a member of (owner or member)
|
2017-08-18 16:05:35 +02:00
|
|
|
*
|
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
|
|
|
|
*/
|
2018-05-18 06:25:12 +02:00
|
|
|
public boolean locationIsOnIsland(Player player, Location loc) {
|
2017-07-07 07:00:21 +02:00
|
|
|
if (player == null) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-05-06 07:26:25 +02:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-01-11 01:50:24 +01:00
|
|
|
* Checks if an online player is in the protected area of an island he owns or he is part of. i.e. rank is greater than VISITOR_RANK
|
2017-08-18 16:05:35 +02:00
|
|
|
*
|
2019-02-12 04:01:44 +01:00
|
|
|
* @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}.
|
2018-11-01 16:34:19 +01:00
|
|
|
* @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
|
|
|
*/
|
2018-05-18 06:25:12 +02:00
|
|
|
public boolean userIsOnIsland(World world, User user) {
|
2018-11-01 16:34:19 +01:00
|
|
|
if (user == null || !user.isPlayer() || world == null) {
|
2018-05-06 07:26:25 +02:00
|
|
|
return false;
|
|
|
|
}
|
2018-11-01 16:34:19 +01:00
|
|
|
return (user.getLocation().getWorld() == world)
|
2018-11-10 19:22:05 +01:00
|
|
|
&& getProtectedIslandAt(user.getLocation())
|
2018-09-03 05:45:31 +02:00
|
|
|
.map(i -> i.getMembers().entrySet().stream()
|
2018-10-30 21:21:27 +01:00
|
|
|
.anyMatch(en -> en.getKey().equals(user.getUniqueId()) && en.getValue() > RanksManager.VISITOR_RANK))
|
2018-09-03 05:45:31 +02:00
|
|
|
.orElse(false);
|
2017-07-07 07:00:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-05-18 06:25:12 +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) {
|
2019-01-13 02:12:30 +01:00
|
|
|
removePlayer(world, user.getUniqueId());
|
2018-05-18 06:25:12 +02:00
|
|
|
}
|
2018-05-30 01:59:52 +02:00
|
|
|
|
2018-05-18 06:25:12 +02:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
*/
|
2018-05-18 06:25:12 +02:00
|
|
|
public void removePlayer(World world, UUID uuid) {
|
2019-01-13 02:12:30 +01:00
|
|
|
Island island = islandCache.removePlayer(world, uuid);
|
|
|
|
if (island != null) {
|
2020-04-26 01:00:49 +02:00
|
|
|
handler.saveObjectAsync(island);
|
2019-01-13 02:12:30 +01:00
|
|
|
}
|
2017-07-07 07:00:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-03-11 08:41:41 +01:00
|
|
|
* This teleports players away from an island - used when reseting or deleting an island
|
2017-11-21 01:05:52 +01:00
|
|
|
* @param island to remove players from
|
2017-07-07 07:00:21 +02:00
|
|
|
*/
|
2018-05-18 06:25:12 +02:00
|
|
|
public void removePlayersFromIsland(Island island) {
|
2018-12-31 03:09:56 +01:00
|
|
|
World w = island.getWorld();
|
2019-01-02 19:55:11 +01:00
|
|
|
Bukkit.getOnlinePlayers().stream()
|
|
|
|
.filter(p -> p.getGameMode().equals(plugin.getIWM().getDefaultGameMode(island.getWorld())))
|
|
|
|
.filter(p -> island.onIsland(p.getLocation())).forEach(p -> {
|
2018-12-31 03:09:56 +01:00
|
|
|
// Teleport island players to their island home
|
|
|
|
if (!island.getMemberSet().contains(p.getUniqueId()) && (hasIsland(w, p.getUniqueId()) || inTeam(w, p.getUniqueId()))) {
|
2020-06-02 10:22:35 +02:00
|
|
|
homeTeleportAsync(w, p);
|
2018-12-31 03:09:56 +01:00
|
|
|
} else {
|
|
|
|
// Move player to spawn
|
|
|
|
if (spawn.containsKey(w)) {
|
|
|
|
// go to island spawn
|
2019-11-14 00:10:33 +01:00
|
|
|
PaperLib.teleportAsync(p, spawn.get(w).getSpawnPoint(w.getEnvironment()));
|
2017-11-21 01:05:52 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-31 03:09:56 +01:00
|
|
|
});
|
2017-11-21 01:05:52 +01:00
|
|
|
}
|
|
|
|
|
2020-11-27 19:21:39 +01:00
|
|
|
public boolean isSaveTaskRunning() {
|
|
|
|
return isSaveTaskRunning;
|
|
|
|
}
|
|
|
|
|
2017-11-21 01:05:52 +01:00
|
|
|
/**
|
2019-01-13 02:12:30 +01:00
|
|
|
* Save the all the islands to the database
|
2017-11-21 01:05:52 +01:00
|
|
|
*/
|
2020-11-27 19:12:10 +01:00
|
|
|
public void saveAll() {
|
|
|
|
saveAll(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save the all the islands to the database
|
|
|
|
* @param schedule true if we should let the task run over multiple ticks to reduce lag spikes
|
|
|
|
*/
|
|
|
|
public void saveAll(boolean schedule){
|
|
|
|
if (!schedule) {
|
2021-01-09 05:55:43 +01:00
|
|
|
for(Island island : islandCache.getIslands()) {
|
|
|
|
if (island.isChanged()) {
|
|
|
|
try {
|
|
|
|
handler.saveObjectAsync(island);
|
|
|
|
} catch (Exception e) {
|
|
|
|
plugin.logError("Could not save island to database when running sync! " + e.getMessage());
|
|
|
|
}
|
2020-11-27 19:12:10 +01:00
|
|
|
}
|
2017-07-07 07:00:21 +02:00
|
|
|
}
|
2020-11-27 19:12:10 +01:00
|
|
|
return;
|
2017-07-07 07:00:21 +02:00
|
|
|
}
|
2020-11-27 19:12:10 +01:00
|
|
|
|
2020-11-27 19:21:39 +01:00
|
|
|
|
|
|
|
isSaveTaskRunning = true;
|
2020-11-27 19:12:10 +01:00
|
|
|
Queue<Island> queue = new LinkedList<>(islandCache.getIslands());
|
|
|
|
new BukkitRunnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
for (int i = 0; i < plugin.getSettings().getMaxSavedIslandsPerTick(); i++) {
|
|
|
|
Island island = queue.poll();
|
|
|
|
if (island == null) {
|
2020-11-27 19:21:39 +01:00
|
|
|
isSaveTaskRunning = false;
|
2020-11-27 19:12:10 +01:00
|
|
|
cancel();
|
|
|
|
return;
|
|
|
|
}
|
2021-01-09 05:55:43 +01:00
|
|
|
if (island.isChanged()) {
|
|
|
|
try {
|
|
|
|
handler.saveObjectAsync(island);
|
|
|
|
} catch (Exception e) {
|
|
|
|
plugin.logError("Could not save island to database when running sync! " + e.getMessage());
|
|
|
|
}
|
2020-11-27 19:12:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}.runTaskTimer(plugin, 0, 1);
|
2017-07-07 07:00:21 +02:00
|
|
|
}
|
|
|
|
|
2017-07-08 02:42:19 +02:00
|
|
|
/**
|
2017-11-21 01:05:52 +01: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
|
2017-07-08 02:42:19 +02:00
|
|
|
*/
|
2018-06-01 03:52:05 +02:00
|
|
|
public void setJoinTeam(Island teamIsland, UUID playerUUID) {
|
2017-11-21 01:05:52 +01:00
|
|
|
// Add player to new island
|
|
|
|
teamIsland.addMember(playerUUID);
|
|
|
|
islandCache.addPlayer(playerUUID, teamIsland);
|
2019-01-13 02:12:30 +01:00
|
|
|
// Save the island
|
2020-04-26 01:00:49 +02:00
|
|
|
handler.saveObjectAsync(teamIsland);
|
2017-08-18 16:05:35 +02:00
|
|
|
}
|
|
|
|
|
2017-11-21 01:05:52 +01:00
|
|
|
public void setLast(Location last) {
|
2018-05-18 06:25:12 +02:00
|
|
|
this.last.put(last.getWorld(), last);
|
2017-08-18 16:05:35 +02:00
|
|
|
}
|
2017-08-26 21:36:03 +02:00
|
|
|
|
|
|
|
/**
|
2017-11-21 01:05:52 +01:00
|
|
|
* Called when a player leaves a team
|
2018-05-18 06:25:12 +02:00
|
|
|
* @param world - world
|
|
|
|
* @param uuid - the player's UUID
|
2017-08-26 21:36:03 +02:00
|
|
|
*/
|
2018-05-18 06:25:12 +02:00
|
|
|
public void setLeaveTeam(World world, UUID uuid) {
|
|
|
|
plugin.getPlayers().clearHomeLocations(world, uuid);
|
|
|
|
removePlayer(world, uuid);
|
2017-11-21 01:05:52 +01:00
|
|
|
}
|
2017-08-28 06:54:01 +02:00
|
|
|
|
2017-11-21 01:05:52 +01:00
|
|
|
public void shutdown(){
|
2021-01-09 05:55:43 +01:00
|
|
|
plugin.log("Removing coops from islands...");
|
2018-11-20 21:52:12 +01:00
|
|
|
// Remove all coop associations
|
2019-05-26 22:14:24 +02:00
|
|
|
islandCache.getIslands().forEach(i -> i.getMembers().values().removeIf(p -> p == RanksManager.COOP_RANK));
|
2021-01-09 05:55:43 +01:00
|
|
|
plugin.log("Saving islands - this has to be done sync so it may take a while with a lot of islands...");
|
2019-01-13 02:12:30 +01:00
|
|
|
saveAll();
|
2021-01-09 05:55:43 +01:00
|
|
|
plugin.log("Islands saved.");
|
2017-11-21 01:05:52 +01:00
|
|
|
islandCache.clear();
|
2021-01-09 05:55:43 +01:00
|
|
|
plugin.log("Closing database.");
|
2018-03-19 05:54:24 +01:00
|
|
|
handler.close();
|
2017-08-26 21:36:03 +02:00
|
|
|
}
|
2018-05-07 01:26:40 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if a player is in a team
|
2018-05-18 06:25:12 +02:00
|
|
|
* @param world - world
|
2018-05-07 01:26:40 +02:00
|
|
|
* @param playerUUID - player's UUID
|
|
|
|
* @return true if in team, false if not
|
|
|
|
*/
|
2018-05-18 06:25:12 +02:00
|
|
|
public boolean inTeam(World world, UUID playerUUID) {
|
|
|
|
return getMembers(world, playerUUID).size() > 1;
|
2018-05-07 01:26:40 +02:00
|
|
|
}
|
2018-05-30 01:59:52 +02:00
|
|
|
|
2018-05-09 04:56:24 +02:00
|
|
|
/**
|
2018-12-09 11:28:31 +01:00
|
|
|
* 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
|
2018-05-09 04:56:24 +02:00
|
|
|
*/
|
2018-10-27 16:32:06 +02:00
|
|
|
public void setOwner(World world, User user, UUID targetUUID) {
|
|
|
|
setOwner(user, targetUUID, getIsland(world, targetUUID));
|
2018-05-09 06:14:20 +02:00
|
|
|
}
|
2018-05-30 01:59:52 +02:00
|
|
|
|
2018-05-09 06:14:20 +02:00
|
|
|
/**
|
2018-12-09 11:28:31 +01:00
|
|
|
* Sets this target as the owner for this island
|
|
|
|
* @param user requester
|
|
|
|
* @param targetUUID new owner
|
|
|
|
* @param island island to register
|
2018-05-09 06:14:20 +02:00
|
|
|
*/
|
2018-10-27 16:32:06 +02:00
|
|
|
public void setOwner(User user, UUID targetUUID, Island island) {
|
2018-05-09 06:14:20 +02:00
|
|
|
islandCache.setOwner(island, targetUUID);
|
2018-12-31 03:09:56 +01:00
|
|
|
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
|
2020-01-19 07:36:38 +01:00
|
|
|
if (target.isOnline() &&
|
|
|
|
target.getEffectivePermissions().parallelStream()
|
2020-03-21 11:29:33 +01:00
|
|
|
.map(PermissionAttachmentInfo::getPermission)
|
2020-01-19 07:36:38 +01:00
|
|
|
.anyMatch(p -> p.startsWith(addon.getPermissionPrefix() + "island.range"))) {
|
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.");
|
2020-01-17 13:42:53 +01:00
|
|
|
|
|
|
|
// Get old range for event
|
|
|
|
int oldRange = island.getProtectionRange();
|
|
|
|
island.setProtectionRange(range);
|
|
|
|
|
2021-02-13 19:19:53 +01:00
|
|
|
// Call Protection Range Change event. Does not support canceling.
|
2020-01-17 13:42:53 +01:00
|
|
|
IslandEvent.builder()
|
2020-01-19 07:36:38 +01:00
|
|
|
.island(island)
|
|
|
|
.location(island.getCenter())
|
|
|
|
.reason(IslandEvent.Reason.RANGE_CHANGE)
|
|
|
|
.involvedPlayer(targetUUID)
|
|
|
|
.admin(true)
|
|
|
|
.protectionRange(range, oldRange)
|
|
|
|
.build();
|
2018-12-11 04:22:07 +01:00
|
|
|
}
|
2018-05-09 04:56:24 +02:00
|
|
|
}
|
2018-12-11 04:22:07 +01:00
|
|
|
});
|
2018-05-09 04:56:24 +02:00
|
|
|
}
|
|
|
|
|
2018-06-25 02:57:31 +02:00
|
|
|
/**
|
2019-07-19 06:58:16 +02:00
|
|
|
* Clear an area of mobs as per world rules. Radius is default 5 blocks in every direction.
|
|
|
|
* Value is set in BentoBox config.yml
|
|
|
|
* Will not remove any named monsters.
|
2018-06-25 02:57:31 +02:00
|
|
|
* @param loc - location to clear
|
|
|
|
*/
|
|
|
|
public void clearArea(Location loc) {
|
2019-07-19 06:58:16 +02:00
|
|
|
if (!plugin.getIWM().inWorld(loc)) return;
|
|
|
|
loc.getWorld().getNearbyEntities(loc, plugin.getSettings().getClearRadius(),
|
|
|
|
plugin.getSettings().getClearRadius(),
|
|
|
|
plugin.getSettings().getClearRadius()).stream()
|
2021-01-05 07:50:16 +01:00
|
|
|
.filter(LivingEntity.class::isInstance)
|
2020-12-31 18:51:09 +01:00
|
|
|
.filter(en -> Util.isHostileEntity(en)
|
2019-03-10 01:54:54 +01:00
|
|
|
&& !plugin.getIWM().getRemoveMobsWhitelist(loc.getWorld()).contains(en.getType())
|
2021-01-05 07:50:16 +01:00
|
|
|
&& !(en instanceof PufferFish)
|
|
|
|
&& ((LivingEntity)en).getRemoveWhenFarAway())
|
2019-07-19 06:58:16 +02:00
|
|
|
.filter(en -> en.getCustomName() == null)
|
2018-06-25 02:57:31 +02:00
|
|
|
.forEach(Entity::remove);
|
|
|
|
}
|
|
|
|
|
2018-11-20 21:52:12 +01:00
|
|
|
/**
|
|
|
|
* 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) {
|
2019-05-26 22:14:24 +02:00
|
|
|
islandCache.getIslands().forEach(i -> i.getMembers().entrySet().removeIf(e -> e.getKey().equals(uniqueId) && e.getValue() == rank));
|
2018-11-20 21:52:12 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 02:12:30 +01:00
|
|
|
/**
|
|
|
|
* Save the island to the database
|
|
|
|
* @param island - island
|
|
|
|
*/
|
|
|
|
public void save(Island island) {
|
2020-04-26 01:00:49 +02:00
|
|
|
handler.saveObjectAsync(island);
|
2019-01-13 02:12:30 +01:00
|
|
|
}
|
|
|
|
|
2019-02-12 04:01:44 +01:00
|
|
|
/**
|
|
|
|
* Try to get an island by its unique id
|
|
|
|
* @param uniqueId - unique id string
|
|
|
|
* @return optional island
|
2019-02-17 13:32:05 +01:00
|
|
|
* @since 1.3.0
|
2019-02-12 04:01:44 +01:00
|
|
|
*/
|
2019-02-21 10:00:59 +01:00
|
|
|
@NonNull
|
2019-02-12 04:01:44 +01:00
|
|
|
public Optional<Island> getIslandById(String uniqueId) {
|
|
|
|
return Optional.ofNullable(islandCache.getIslandById(uniqueId));
|
|
|
|
}
|
2019-02-17 07:31:46 +01:00
|
|
|
|
2019-02-17 13:32:05 +01:00
|
|
|
/**
|
|
|
|
* Try to get a list of quarantined islands owned by uuid in this world
|
|
|
|
*
|
|
|
|
* @param world - world
|
|
|
|
* @param uuid - target player's UUID, or <tt>null</tt> = unowned islands
|
|
|
|
* @return list of islands; may be empty
|
|
|
|
* @since 1.3.0
|
|
|
|
*/
|
2019-02-21 10:00:59 +01:00
|
|
|
@NonNull
|
2019-02-17 13:32:05 +01:00
|
|
|
public List<Island> getQuarantinedIslandByUser(@NonNull World world, @Nullable UUID uuid) {
|
|
|
|
return quarantineCache.getOrDefault(uuid, Collections.emptyList()).stream()
|
|
|
|
.filter(i -> i.getWorld().equals(world)).collect(Collectors.toList());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete quarantined islands owned by uuid in this world
|
|
|
|
*
|
|
|
|
* @param world - world
|
|
|
|
* @param uuid - target player's UUID, or <tt>null</tt> = unowned islands
|
|
|
|
* @since 1.3.0
|
|
|
|
*/
|
|
|
|
public void deleteQuarantinedIslandByUser(World world, @Nullable UUID uuid) {
|
|
|
|
if (quarantineCache.containsKey(uuid)) {
|
|
|
|
quarantineCache.get(uuid).stream().filter(i -> i.getWorld().equals(world))
|
|
|
|
.forEach(i -> handler.deleteObject(i));
|
|
|
|
quarantineCache.get(uuid).removeIf(i -> i.getWorld().equals(world));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the quarantineCache
|
|
|
|
* @since 1.3.0
|
|
|
|
*/
|
2019-02-21 10:00:59 +01:00
|
|
|
@NonNull
|
2019-02-17 13:32:05 +01:00
|
|
|
public Map<UUID, List<Island>> getQuarantineCache() {
|
|
|
|
return quarantineCache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a quarantined island and delete it from the database completely.
|
|
|
|
* This is NOT recoverable unless you have database backups.
|
|
|
|
* @param island island
|
2019-02-21 10:00:59 +01:00
|
|
|
* @return {@code true} if island is quarantined and removed
|
2019-02-17 13:32:05 +01:00
|
|
|
* @since 1.3.0
|
|
|
|
*/
|
|
|
|
public boolean purgeQuarantinedIsland(Island island) {
|
2019-02-21 10:00:59 +01:00
|
|
|
if (quarantineCache.containsKey(island.getOwner()) && quarantineCache.get(island.getOwner()).remove(island)) {
|
|
|
|
handler.deleteObject(island);
|
|
|
|
return true;
|
2019-02-17 13:32:05 +01:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Switches active island and island in trash
|
|
|
|
* @param world - game world
|
|
|
|
* @param target - target player's UUID
|
|
|
|
* @param island - island in trash
|
|
|
|
* @return <tt>true</tt> if successful, otherwise <tt>false</tt>
|
|
|
|
* @since 1.3.0
|
|
|
|
*/
|
|
|
|
public boolean switchIsland(World world, UUID target, Island island) {
|
|
|
|
// Remove trashed island from trash
|
|
|
|
if (!quarantineCache.containsKey(island.getOwner()) || !quarantineCache.get(island.getOwner()).remove(island)) {
|
|
|
|
plugin.logError("Could not remove island from trash");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Remove old island from cache if it exists
|
|
|
|
if (this.hasIsland(world, target)) {
|
|
|
|
Island oldIsland = islandCache.get(world, target);
|
|
|
|
islandCache.removeIsland(oldIsland);
|
|
|
|
|
|
|
|
// Set old island to trash
|
|
|
|
oldIsland.setDoNotLoad(true);
|
|
|
|
|
|
|
|
// Put old island into trash
|
|
|
|
quarantineCache.computeIfAbsent(target, k -> new ArrayList<>()).add(oldIsland);
|
|
|
|
// Save old island
|
2020-04-26 01:00:49 +02:00
|
|
|
handler.saveObjectAsync(oldIsland).thenAccept(result -> {
|
2020-06-02 14:22:46 +02:00
|
|
|
if (Boolean.FALSE.equals(result)) plugin.logError("Could not save trashed island in database");
|
2020-04-26 01:00:49 +02:00
|
|
|
});
|
2019-02-17 13:32:05 +01:00
|
|
|
}
|
|
|
|
// Restore island from trash
|
|
|
|
island.setDoNotLoad(false);
|
|
|
|
// Add new island to cache
|
|
|
|
if (!islandCache.addIsland(island)) {
|
|
|
|
plugin.logError("Could not add recovered island to cache");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Save new island
|
2020-04-26 01:00:49 +02:00
|
|
|
handler.saveObjectAsync(island).thenAccept(result -> {
|
2020-06-02 14:22:46 +02:00
|
|
|
if (Boolean.FALSE.equals(result)) plugin.logError("Could not save recovered island to database");
|
2020-04-26 01:00:49 +02:00
|
|
|
});
|
2019-02-17 13:32:05 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-02-17 07:31:46 +01:00
|
|
|
/**
|
|
|
|
* 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();
|
|
|
|
}
|
2019-08-28 14:29:20 +02:00
|
|
|
|
2019-09-29 23:35:15 +02:00
|
|
|
/**
|
|
|
|
* Resets a flag to gamemode config.yml default
|
|
|
|
* @param world - world
|
|
|
|
* @param flag - flag to reset
|
|
|
|
* @since 1.8.0
|
|
|
|
*/
|
|
|
|
public void resetFlag(World world, Flag flag) {
|
|
|
|
islandCache.resetFlag(world, flag);
|
|
|
|
this.saveAll();
|
|
|
|
}
|
|
|
|
|
2019-08-28 14:29:20 +02:00
|
|
|
/**
|
|
|
|
* Returns whether the specified island custom name exists in this world.
|
|
|
|
* @param world World of the gamemode
|
|
|
|
* @param name Name of an island
|
|
|
|
* @return {@code true} if there is an island with the specified name in this world, {@code false} otherwise.
|
|
|
|
* @since 1.7.0
|
|
|
|
*/
|
|
|
|
public boolean nameExists(@NonNull World world, @NonNull String name) {
|
2019-08-28 14:44:55 +02:00
|
|
|
return getIslands(world).stream().filter(island -> island.getName() != null).map(Island::getName)
|
|
|
|
.anyMatch(n -> ChatColor.stripColor(n).equals(ChatColor.stripColor(name)));
|
2019-08-28 14:29:20 +02:00
|
|
|
}
|
2019-09-29 23:35:15 +02:00
|
|
|
|
2020-12-24 18:14:21 +01:00
|
|
|
/**
|
|
|
|
* Called by the admin team fix command. Attempts to fix the database for teams.
|
|
|
|
* It will identify and correct situations where a player is listed in multiple
|
|
|
|
* teams, or is the owner of multiple teams. It will also try to fix the current
|
|
|
|
* cache. It is recommended to restart the server after this command is run.
|
|
|
|
* @param user - admin calling
|
|
|
|
* @param world - game world to check
|
|
|
|
* @return CompletableFuture boolean - true when done
|
|
|
|
*/
|
2020-11-02 00:53:52 +01:00
|
|
|
public CompletableFuture<Boolean> checkTeams(User user, World world) {
|
|
|
|
CompletableFuture<Boolean> r = new CompletableFuture<>();
|
|
|
|
user.sendMessage("commands.admin.team.fix.scanning");
|
|
|
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
|
|
|
Map<UUID, Island> owners = new HashMap<>();
|
|
|
|
Map<UUID, Integer> freq = new HashMap<>();
|
2020-11-02 02:29:18 +01:00
|
|
|
Map<UUID, List<Island>> memberships = new HashMap<>();
|
2020-11-02 00:53:52 +01:00
|
|
|
handler.loadObjects()
|
|
|
|
.stream().filter(i -> i.getOwner() != null)
|
2021-02-19 22:13:09 +01:00
|
|
|
.filter(i -> i.getWorld() != null)
|
2020-11-02 00:53:52 +01:00
|
|
|
.filter(i -> i.getWorld().equals(world))
|
2020-11-08 01:08:54 +01:00
|
|
|
.filter(i -> !i.isDoNotLoad())
|
2020-11-02 00:53:52 +01:00
|
|
|
.forEach(i -> {
|
2020-11-09 01:48:08 +01:00
|
|
|
int count = freq.getOrDefault(i.getOwner(), 0);
|
2020-11-02 00:53:52 +01:00
|
|
|
freq.put(i.getOwner(), count + 1);
|
|
|
|
if (owners.containsKey(i.getOwner())) {
|
2020-11-08 01:08:54 +01:00
|
|
|
// Player already has an island in the database
|
2020-11-02 00:53:52 +01:00
|
|
|
user.sendMessage("commands.admin.team.fix.duplicate-owner" , TextVariables.NAME, plugin.getPlayers().getName(i.getOwner()));
|
2020-11-08 01:08:54 +01:00
|
|
|
Island prev = owners.get(i.getOwner());
|
|
|
|
// Find out if this island is in the cache
|
|
|
|
Island cachedIsland = this.getIsland(i.getWorld(), i.getOwner());
|
|
|
|
if (cachedIsland != null && !cachedIsland.getUniqueId().equals(i.getUniqueId())) {
|
|
|
|
islandCache.deleteIslandFromCache(i.getUniqueId());
|
|
|
|
handler.deleteID(i.getUniqueId());
|
|
|
|
}
|
|
|
|
if (cachedIsland != null && !cachedIsland.getUniqueId().equals(prev.getUniqueId())) {
|
|
|
|
islandCache.deleteIslandFromCache(prev.getUniqueId());
|
|
|
|
handler.deleteID(prev.getUniqueId());
|
|
|
|
}
|
2020-11-02 00:53:52 +01:00
|
|
|
} else {
|
|
|
|
owners.put(i.getOwner(), i);
|
2020-11-02 02:29:18 +01:00
|
|
|
i.getMemberSet().forEach(u ->
|
|
|
|
// Place into membership
|
|
|
|
memberships.computeIfAbsent(u, k -> new ArrayList<>()).add(i));
|
2020-11-02 00:53:52 +01:00
|
|
|
}
|
|
|
|
});
|
2020-11-09 01:48:08 +01:00
|
|
|
freq.entrySet().stream().filter(en -> en.getValue() > 1).forEach(en -> user.sendMessage("commands.admin.team.fix.player-has", TextVariables.NAME, plugin.getPlayers().getName(en.getKey()), TextVariables.NUMBER, String.valueOf(en.getValue())));
|
2020-11-02 02:29:18 +01:00
|
|
|
// Check for players in multiple teams
|
|
|
|
memberships.entrySet().stream()
|
|
|
|
.filter(en -> en.getValue().size() > 1)
|
|
|
|
.forEach(en -> {
|
|
|
|
// Get the islands
|
|
|
|
String ownerName = plugin.getPlayers().getName(en.getKey());
|
|
|
|
user.sendMessage("commands.admin.team.fix.duplicate-member", TextVariables.NAME, ownerName);
|
|
|
|
int highestRank = 0;
|
|
|
|
Island highestIsland = null;
|
|
|
|
for (Island i : en.getValue()) {
|
|
|
|
int rankValue = i.getRank(en.getKey());
|
|
|
|
String rank = plugin.getRanksManager().getRank(rankValue);
|
|
|
|
if (rankValue > highestRank || highestIsland == null) {
|
|
|
|
highestRank = rankValue;
|
|
|
|
highestIsland = i;
|
|
|
|
}
|
|
|
|
String xyz = Util.xyz(i.getCenter().toVector());
|
2021-02-13 19:19:53 +01:00
|
|
|
user.sendMessage("commands.admin.team.fix.rank-on-island", TextVariables.RANK, user.getTranslation(rank), TextVariables.XYZ, xyz);
|
2020-11-08 01:08:54 +01:00
|
|
|
user.sendRawMessage(i.getUniqueId());
|
2020-11-02 02:29:18 +01:00
|
|
|
}
|
|
|
|
// Fix island ownership in cache
|
|
|
|
// Correct island cache
|
2021-01-16 11:27:12 +01:00
|
|
|
if (highestRank == RanksManager.OWNER_RANK && islandCache.getIslandById(highestIsland.getUniqueId()) != null) {
|
|
|
|
islandCache.setOwner(islandCache.getIslandById(highestIsland.getUniqueId()), en.getKey());
|
2020-11-02 02:29:18 +01:00
|
|
|
}
|
|
|
|
// Fix all the entries that are not the highest
|
|
|
|
for (Island island : en.getValue()) {
|
|
|
|
if (!island.equals(highestIsland)) {
|
|
|
|
// Get the actual island being used in the cache
|
|
|
|
Island i = islandCache.getIslandById(island.getUniqueId());
|
2020-11-08 01:08:54 +01:00
|
|
|
if (i != null) {
|
|
|
|
// Remove membership of this island
|
|
|
|
i.removeMember(en.getKey());
|
|
|
|
}
|
|
|
|
// Remove from database island
|
|
|
|
island.removeMember(en.getKey());
|
2020-11-02 02:29:18 +01:00
|
|
|
// Save to database
|
2020-11-08 01:08:54 +01:00
|
|
|
handler.saveObjectAsync(island).thenRun(() -> user.sendMessage("commands.admin.team.fix.fixed"));
|
|
|
|
} else {
|
|
|
|
// Special check for when a player is an owner and member
|
2020-11-02 02:29:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-02 00:53:52 +01:00
|
|
|
});
|
|
|
|
user.sendMessage("commands.admin.team.fix.done");
|
|
|
|
r.complete(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2021-01-03 20:31:34 +01:00
|
|
|
|
2017-05-20 23:09:53 +02:00
|
|
|
}
|