1867 lines
58 KiB
Java
1867 lines
58 KiB
Java
package world.bentobox.bentobox.database.objects;
|
|
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.EnumMap;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.stream.Collectors;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.GameMode;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.World;
|
|
import org.bukkit.World.Environment;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.util.BoundingBox;
|
|
import org.eclipse.jdt.annotation.NonNull;
|
|
import org.eclipse.jdt.annotation.Nullable;
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.common.collect.ImmutableSet.Builder;
|
|
import com.google.gson.annotations.Expose;
|
|
|
|
import world.bentobox.bentobox.BentoBox;
|
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
|
import world.bentobox.bentobox.api.configuration.WorldSettings;
|
|
import world.bentobox.bentobox.api.flags.Flag;
|
|
import world.bentobox.bentobox.api.logs.LogEntry;
|
|
import world.bentobox.bentobox.api.metadata.MetaDataAble;
|
|
import world.bentobox.bentobox.api.metadata.MetaDataValue;
|
|
import world.bentobox.bentobox.api.user.User;
|
|
import world.bentobox.bentobox.database.objects.adapters.Adapter;
|
|
import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter;
|
|
import world.bentobox.bentobox.lists.Flags;
|
|
import world.bentobox.bentobox.managers.RanksManager;
|
|
import world.bentobox.bentobox.util.Pair;
|
|
import world.bentobox.bentobox.util.Util;
|
|
|
|
/**
|
|
* Stores all the info about an island
|
|
* Managed by IslandsManager
|
|
* Responsible for team information as well.
|
|
*
|
|
* @author tastybento
|
|
* @author Poslovitch
|
|
*/
|
|
@Table(name = "Islands")
|
|
public class Island implements DataObject, MetaDataAble {
|
|
|
|
/**
|
|
* Set to true if this data object has been changed since being loaded from the database
|
|
*/
|
|
private boolean changed;
|
|
|
|
// True if this island is deleted and pending deletion from the database
|
|
@Expose
|
|
private boolean deleted = false;
|
|
|
|
@Expose
|
|
@NonNull
|
|
private String uniqueId = UUID.randomUUID().toString();
|
|
|
|
//// Island ////
|
|
// The center of the island space
|
|
@Expose
|
|
private Location center;
|
|
|
|
/**
|
|
* The center location of the protection area
|
|
*/
|
|
@Expose
|
|
@Nullable
|
|
private Location location;
|
|
|
|
|
|
// Island range
|
|
@Expose
|
|
private int range;
|
|
|
|
// Protection size
|
|
@Expose
|
|
private int protectionRange;
|
|
|
|
/**
|
|
* Bonuses to protection range
|
|
* @since 1.20.0
|
|
*/
|
|
@Expose
|
|
private List<BonusRangeRecord> bonusRanges = new ArrayList<>();
|
|
|
|
// Maximum ever protection range - used in island deletion
|
|
@Expose
|
|
private int maxEverProtectionRange;
|
|
|
|
// World the island started in. This may be different from the island location
|
|
@Expose
|
|
private World world;
|
|
|
|
/**
|
|
* Name of the {@link world.bentobox.bentobox.api.addons.GameModeAddon GameModeAddon} this island is handled by.
|
|
* @since 1.5.0
|
|
*/
|
|
@Expose
|
|
private String gameMode;
|
|
|
|
// Display name
|
|
@Expose
|
|
@Nullable
|
|
private String name;
|
|
|
|
// Time parameters
|
|
@Expose
|
|
private long createdDate;
|
|
@Expose
|
|
private long updatedDate;
|
|
|
|
//// Team ////
|
|
/**
|
|
* Owner of the island.
|
|
* There can only be one per island.
|
|
* If it is {@code null}, then the island is considered as unowned.
|
|
*/
|
|
@Expose
|
|
@Nullable
|
|
private UUID owner;
|
|
|
|
/**
|
|
* Members of the island.
|
|
* It contains any player which has one of the following rank on this island: {@link RanksManager#COOP_RANK COOP},
|
|
* {@link RanksManager#TRUSTED_RANK TRUSTED}, {@link RanksManager#MEMBER_RANK MEMBER}, {@link RanksManager#SUB_OWNER_RANK SUB_OWNER},
|
|
* {@link RanksManager#OWNER_RANK OWNER}.
|
|
*/
|
|
@Expose
|
|
private Map<UUID, Integer> members = new HashMap<>();
|
|
|
|
/**
|
|
* Maximum number of members allowed in this island.
|
|
* Key is rank, value is number
|
|
* @since 1.16.0
|
|
*/
|
|
@Expose
|
|
private Map<Integer, Integer> maxMembers;
|
|
|
|
//// State ////
|
|
@Expose
|
|
private boolean spawn = false;
|
|
@Expose
|
|
private boolean purgeProtected = false;
|
|
|
|
//// Protection flags ////
|
|
@Expose
|
|
private Map<String, Integer> flags = new HashMap<>();
|
|
|
|
//// Island History ////
|
|
@Adapter(LogEntryListAdapter.class)
|
|
@Expose
|
|
private List<LogEntry> history = new LinkedList<>();
|
|
|
|
@Expose
|
|
private Map<Environment, Location> spawnPoint = new EnumMap<>(Environment.class);
|
|
|
|
/**
|
|
* This flag is used to quarantine islands that cannot be loaded and should be purged at some point
|
|
*/
|
|
@Expose
|
|
private boolean doNotLoad;
|
|
|
|
/**
|
|
* Used to store flag cooldowns for this island
|
|
*/
|
|
@Expose
|
|
private Map<String, Long> cooldowns = new HashMap<>();
|
|
|
|
/**
|
|
* Commands and the rank required to use them for this island
|
|
*/
|
|
@Expose
|
|
private Map<String, Integer> commandRanks;
|
|
|
|
/**
|
|
* If true then this space is reserved for the owner and when they teleport there they will be asked to make an island
|
|
* @since 1.6.0
|
|
*/
|
|
@Expose
|
|
@Nullable
|
|
private Boolean reserved = null;
|
|
|
|
/**
|
|
* A place to store metadata for this island.
|
|
* @since 1.15.4
|
|
*/
|
|
@Expose
|
|
private Map<String, MetaDataValue> metaData;
|
|
|
|
/**
|
|
* Island homes. Replaces player homes
|
|
* @since 1.16.0
|
|
*/
|
|
@Expose
|
|
private Map<String, Location> homes;
|
|
|
|
/**
|
|
* The maximum number of homes allowed on this island. If null, then the world default is used.
|
|
*/
|
|
@Expose
|
|
private Integer maxHomes;
|
|
|
|
/*
|
|
* *************************** Constructors ******************************
|
|
*/
|
|
|
|
public Island() {}
|
|
|
|
public Island(@NonNull Location location, UUID owner, int protectionRange) {
|
|
setOwner(owner);
|
|
createdDate = System.currentTimeMillis();
|
|
updatedDate = System.currentTimeMillis();
|
|
world = location.getWorld();
|
|
// Make a copy of the location
|
|
center = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ());
|
|
range = BentoBox.getInstance().getIWM().getIslandDistance(world);
|
|
this.protectionRange = protectionRange;
|
|
this.maxEverProtectionRange = protectionRange;
|
|
this.setChanged();
|
|
}
|
|
|
|
|
|
/**
|
|
* Clones an island object
|
|
* @param island - island to clone
|
|
*/
|
|
public Island(Island island) {
|
|
this.center = island.getCenter().clone();
|
|
this.createdDate = island.getCreatedDate();
|
|
Optional.ofNullable(island.getCommandRanks()).ifPresent(cr -> {
|
|
this.commandRanks = new HashMap<>();
|
|
this.commandRanks.putAll(cr);
|
|
});
|
|
Optional.ofNullable(island.getCooldowns()).ifPresent(c -> {
|
|
this.cooldowns = new HashMap<>();
|
|
this.cooldowns.putAll(c);
|
|
});
|
|
this.createdDate = island.getCreatedDate();
|
|
this.deleted = island.isDeleted();
|
|
this.doNotLoad = island.isDoNotLoad();
|
|
this.flags.putAll(island.getFlags());
|
|
this.gameMode = island.getGameMode();
|
|
this.homes = new HashMap<>(island.getHomes());
|
|
this.history.addAll(island.getHistory());
|
|
this.location = island.getProtectionCenter();
|
|
this.maxEverProtectionRange = island.getMaxEverProtectionRange();
|
|
this.maxHomes = island.getMaxHomes();
|
|
this.maxMembers = new HashMap<>(island.getMaxMembers());
|
|
this.members.putAll(island.getMembers());
|
|
island.getMetaData().ifPresent(m -> {
|
|
this.metaData = new HashMap<>();
|
|
this.metaData.putAll(m);
|
|
});
|
|
this.name = island.getName();
|
|
this.owner = island.getOwner();
|
|
this.protectionRange = island.getProtectionRange();
|
|
this.purgeProtected = island.getPurgeProtected();
|
|
this.range = island.getRange();
|
|
this.reserved = island.isReserved();
|
|
this.spawn = island.isSpawn();
|
|
island.getSpawnPoint().forEach((k,v) -> island.spawnPoint.put(k, v.clone()));
|
|
this.uniqueId = island.getUniqueId();
|
|
this.updatedDate = island.getUpdatedDate();
|
|
this.world = island.getWorld();
|
|
this.bonusRanges.addAll(island.getBonusRanges());
|
|
this.setChanged();
|
|
}
|
|
|
|
/*
|
|
* *************************** Methods ******************************
|
|
*/
|
|
|
|
/**
|
|
* Adds a team member. If player is on banned list, they will be removed from it.
|
|
* @param playerUUID - the player's UUID
|
|
*/
|
|
public void addMember(@NonNull UUID playerUUID) {
|
|
setRank(playerUUID, RanksManager.MEMBER_RANK);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Bans the target player from this Island.
|
|
* If the player is a member, coop or trustee, they will be removed from those lists.
|
|
* <br/>
|
|
* Calling this method won't call the {@link world.bentobox.bentobox.api.events.island.IslandBanEvent}.
|
|
* @param issuer UUID of the issuer, may be null.
|
|
* Whenever possible, one should be provided.
|
|
* @param target UUID of the target, must be provided.
|
|
* @return {@code true}
|
|
*/
|
|
public boolean ban(@NonNull UUID issuer, @NonNull UUID target) {
|
|
setRank(target, RanksManager.BANNED_RANK);
|
|
log(new LogEntry.Builder("BAN").data("player", target.toString()).data("issuer", issuer.toString()).build());
|
|
setChanged();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return the banned
|
|
*/
|
|
public Set<UUID> getBanned() {
|
|
Set<UUID> result = new HashSet<>();
|
|
for (Entry<UUID, Integer> member: members.entrySet()) {
|
|
if (member.getValue() <= RanksManager.BANNED_RANK) {
|
|
result.add(member.getKey());
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Unbans the target player from this Island.
|
|
* <br/>
|
|
* Calling this method won't call the {@link world.bentobox.bentobox.api.events.island.IslandUnbanEvent}.
|
|
* @param issuer UUID of the issuer, may be null.
|
|
* Whenever possible, one should be provided.
|
|
* @param target UUID of the target, must be provided.
|
|
* @return {@code true} if the target is successfully unbanned, {@code false} otherwise.
|
|
*/
|
|
public boolean unban(@NonNull UUID issuer, @NonNull UUID target) {
|
|
if (members.remove(target) != null) {
|
|
log(new LogEntry.Builder("UNBAN").data("player", target.toString()).data("issuer", issuer.toString()).build());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns a clone of the location of the center of this island.
|
|
* @return clone of the center Location
|
|
*/
|
|
@NonNull
|
|
public Location getCenter(){
|
|
return Objects.requireNonNull(center, "Island getCenter requires a non-null center").clone();
|
|
}
|
|
|
|
/**
|
|
* @return the date when the island was created
|
|
*/
|
|
public long getCreatedDate(){
|
|
return createdDate;
|
|
}
|
|
|
|
/**
|
|
* Gets the Island Guard flag's setting. If this is a protection flag, then this will be the
|
|
* rank needed to bypass this flag. If it is a Settings flag, any non-zero value means the
|
|
* setting is allowed.
|
|
* @param flag - flag
|
|
* @return flag value
|
|
*/
|
|
public int getFlag(@NonNull Flag flag) {
|
|
return flags.computeIfAbsent(flag.getID(), k -> flag.getDefaultRank());
|
|
}
|
|
|
|
/**
|
|
* @return the flags
|
|
*/
|
|
public Map<String, Integer> getFlags() {
|
|
return flags;
|
|
}
|
|
|
|
/**
|
|
* Returns the members of this island.
|
|
* It contains all players that have any rank on this island, including {@link RanksManager#BANNED_RANK BANNED},
|
|
* {@link RanksManager#TRUSTED_RANK TRUSTED}, {@link RanksManager#MEMBER_RANK MEMBER}, {@link RanksManager#SUB_OWNER_RANK SUB_OWNER},
|
|
* {@link RanksManager#OWNER_RANK OWNER}, etc.
|
|
*
|
|
* @return the members - key is the UUID, value is the RanksManager enum, e.g. {@link RanksManager#MEMBER_RANK}.
|
|
* @see #getMemberSet()
|
|
*/
|
|
public Map<UUID, Integer> getMembers() {
|
|
return members;
|
|
}
|
|
|
|
/**
|
|
* Returns an immutable set containing the UUIDs of players that are truly members of this island.
|
|
* This includes any player which has one of the following rank on this island: {@link RanksManager#MEMBER_RANK MEMBER},
|
|
* {@link RanksManager#SUB_OWNER_RANK SUB_OWNER}, {@link RanksManager#OWNER_RANK OWNER}.
|
|
* @return the members of the island (owner included)
|
|
* @see #getMembers()
|
|
*/
|
|
public ImmutableSet<UUID> getMemberSet(){
|
|
return getMemberSet(RanksManager.MEMBER_RANK);
|
|
}
|
|
|
|
/**
|
|
* Returns an immutable set containing the UUIDs of players with rank above that requested rank inclusive
|
|
* @param minimumRank minimum rank (inclusive) of members
|
|
* @return immutable set of UUIDs
|
|
* @see #getMembers()
|
|
* @since 1.5.0
|
|
*/
|
|
public @NonNull ImmutableSet<UUID> getMemberSet(int minimumRank) {
|
|
Builder<UUID> result = new ImmutableSet.Builder<>();
|
|
members.entrySet().stream().filter(e -> e.getValue() >= minimumRank).map(Map.Entry::getKey).forEach(result::add);
|
|
return result.build();
|
|
}
|
|
|
|
/**
|
|
* Returns an immutable set containing the UUIDs of players with rank equal or above that requested rank (inclusive).
|
|
* @param rank rank to request
|
|
* @param includeAboveRanks whether including players with rank above the requested rank or not
|
|
* @return immutable set of UUIDs
|
|
* @see #getMemberSet(int)
|
|
* @see #getMembers()
|
|
* @since 1.5.0
|
|
*/
|
|
public @NonNull ImmutableSet<UUID> getMemberSet(int rank, boolean includeAboveRanks) {
|
|
if (includeAboveRanks) {
|
|
return getMemberSet(rank);
|
|
}
|
|
Builder<UUID> result = new ImmutableSet.Builder<>();
|
|
members.entrySet().stream().filter(e -> e.getValue() == rank).map(Map.Entry::getKey).forEach(result::add);
|
|
return result.build();
|
|
}
|
|
|
|
/**
|
|
* Get the minimum protected X block coordinate based on the island location.
|
|
* It will never be less than {@link #getMinX()}
|
|
* @return the minProtectedX
|
|
*/
|
|
public int getMinProtectedX() {
|
|
return Math.max(getMinX(), getProtectionCenter().getBlockX() - protectionRange);
|
|
}
|
|
|
|
/**
|
|
* Get the maximum protected X block coordinate based on the island location.
|
|
* It will never be more than {@link #getMaxX()}
|
|
* @return the maxProtectedX
|
|
* @since 1.5.2
|
|
*/
|
|
public int getMaxProtectedX() {
|
|
return Math.min(getMaxX(), getProtectionCenter().getBlockX() + protectionRange);
|
|
}
|
|
|
|
/**
|
|
* Get the minimum protected Z block coordinate based on the island location.
|
|
* It will never be less than {@link #getMinZ()}
|
|
* @return the minProtectedZ
|
|
*/
|
|
public int getMinProtectedZ() {
|
|
return Math.max(getMinZ(), getProtectionCenter().getBlockZ() - protectionRange);
|
|
}
|
|
|
|
/**
|
|
* Get the maximum protected Z block coordinate based on the island location.
|
|
* It will never be more than {@link #getMinZ()}
|
|
* @return the maxProtectedZ
|
|
* @since 1.5.2
|
|
*/
|
|
public int getMaxProtectedZ() {
|
|
return Math.min(getMaxZ(), getProtectionCenter().getBlockZ() + protectionRange);
|
|
}
|
|
|
|
/**
|
|
* @return the minX
|
|
*/
|
|
public int getMinX() {
|
|
return center.getBlockX() - range;
|
|
}
|
|
|
|
/**
|
|
* @return the maxX
|
|
* @since 1.5.2
|
|
*/
|
|
public int getMaxX() {
|
|
return center.getBlockX() + range;
|
|
}
|
|
|
|
/**
|
|
* @return the minZ
|
|
*/
|
|
public int getMinZ() {
|
|
return center.getBlockZ() - range;
|
|
}
|
|
|
|
/**
|
|
* @return the maxZ
|
|
* @since 1.5.2
|
|
*/
|
|
public int getMaxZ() {
|
|
return center.getBlockZ() + range;
|
|
}
|
|
|
|
/**
|
|
* @return the island display name. Might be {@code null} if none is set.
|
|
*/
|
|
@Nullable
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Returns the owner of this island.
|
|
* @return the owner, may be null.
|
|
* @see #isOwned()
|
|
* @see #isUnowned()
|
|
*/
|
|
@Nullable
|
|
public UUID getOwner(){
|
|
return owner;
|
|
}
|
|
|
|
/**
|
|
* Returns whether this island is owned or not.
|
|
* @return {@code true} if this island has an owner, {@code false} otherwise.
|
|
* @since 1.9.1
|
|
* @see #getOwner()
|
|
* @see #isUnowned()
|
|
*/
|
|
public boolean isOwned() {
|
|
return owner != null;
|
|
}
|
|
|
|
/**
|
|
* Returns whether this island does not have an owner.
|
|
* @return {@code true} if this island does not have an owner, {@code false} otherwise.
|
|
* @since 1.9.1
|
|
* @see #getOwner()
|
|
* @see #isOwned()
|
|
*/
|
|
public boolean isUnowned() {
|
|
return owner == null;
|
|
}
|
|
|
|
/**
|
|
* Returns the protection range of this Island plus any bonuses. Will not be greater than getRange().
|
|
* This represents half of the length of the side of a theoretical square around the island center inside which flags are enforced.
|
|
* @return the protection range of this island, strictly positive integer.
|
|
* @see #getRange()
|
|
*/
|
|
public int getProtectionRange() {
|
|
return Math.min(this.getRange(), getRawProtectionRange() + this.getBonusRanges().stream().mapToInt(BonusRangeRecord::getRange).sum());
|
|
}
|
|
|
|
/**
|
|
* Returns the protection range of this Island without any bonuses
|
|
* This represents half of the length of the side of a theoretical square around the island center inside which flags are enforced.
|
|
* @return the protection range of this island, strictly positive integer.
|
|
* @since 1.20.0
|
|
*/
|
|
public int getRawProtectionRange() {
|
|
return protectionRange;
|
|
}
|
|
|
|
/**
|
|
* @return the maxEverProtectionRange or the protection range, whichever is larger
|
|
*/
|
|
public int getMaxEverProtectionRange() {
|
|
if (maxEverProtectionRange > this.getRange()) {
|
|
maxEverProtectionRange = this.getRange();
|
|
setChanged();
|
|
}
|
|
return Math.max(this.getProtectionRange(), maxEverProtectionRange);
|
|
}
|
|
|
|
/**
|
|
* Sets the maximum protection range. This can be used to optimize
|
|
* island deletion. Setting this values to a lower value than the current value
|
|
* will have no effect.
|
|
* @param maxEverProtectionRange the maxEverProtectionRange to set
|
|
*/
|
|
public void setMaxEverProtectionRange(int maxEverProtectionRange) {
|
|
if (maxEverProtectionRange > this.maxEverProtectionRange) {
|
|
this.maxEverProtectionRange = maxEverProtectionRange;
|
|
}
|
|
if (maxEverProtectionRange > this.range) {
|
|
this.maxEverProtectionRange = this.range;
|
|
}
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @return true if the island is protected from the Purge, otherwise false
|
|
*/
|
|
public boolean getPurgeProtected(){
|
|
return purgeProtected;
|
|
}
|
|
|
|
/**
|
|
* Returns the island range.
|
|
* It is a convenience method that returns the exact same value than island range, although it has been saved into the Island object for easier access.
|
|
* @return the island range
|
|
* @see #getProtectionRange()
|
|
*/
|
|
public int getRange(){
|
|
return range;
|
|
}
|
|
|
|
/**
|
|
* Get the rank of user for this island
|
|
* @param user - the User
|
|
* @return rank integer
|
|
*/
|
|
public int getRank(User user) {
|
|
return members.getOrDefault(user.getUniqueId(), RanksManager.VISITOR_RANK);
|
|
}
|
|
|
|
/**
|
|
* Get the rank of user for this island
|
|
* @param userUUID - the User's UUID
|
|
* @return rank integer
|
|
* @since 1.14.0
|
|
*/
|
|
public int getRank(UUID userUUID) {
|
|
return members.getOrDefault(userUUID, RanksManager.VISITOR_RANK);
|
|
}
|
|
|
|
@Override
|
|
public @NonNull String getUniqueId() {
|
|
return uniqueId;
|
|
}
|
|
|
|
/**
|
|
* @return the date when the island was updated (team member connection, etc...)
|
|
*/
|
|
public long getUpdatedDate(){
|
|
return updatedDate;
|
|
}
|
|
|
|
/**
|
|
* @return the world
|
|
*/
|
|
public World getWorld() {
|
|
return world;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return the nether world
|
|
*/
|
|
@Nullable
|
|
public World getNetherWorld()
|
|
{
|
|
return this.getWorld(Environment.NETHER);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return the end world
|
|
*/
|
|
@Nullable
|
|
public World getEndWorld()
|
|
{
|
|
return this.getWorld(Environment.THE_END);
|
|
}
|
|
|
|
|
|
/**
|
|
* This method returns this island world in given environment. This method can return {@code null} if dimension is
|
|
* disabled.
|
|
* @param environment The environment of the island world.
|
|
* @return the world in given environment.
|
|
*/
|
|
@Nullable
|
|
public World getWorld(Environment environment)
|
|
{
|
|
if (Environment.NORMAL.equals(environment))
|
|
{
|
|
return this.world;
|
|
}
|
|
else if (Environment.THE_END.equals(environment) && this.isEndIslandEnabled())
|
|
{
|
|
return this.getPlugin().getIWM().getEndWorld(this.world);
|
|
}
|
|
else if (Environment.NETHER.equals(environment) && this.isNetherIslandEnabled())
|
|
{
|
|
return this.getPlugin().getIWM().getNetherWorld(this.world);
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @return the x coordinate of the island center
|
|
*/
|
|
public int getX(){
|
|
return center.getBlockX();
|
|
}
|
|
|
|
/**
|
|
* @return the y coordinate of the island center
|
|
*/
|
|
public int getY(){
|
|
return center.getBlockY();
|
|
}
|
|
|
|
/**
|
|
* @return the z coordinate of the island center
|
|
*/
|
|
public int getZ(){
|
|
return center.getBlockZ();
|
|
}
|
|
|
|
/**
|
|
* Checks if coords are in the island space
|
|
* @param x - x coordinate
|
|
* @param z - z coordinate
|
|
* @return true if in the island space
|
|
*/
|
|
public boolean inIslandSpace(int x, int z) {
|
|
return x >= getMinX() && x < getMinX() + range*2 && z >= getMinZ() && z < getMinZ() + range*2;
|
|
}
|
|
|
|
/**
|
|
* Checks if location is in full island space, not just protected space
|
|
* @param location - location
|
|
* @return true if in island space
|
|
*/
|
|
@SuppressWarnings("ConstantConditions")
|
|
public boolean inIslandSpace(Location location) {
|
|
return Util.sameWorld(this.world, location.getWorld()) &&
|
|
(location.getWorld().getEnvironment().equals(Environment.NORMAL) ||
|
|
this.getPlugin().getIWM().isIslandNether(location.getWorld()) ||
|
|
this.getPlugin().getIWM().isIslandEnd(location.getWorld())) &&
|
|
this.inIslandSpace(location.getBlockX(), location.getBlockZ());
|
|
}
|
|
|
|
/**
|
|
* Checks if the coordinates are in full island space, not just protected space
|
|
* @param blockCoordinates - Pair(x,z) coordinates of block
|
|
* @return true or false
|
|
*/
|
|
public boolean inIslandSpace(Pair<Integer, Integer> blockCoordinates) {
|
|
return inIslandSpace(blockCoordinates.x, blockCoordinates.z);
|
|
}
|
|
|
|
/**
|
|
* Returns a {@link BoundingBox} of the full island space for overworld.
|
|
* @return a {@link BoundingBox} of the full island space.
|
|
* @since 1.5.2
|
|
*/
|
|
@SuppressWarnings("ConstantConditions")
|
|
@NotNull
|
|
public BoundingBox getBoundingBox() {
|
|
return this.getBoundingBox(Environment.NORMAL);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a {@link BoundingBox} of this island's space area in requested dimension.
|
|
* @param environment the requested dimension.
|
|
* @return a {@link BoundingBox} of this island's space area or {@code null} if island is not created in requested dimension.
|
|
* @since 1.21.0
|
|
*/
|
|
@Nullable
|
|
public BoundingBox getBoundingBox(Environment environment)
|
|
{
|
|
BoundingBox boundingBox;
|
|
|
|
if (Environment.NORMAL.equals(environment))
|
|
{
|
|
// Return normal world bounding box.
|
|
boundingBox = new BoundingBox(this.getMinX(),
|
|
this.world.getMinHeight(),
|
|
this.getMinZ(),
|
|
this.getMaxX(),
|
|
this.world.getMaxHeight(),
|
|
this.getMaxZ());
|
|
}
|
|
else if (Environment.THE_END.equals(environment) && this.isEndIslandEnabled())
|
|
{
|
|
// If end world is generated, return end island bounding box.
|
|
//noinspection ConstantConditions
|
|
boundingBox = new BoundingBox(this.getMinX(),
|
|
this.getEndWorld().getMinHeight(),
|
|
this.getMinZ(),
|
|
this.getMaxX(),
|
|
this.getEndWorld().getMaxHeight(),
|
|
this.getMaxZ());
|
|
}
|
|
else if (Environment.NETHER.equals(environment) && this.isNetherIslandEnabled())
|
|
{
|
|
// If nether world is generated, return nether island bounding box.
|
|
//noinspection ConstantConditions
|
|
boundingBox = new BoundingBox(this.getMinX(),
|
|
this.getNetherWorld().getMinHeight(),
|
|
this.getMinZ(),
|
|
this.getMaxX(),
|
|
this.getNetherWorld().getMaxHeight(),
|
|
this.getMaxZ());
|
|
}
|
|
else
|
|
{
|
|
boundingBox = null;
|
|
}
|
|
|
|
return boundingBox;
|
|
}
|
|
|
|
|
|
/**
|
|
* Using this method in the filtering for getVisitors and hasVisitors
|
|
* @param player The player that must be checked.
|
|
* @return true if player is a visitor
|
|
*/
|
|
private boolean playerIsVisitor(Player player) {
|
|
if (player.getGameMode() == GameMode.SPECTATOR) {
|
|
return false;
|
|
}
|
|
|
|
return onIsland(player.getLocation()) && getRank(User.getInstance(player)) == RanksManager.VISITOR_RANK;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of players that are physically inside the island's protection range and that are visitors.
|
|
* @return list of visitors
|
|
* @since 1.3.0
|
|
*/
|
|
@NonNull
|
|
public List<Player> getVisitors() {
|
|
return Bukkit.getOnlinePlayers().stream().filter(this::playerIsVisitor).collect(Collectors.toList());
|
|
}
|
|
|
|
/**
|
|
* Returns whether this Island has visitors inside its protection range.
|
|
* Note this is equivalent to {@code !island.getVisitors().isEmpty()}.
|
|
* @return {@code true} if there are visitors inside this Island's protection range, {@code false} otherwise.
|
|
*
|
|
* @since 1.3.0
|
|
* @see #getVisitors()
|
|
*/
|
|
public boolean hasVisitors() {
|
|
return Bukkit.getOnlinePlayers().stream().anyMatch(this::playerIsVisitor);
|
|
}
|
|
|
|
/**
|
|
* Returns a list of players that are physically inside the island's protection range
|
|
* @return list of players
|
|
* @since 1.6.0
|
|
*/
|
|
@NonNull
|
|
public List<Player> getPlayersOnIsland() {
|
|
return Bukkit.getOnlinePlayers().stream()
|
|
.filter(player -> onIsland(player.getLocation()))
|
|
.collect(Collectors.toList());
|
|
}
|
|
|
|
/**
|
|
* Returns whether this Island has players inside its protection range.
|
|
* Note this is equivalent to {@code !island.getPlayersOnIsland().isEmpty()}.
|
|
* @return {@code true} if there are players inside this Island's protection range, {@code false} otherwise.
|
|
*
|
|
* @since 1.6.0
|
|
* @see #getPlayersOnIsland()
|
|
*/
|
|
public boolean hasPlayersOnIsland() {
|
|
return Bukkit.getOnlinePlayers().stream().anyMatch(player -> onIsland(player.getLocation()));
|
|
}
|
|
|
|
/**
|
|
* Check if the flag is allowed or not
|
|
* For flags that are for the island in general and not related to rank.
|
|
* @param flag - flag
|
|
* @return true if allowed, false if not
|
|
*/
|
|
public boolean isAllowed(Flag flag) {
|
|
// A negative value means not allowed
|
|
return getFlag(flag) >= 0;
|
|
}
|
|
|
|
/**
|
|
* Check if a user is allowed to bypass the flag or not
|
|
* @param user - the User - user
|
|
* @param flag - flag
|
|
* @return true if allowed, false if not
|
|
*/
|
|
public boolean isAllowed(User user, Flag flag) {
|
|
return getRank(user) >= getFlag(flag);
|
|
}
|
|
|
|
/**
|
|
* Check if banned
|
|
* @param targetUUID - the target's UUID
|
|
* @return Returns true if target is banned on this island
|
|
*/
|
|
public boolean isBanned(UUID targetUUID) {
|
|
return members.containsKey(targetUUID) && members.get(targetUUID).equals(RanksManager.BANNED_RANK);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the island is a spawn or not.
|
|
* @return {@code true} if the island is a spawn, {@code false} otherwise.
|
|
*/
|
|
public boolean isSpawn() {
|
|
return spawn;
|
|
}
|
|
|
|
/**
|
|
* Checks if a location is within this island's protected area.
|
|
*
|
|
* @param target location to check, not null
|
|
* @return {@code true} if this location is within this island's protected area, {@code false} otherwise.
|
|
*/
|
|
@SuppressWarnings("ConstantConditions")
|
|
public boolean onIsland(@NonNull Location target) {
|
|
return Util.sameWorld(this.world, target.getWorld()) &&
|
|
(target.getWorld().getEnvironment().equals(Environment.NORMAL) ||
|
|
this.getPlugin().getIWM().isIslandNether(target.getWorld()) ||
|
|
this.getPlugin().getIWM().isIslandEnd(target.getWorld())) &&
|
|
target.getBlockX() >= this.getMinProtectedX() &&
|
|
target.getBlockX() < (this.getMinProtectedX() + this.protectionRange * 2) &&
|
|
target.getBlockZ() >= this.getMinProtectedZ() &&
|
|
target.getBlockZ() < (this.getMinProtectedZ() + this.protectionRange * 2);
|
|
}
|
|
|
|
/**
|
|
* Returns a {@link BoundingBox} of this island's protected area for overworld.
|
|
* @return a {@link BoundingBox} of this island's protected area.
|
|
* @since 1.5.2
|
|
*/
|
|
@SuppressWarnings("ConstantConditions")
|
|
@NotNull
|
|
public BoundingBox getProtectionBoundingBox() {
|
|
return this.getProtectionBoundingBox(Environment.NORMAL);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a {@link BoundingBox} of this island's protected area.
|
|
* @param environment an environment of bounding box area.
|
|
* @return a {@link BoundingBox} of this island's protected area or {@code null} if island is not created in required dimension.
|
|
* in required dimension.
|
|
* @since 1.21.0
|
|
*/
|
|
@Nullable
|
|
public BoundingBox getProtectionBoundingBox(Environment environment)
|
|
{
|
|
BoundingBox boundingBox;
|
|
|
|
if (Environment.NORMAL.equals(environment))
|
|
{
|
|
// Return normal world bounding box.
|
|
boundingBox = new BoundingBox(this.getMinProtectedX(),
|
|
this.world.getMinHeight(),
|
|
this.getMinProtectedZ(),
|
|
this.getMaxProtectedX(),
|
|
this.world.getMaxHeight(),
|
|
this.getMaxProtectedZ());
|
|
}
|
|
else if (Environment.THE_END.equals(environment) && this.isEndIslandEnabled())
|
|
{
|
|
// If end world is generated, return end island bounding box.
|
|
//noinspection ConstantConditions
|
|
boundingBox = new BoundingBox(this.getMinProtectedX(),
|
|
this.getEndWorld().getMinHeight(),
|
|
this.getMinProtectedZ(),
|
|
this.getMaxProtectedX(),
|
|
this.getEndWorld().getMaxHeight(),
|
|
this.getMaxProtectedZ());
|
|
}
|
|
else if (Environment.NETHER.equals(environment) && this.isNetherIslandEnabled())
|
|
{
|
|
// If nether world is generated, return nether island bounding box.
|
|
//noinspection ConstantConditions
|
|
boundingBox = new BoundingBox(this.getMinProtectedX(),
|
|
this.getNetherWorld().getMinHeight(),
|
|
this.getMinProtectedZ(),
|
|
this.getMaxProtectedX(),
|
|
this.getNetherWorld().getMaxHeight(),
|
|
this.getMaxProtectedZ());
|
|
}
|
|
else
|
|
{
|
|
boundingBox = null;
|
|
}
|
|
|
|
return boundingBox;
|
|
}
|
|
|
|
|
|
/**
|
|
* Removes a player from the team member map. Generally, you should
|
|
* use {@link world.bentobox.bentobox.managers.IslandsManager#removePlayer(World, UUID)}
|
|
* @param playerUUID - uuid of player
|
|
*/
|
|
public void removeMember(UUID playerUUID) {
|
|
members.remove(playerUUID);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @param center the center to set
|
|
*/
|
|
public void setCenter(@NonNull Location center) {
|
|
this.world = center.getWorld();
|
|
this.center = center;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @param createdDate - the createdDate to sets
|
|
*/
|
|
public void setCreatedDate(long createdDate){
|
|
this.createdDate = createdDate;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Set the Island Guard flag rank
|
|
* This method affects subflags (if the given flag is a parent flag)
|
|
* @param flag - flag
|
|
* @param value - Use RanksManager settings, e.g. RanksManager.MEMBER
|
|
*/
|
|
public void setFlag(Flag flag, int value) {
|
|
setFlag(flag, value, true);
|
|
}
|
|
|
|
/**
|
|
* Set the Island Guard flag rank
|
|
* Also specify whether subflags are affected by this method call
|
|
* @param flag - flag
|
|
* @param value - Use RanksManager settings, e.g. RanksManager.MEMBER
|
|
* @param doSubflags - whether to set subflags
|
|
*/
|
|
public void setFlag(Flag flag, int value, boolean doSubflags) {
|
|
flags.put(flag.getID(), value);
|
|
// Subflag support
|
|
if (doSubflags && flag.hasSubflags()) {
|
|
// Ensure that a subflag isn't a subflag of itself or else we're in trouble!
|
|
flag.getSubflags().forEach(subflag -> setFlag(subflag, value, true));
|
|
}
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @param flags the flags to set
|
|
*/
|
|
public void setFlags(Map<String, Integer> flags) {
|
|
this.flags = flags;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Resets the flags to their default as set in config.yml for this island.
|
|
* If flags are missing from the config, the default hard-coded value is used and set
|
|
*/
|
|
public void setFlagsDefaults() {
|
|
BentoBox plugin = BentoBox.getInstance();
|
|
Map<String, Integer> result = new HashMap<>();
|
|
plugin.getFlagsManager().getFlags().stream().
|
|
filter(f -> f.getType().equals(Flag.Type.PROTECTION)).
|
|
forEach(f -> result.put(f.getID(), plugin.getIWM().getDefaultIslandFlags(world).getOrDefault(f, f.getDefaultRank())));
|
|
plugin.getFlagsManager().getFlags().stream().
|
|
filter(f -> f.getType().equals(Flag.Type.SETTING)).
|
|
forEach(f -> result.put(f.getID(), plugin.getIWM().getDefaultIslandSettings(world).getOrDefault(f, f.getDefaultRank())));
|
|
this.setFlags(result);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @param members the members to set
|
|
*/
|
|
public void setMembers(Map<UUID, Integer> members) {
|
|
this.members = members;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Sets the display name of this Island.
|
|
* <br/><br/>
|
|
* An empty String or {@code null} will remove the display name.
|
|
* @param name The display name to set.
|
|
*/
|
|
public void setName(String name){
|
|
this.name = (name != null && !name.equals("")) ? name : null;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Sets the owner of the island.
|
|
* @param owner the island owner - the owner to set
|
|
*/
|
|
public void setOwner(@Nullable UUID owner){
|
|
if (this.owner == owner) {
|
|
return; //No need to update anything
|
|
}
|
|
|
|
this.owner = owner;
|
|
if (owner == null) {
|
|
log(new LogEntry.Builder("UNOWNED").build());
|
|
return;
|
|
}
|
|
// Defensive code: demote any previous owner
|
|
for (Entry<UUID, Integer> en : members.entrySet()) {
|
|
if (en.getValue().equals(RanksManager.OWNER_RANK)) {
|
|
setRank(en.getKey(), RanksManager.MEMBER_RANK);
|
|
}
|
|
}
|
|
setRank(owner, RanksManager.OWNER_RANK);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @param protectionRange the protectionRange to set
|
|
*/
|
|
public void setProtectionRange(int protectionRange) {
|
|
this.protectionRange = protectionRange;
|
|
this.updateMaxEverProtectionRange();
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Updates the maxEverProtectionRange based on the current protectionRange
|
|
*/
|
|
public void updateMaxEverProtectionRange() {
|
|
// Ratchet up the maximum protection range
|
|
// Distance from maxes
|
|
int diffMinX = Math.abs(Objects.requireNonNull(getCenter()).getBlockX() - this.getMinProtectedX());
|
|
int diffMaxX = Math.abs(getCenter().getBlockX() - this.getMaxProtectedX());
|
|
int diffMinZ = Math.abs(getCenter().getBlockZ() - this.getMinProtectedZ());
|
|
int diffMaxZ = Math.abs(getCenter().getBlockZ() - this.getMaxProtectedZ());
|
|
if (diffMinX > this.maxEverProtectionRange) {
|
|
this.maxEverProtectionRange = diffMinX;
|
|
}
|
|
if (diffMaxX > this.maxEverProtectionRange) {
|
|
this.maxEverProtectionRange = diffMaxX;
|
|
}
|
|
if (diffMinZ > this.maxEverProtectionRange) {
|
|
this.maxEverProtectionRange = diffMinZ;
|
|
}
|
|
if (diffMaxZ > this.maxEverProtectionRange) {
|
|
this.maxEverProtectionRange = diffMaxZ;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @param purgeProtected - if the island is protected from the Purge
|
|
*/
|
|
public void setPurgeProtected(boolean purgeProtected){
|
|
this.purgeProtected = purgeProtected;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Sets the island range.
|
|
* This method should <u><strong>NEVER</strong></u> be used except for testing purposes.
|
|
* <br>
|
|
* The range value is a copy of {@link WorldSettings#getIslandDistance()} made when the Island
|
|
* got created in order to allow easier access to this value and must therefore remain
|
|
* <u><strong>AS IS</strong></u>.
|
|
* @param range the range to set
|
|
* @see #setProtectionRange(int)
|
|
*/
|
|
public void setRange(int range){
|
|
this.range = range;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Set user's rank to an arbitrary rank value
|
|
* @param user the User
|
|
* @param rank rank value
|
|
*/
|
|
public void setRank(User user, int rank) {
|
|
setRank(user.getUniqueId(), rank);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Sets player's rank to an arbitrary rank value.
|
|
* Calling this method won't call the {@link world.bentobox.bentobox.api.events.island.IslandRankChangeEvent}.
|
|
* @param uuid UUID of the player
|
|
* @param rank rank value
|
|
* @since 1.1
|
|
*/
|
|
public void setRank(@Nullable UUID uuid, int rank) {
|
|
if (uuid == null) {
|
|
return; // Defensive code
|
|
}
|
|
members.put(uuid, rank);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @param ranks the ranks to set
|
|
*/
|
|
public void setRanks(Map<UUID, Integer> ranks) {
|
|
members = ranks;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Sets whether this island is a spawn or not.
|
|
* <br/>
|
|
* If {@code true}, the members and the owner will be removed from this island.
|
|
* The flags will also be reset to default values.
|
|
* @param isSpawn {@code true} if the island is a spawn, {@code false} otherwise.
|
|
*/
|
|
public void setSpawn(boolean isSpawn){
|
|
if (spawn == isSpawn) {
|
|
return; // No need to update anything
|
|
}
|
|
|
|
spawn = isSpawn;
|
|
if (isSpawn) {
|
|
setOwner(null);
|
|
members.clear();
|
|
setFlagsDefaults();
|
|
setFlag(Flags.LOCK, RanksManager.VISITOR_RANK);
|
|
}
|
|
log(new LogEntry.Builder("SPAWN").data("value", String.valueOf(isSpawn)).build());
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Get the default spawn location for this island. Note that this may only be valid
|
|
* after the initial pasting because the player can change the island after that point
|
|
* @return the spawnPoint
|
|
*/
|
|
public Map<Environment, Location> getSpawnPoint() {
|
|
return spawnPoint;
|
|
}
|
|
|
|
/**
|
|
* Set when island is pasted
|
|
* @param spawnPoint the spawnPoint to set
|
|
*/
|
|
public void setSpawnPoint(Map<Environment, Location> spawnPoint) {
|
|
this.spawnPoint = spawnPoint;
|
|
setChanged();
|
|
}
|
|
|
|
@Override
|
|
public void setUniqueId(String uniqueId) {
|
|
this.uniqueId = uniqueId;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @param updatedDate - the updatedDate to sets
|
|
*/
|
|
public void setUpdatedDate(long updatedDate){
|
|
this.updatedDate = updatedDate;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @param world the world to set
|
|
*/
|
|
public void setWorld(World world) {
|
|
this.world = world;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Toggles a settings flag
|
|
* This method affects subflags (if the given flag is a parent flag)
|
|
* @param flag - flag
|
|
*/
|
|
public void toggleFlag(Flag flag) {
|
|
toggleFlag(flag, true);
|
|
}
|
|
|
|
/**
|
|
* Toggles a settings flag
|
|
* Also specify whether subflags are affected by this method call
|
|
* @param flag - flag
|
|
*/
|
|
public void toggleFlag(Flag flag, boolean doSubflags) {
|
|
boolean newToggleValue = !isAllowed(flag); // Use for subflags
|
|
if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) {
|
|
setSettingsFlag(flag, newToggleValue, doSubflags);
|
|
}
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Sets the state of a settings flag
|
|
* This method affects subflags (if the given flag is a parent flag)
|
|
* @param flag - flag
|
|
* @param state - true or false
|
|
*/
|
|
public void setSettingsFlag(Flag flag, boolean state) {
|
|
setSettingsFlag(flag, state, true);
|
|
}
|
|
|
|
/**
|
|
* Sets the state of a settings flag
|
|
* Also specify whether subflags are affected by this method call
|
|
* @param flag - flag
|
|
* @param state - true or false
|
|
*/
|
|
public void setSettingsFlag(Flag flag, boolean state, boolean doSubflags) {
|
|
int newState = state ? 1 : -1;
|
|
if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) {
|
|
flags.put(flag.getID(), newState);
|
|
if (doSubflags && flag.hasSubflags()) {
|
|
// If we have circular subflags or a flag is a subflag of itself we are in trouble!
|
|
flag.getSubflags().forEach(subflag -> setSettingsFlag(subflag, state, true));
|
|
}
|
|
}
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Set the spawn location for this island type
|
|
* @param islandType - island type
|
|
* @param l - location
|
|
*/
|
|
public void setSpawnPoint(Environment islandType, Location l) {
|
|
spawnPoint.put(islandType, l);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Get the spawn point for this island type
|
|
* @param islandType - island type
|
|
* @return - location or null if one does not exist
|
|
*/
|
|
@Nullable
|
|
public Location getSpawnPoint(Environment islandType) {
|
|
return spawnPoint.get(islandType);
|
|
}
|
|
|
|
/**
|
|
* Removes all of a specified rank from the member list
|
|
* @param rank rank value
|
|
*/
|
|
public void removeRank(Integer rank) {
|
|
members.values().removeIf(rank::equals);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Gets the history of the island.
|
|
* @return the list of {@link LogEntry} for this island.
|
|
*/
|
|
public List<LogEntry> getHistory() {
|
|
return history;
|
|
}
|
|
|
|
/**
|
|
* Adds a {@link LogEntry} to the history of this island.
|
|
* @param logEntry the LogEntry to add.
|
|
*/
|
|
public void log(LogEntry logEntry) {
|
|
history.add(logEntry);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Sets the history of the island.
|
|
* @param history the list of {@link LogEntry} to set for this island.
|
|
*/
|
|
public void setHistory(List<LogEntry> history) {
|
|
this.history = history;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @return the doNotLoad
|
|
*/
|
|
public boolean isDoNotLoad() {
|
|
return doNotLoad;
|
|
}
|
|
|
|
/**
|
|
* @param doNotLoad the doNotLoad to set
|
|
*/
|
|
public void setDoNotLoad(boolean doNotLoad) {
|
|
this.doNotLoad = doNotLoad;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @return the deleted
|
|
*/
|
|
public boolean isDeleted() {
|
|
return deleted;
|
|
}
|
|
|
|
/**
|
|
* @param deleted the deleted to set
|
|
*/
|
|
public void setDeleted(boolean deleted) {
|
|
this.deleted = deleted;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Returns the name of the {@link world.bentobox.bentobox.api.addons.GameModeAddon GameModeAddon} this island is handled by.
|
|
* @return the name of the {@link world.bentobox.bentobox.api.addons.GameModeAddon GameModeAddon} this island is handled by.
|
|
* @since 1.5.0
|
|
*/
|
|
public String getGameMode() {
|
|
return gameMode;
|
|
}
|
|
|
|
/**
|
|
* Sets the name of the {@link world.bentobox.bentobox.api.addons.GameModeAddon GameModeAddon} this island is handled by.
|
|
* Note this has no effect over the actual location of the island, however this may cause issues with addons using this data.
|
|
* @since 1.5.0
|
|
*/
|
|
public void setGameMode(String gameMode) {
|
|
this.gameMode = gameMode;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Checks whether this island has its nether island generated or not.
|
|
* @return {@code true} if this island has its nether island generated, {@code false} otherwise.
|
|
* @since 1.5.0
|
|
*/
|
|
public boolean hasNetherIsland() {
|
|
World nether = BentoBox.getInstance().getIWM().getNetherWorld(getWorld());
|
|
return nether != null && !getCenter().toVector().toLocation(nether).getBlock().getType().isAir();
|
|
}
|
|
|
|
/**
|
|
* Checks whether this island has its nether island mode enabled or not.
|
|
* @return {@code true} if this island has its nether island enabled, {@code false} otherwise.
|
|
* @since 1.21.0
|
|
*/
|
|
public boolean isNetherIslandEnabled() {
|
|
return this.getPlugin().getIWM().isNetherGenerate(this.world) && this.getPlugin().getIWM().isNetherIslands(this.world);
|
|
}
|
|
|
|
/**
|
|
* Checks whether this island has its end island generated or not.
|
|
* @return {@code true} if this island has its end island generated, {@code false} otherwise.
|
|
* @since 1.5.0
|
|
*/
|
|
public boolean hasEndIsland() {
|
|
World end = BentoBox.getInstance().getIWM().getEndWorld(getWorld());
|
|
return end != null && !getCenter().toVector().toLocation(end).getBlock().getType().isAir();
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks whether this island has its end island mode enabled or not.
|
|
* @return {@code true} if this island has its end island enabled, {@code false} otherwise.
|
|
* @since 1.21.0
|
|
*/
|
|
public boolean isEndIslandEnabled() {
|
|
return this.getPlugin().getIWM().isEndGenerate(this.world) && this.getPlugin().getIWM().isEndIslands(this.world);
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if a flag is on cooldown. Only stored in memory so a server restart will reset the cooldown.
|
|
* @param flag - flag
|
|
* @return true if on cooldown, false if not
|
|
* @since 1.6.0
|
|
*/
|
|
public boolean isCooldown(Flag flag) {
|
|
if (cooldowns.containsKey(flag.getID()) && cooldowns.get(flag.getID()) > System.currentTimeMillis()) {
|
|
return true;
|
|
}
|
|
cooldowns.remove(flag.getID());
|
|
setChanged();
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Sets a cooldown for this flag on this island.
|
|
* @param flag - Flag to cooldown
|
|
*/
|
|
public void setCooldown(Flag flag) {
|
|
cooldowns.put(flag.getID(), flag.getCooldown() * 1000L + System.currentTimeMillis());
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @return the cooldowns
|
|
*/
|
|
public Map<String, Long> getCooldowns() {
|
|
return cooldowns;
|
|
}
|
|
|
|
/**
|
|
* @param cooldowns the cooldowns to set
|
|
*/
|
|
public void setCooldowns(Map<String, Long> cooldowns) {
|
|
this.cooldowns = cooldowns;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @return the commandRanks
|
|
*/
|
|
public Map<String, Integer> getCommandRanks() {
|
|
return commandRanks;
|
|
}
|
|
|
|
/**
|
|
* @param commandRanks the commandRanks to set
|
|
*/
|
|
public void setCommandRanks(Map<String, Integer> commandRanks) {
|
|
this.commandRanks = commandRanks;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Get the rank required to run command on this island.
|
|
* The command must have been registered with a rank.
|
|
* @param command - the string given by {@link CompositeCommand#getUsage()}
|
|
* @return Rank value required, or if command is not set {@link CompositeCommand#getDefaultCommandRank()}
|
|
*/
|
|
public int getRankCommand(String command) {
|
|
|
|
if (this.commandRanks == null) {
|
|
this.commandRanks = new HashMap<>();
|
|
}
|
|
|
|
// Return or calculate default rank for a command.
|
|
return this.commandRanks.computeIfAbsent(command, key -> {
|
|
|
|
// Need to find default value for the command.
|
|
String[] labels = key.replaceFirst("/", "").split(" ");
|
|
|
|
// Get first command label.
|
|
CompositeCommand compositeCommand = this.getPlugin().getCommandsManager().getCommand(labels[0]);
|
|
|
|
for (int i = 1; i < labels.length && compositeCommand != null; i++)
|
|
{
|
|
compositeCommand = compositeCommand.getSubCommand(labels[i]).orElse(null);
|
|
}
|
|
|
|
// Return default command rank or owner rank, if command does not exist.
|
|
return compositeCommand == null ? RanksManager.OWNER_RANK : compositeCommand.getDefaultCommandRank();
|
|
});
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param command - the string given by {@link CompositeCommand#getUsage()}
|
|
* @param rank value as used by {@link RanksManager}
|
|
*/
|
|
public void setRankCommand(String command, int rank) {
|
|
if (this.commandRanks == null) this.commandRanks = new HashMap<>();
|
|
this.commandRanks.put(command, rank);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Returns whether this Island is currently reserved or not.
|
|
* If {@code true}, this means no blocks, except a bedrock one at the center of the island, exist.
|
|
* @return {@code true} if this Island is reserved, {@code false} otherwise.
|
|
* @since 1.6.0
|
|
*/
|
|
public boolean isReserved() {
|
|
return reserved != null && reserved;
|
|
}
|
|
|
|
/**
|
|
* @param reserved the reserved to set
|
|
* @since 1.6.0
|
|
*/
|
|
public void setReserved(boolean reserved) {
|
|
this.reserved = reserved;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @return the metaData
|
|
* @since 1.15.5
|
|
*/
|
|
@Override
|
|
public Optional<Map<String, MetaDataValue>> getMetaData() {
|
|
if (metaData == null) {
|
|
metaData = new HashMap<>();
|
|
}
|
|
return Optional.of(metaData);
|
|
}
|
|
|
|
/**
|
|
* @param metaData the metaData to set
|
|
* @since 1.15.4
|
|
*/
|
|
@Override
|
|
public void setMetaData(Map<String, MetaDataValue> metaData) {
|
|
this.metaData = metaData;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @return changed state
|
|
*/
|
|
public boolean isChanged() {
|
|
return changed;
|
|
}
|
|
|
|
/**
|
|
* Indicates the fields have been changed. Used to optimize saving on shutdown.
|
|
*/
|
|
public void setChanged() {
|
|
this.changed = true;
|
|
}
|
|
|
|
/**
|
|
* @param changed the changed to set
|
|
*/
|
|
public void setChanged(boolean changed) {
|
|
this.changed = changed;
|
|
}
|
|
|
|
/**
|
|
* Get the center location of the protection zone.
|
|
* This can be anywhere within the island space and can move.
|
|
* Unless explicitly set, it will return the same as {@link #getCenter()}.
|
|
* @return a clone of the protection center location
|
|
* @since 1.16.0
|
|
*/
|
|
@NonNull
|
|
public Location getProtectionCenter() {
|
|
return location == null ? getCenter() : location.clone();
|
|
}
|
|
|
|
/**
|
|
* Sets the protection center location of the island within the island space.
|
|
* @param location the location to set
|
|
* @throws IOException if the location is not in island space
|
|
* @since 1.16.0
|
|
*/
|
|
public void setProtectionCenter(Location location) throws IOException {
|
|
if (!this.inIslandSpace(location)) {
|
|
throw new IOException("Location must be in island space");
|
|
}
|
|
this.location = location;
|
|
this.updateMaxEverProtectionRange();
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @return the homes
|
|
* @since 1.16.0
|
|
*/
|
|
@NotNull
|
|
public Map<String, Location> getHomes() {
|
|
if (homes == null) {
|
|
homes = new HashMap<>();
|
|
}
|
|
return homes;
|
|
}
|
|
|
|
/**
|
|
* @return the homes
|
|
* @since 1.16.0
|
|
*/
|
|
@Nullable
|
|
public Location getHome(String name) {
|
|
return getHomes().get(name.toLowerCase());
|
|
}
|
|
|
|
/**
|
|
* @param homes the homes to set
|
|
* @since 1.16.0
|
|
*/
|
|
public void setHomes(Map<String, Location> homes) {
|
|
this.homes = homes;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @param name the name of the home
|
|
* @since 1.16.0
|
|
*/
|
|
public void addHome(String name, Location location) {
|
|
getHomes().put(name.toLowerCase(), location);
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Remove a named home from this island
|
|
* @param name - home name to remove
|
|
* @return true if home removed successfully
|
|
* @since 1.16.0
|
|
*/
|
|
public boolean removeHome(String name) {
|
|
setChanged();
|
|
return getHomes().remove(name.toLowerCase()) != null;
|
|
}
|
|
|
|
/**
|
|
* Remove all homes from this island except the default home
|
|
* @return true if any non-default homes removed
|
|
* @since 1.20.0
|
|
*/
|
|
public boolean removeHomes() {
|
|
setChanged();
|
|
return getHomes().keySet().removeIf(k -> !k.isEmpty());
|
|
}
|
|
|
|
/**
|
|
* Rename a home
|
|
* @param oldName - old name of home
|
|
* @param newName - new name of home
|
|
* @return true if successful, false if oldName does not exist, already exists
|
|
* @since 1.16.0
|
|
*/
|
|
public boolean renameHome(String oldName, String newName) {
|
|
if (getHomes().containsKey(oldName.toLowerCase()) && !getHomes().containsKey(newName.toLowerCase())) {
|
|
this.addHome(newName, this.getHome(oldName));
|
|
this.removeHome(oldName);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get the max homes. You shouldn't access this directly.
|
|
* Use {@link world.bentobox.bentobox.managers.IslandsManager#getMaxHomes(Island)}
|
|
* @return the maxHomes. If null, then the world default should be used.
|
|
* @since 1.16.0
|
|
*/
|
|
@Nullable
|
|
public Integer getMaxHomes() {
|
|
return maxHomes;
|
|
}
|
|
|
|
/**
|
|
* @param maxHomes the maxHomes to set. If null then the world default will be used.
|
|
* You shouldn't access this directly.
|
|
* Use {@link world.bentobox.bentobox.managers.IslandsManager#setMaxHomes(Island, Integer)}
|
|
* @since 1.16.0
|
|
*/
|
|
public void setMaxHomes(@Nullable Integer maxHomes) {
|
|
this.maxHomes = maxHomes;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* @return the maxMembers
|
|
* @since 1.16.0
|
|
*/
|
|
public Map<Integer, Integer> getMaxMembers() {
|
|
if (maxMembers == null) {
|
|
maxMembers = new HashMap<>();
|
|
}
|
|
return maxMembers;
|
|
}
|
|
|
|
/**
|
|
* @param maxMembers the maxMembers to set
|
|
* @since 1.16.0
|
|
*/
|
|
public void setMaxMembers(Map<Integer, Integer> maxMembers) {
|
|
this.maxMembers = maxMembers;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Get the maximum number of island members
|
|
* @param rank island rank value from {@link RanksManager}
|
|
* @return the maxMembers for the rank given - if null then the world default should be used. Negative values = unlimited.
|
|
* @since 1.16.0
|
|
*/
|
|
@Nullable
|
|
public Integer getMaxMembers(int rank) {
|
|
return getMaxMembers().get(rank);
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the maximum number of island members
|
|
* @param rank island rank value from {@link RanksManager}
|
|
* @param maxMembers the maxMembers to set. If null then the world default applies. Negative values = unlimited.
|
|
* @since 1.16.0
|
|
*/
|
|
public void setMaxMembers(int rank, Integer maxMembers) {
|
|
getMaxMembers().put(rank, maxMembers);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return the bonusRanges
|
|
*/
|
|
public List<BonusRangeRecord> getBonusRanges() {
|
|
if (bonusRanges == null) {
|
|
this.setBonusRanges(new ArrayList<>());
|
|
}
|
|
return bonusRanges;
|
|
}
|
|
|
|
/**
|
|
* @param bonusRanges the bonusRanges to set
|
|
*/
|
|
public void setBonusRanges(List<BonusRangeRecord> bonusRanges) {
|
|
this.bonusRanges = bonusRanges;
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Get the bonus range provided by all settings of the range giver
|
|
* @param id an id to identify this bonus
|
|
* @return bonus range, or 0 if unknown
|
|
*/
|
|
public int getBonusRange(String id) {
|
|
return this.getBonusRanges().stream().filter(r -> r.getUniqueId().equals(id)).mapToInt(BonusRangeRecord::getRange).sum();
|
|
}
|
|
|
|
/**
|
|
* Get the BonusRangeRecord for uniqueId
|
|
* @param uniqueId a unique id to identify this bonus
|
|
* @return optional BonusRangeRecord
|
|
*/
|
|
public Optional<BonusRangeRecord> getBonusRangeRecord(String uniqueId) {
|
|
return this.getBonusRanges().stream().filter(r -> r.getUniqueId().equals(uniqueId)).findFirst();
|
|
}
|
|
|
|
/**
|
|
* Add a bonus range amount to the island for this addon or plugin.
|
|
* Note, this will not replace any range set already with the same id
|
|
* @param id an id to identify this bonus
|
|
* @param range range to add to the island protected range
|
|
* @param message the reference key to a locale message related to this bonus. May be blank.
|
|
*/
|
|
public void addBonusRange(String id, int range, String message) {
|
|
this.getBonusRanges().add(new BonusRangeRecord(id, range, message));
|
|
setMaxEverProtectionRange(this.getProtectionRange());
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Clear the bonus ranges for a unique ID
|
|
* @param id id to identify this bonus
|
|
*/
|
|
public void clearBonusRange(String id) {
|
|
this.getBonusRanges().removeIf(r -> r.getUniqueId().equals(id));
|
|
setChanged();
|
|
}
|
|
|
|
/**
|
|
* Clear all bonus ranges for this island
|
|
*/
|
|
public void clearAllBonusRanges() {
|
|
this.getBonusRanges().clear();
|
|
setChanged();
|
|
}
|
|
|
|
/* (non-Javadoc)
|
|
* @see java.lang.Object#toString()
|
|
*/
|
|
@Override
|
|
public String toString() {
|
|
return "Island [changed=" + changed + ", deleted=" + deleted + ", uniqueId=" + uniqueId + ", center=" + center
|
|
+ ", location=" + location + ", range=" + range + ", protectionRange=" + protectionRange
|
|
+ ", maxEverProtectionRange=" + maxEverProtectionRange + ", world=" + world + ", gameMode=" + gameMode
|
|
+ ", name=" + name + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + ", owner="
|
|
+ owner + ", members=" + members + ", maxMembers=" + maxMembers + ", spawn=" + spawn
|
|
+ ", purgeProtected=" + purgeProtected + ", flags=" + flags + ", history=" + history
|
|
+ ", spawnPoint=" + spawnPoint + ", doNotLoad=" + doNotLoad
|
|
+ ", cooldowns=" + cooldowns + ", commandRanks=" + commandRanks + ", reserved=" + reserved
|
|
+ ", metaData=" + metaData + ", homes=" + homes + ", maxHomes=" + maxHomes + "]";
|
|
}
|
|
}
|