mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-12-28 20:17:40 +01:00
Island delete enhancements
Island deletion is done a few chunks at a time per tick. Current speed is 5 chunks per tick per world (e.g., 15 chunks per tick if nether and end islands are used). Chunks are deleted based on the all-time maximum protection range of the island. This is because the protection range can grow bigger or smaller over the island's lifetime. To ensure all possible chunks are erased, the largest every protection range is remembered and used. Very large protection ranges will take a long time to fully delete. Info on islands that are being deleted is stored in the database. If the server shuts down mid-deletion, deletion will restart when the server restarts. While an island is being deleted, new islands cannot occupy that spot and the spot cannot be reserved by the admin. In addition, async approaches to island saving and player saving were removed. Async will be implemented another way. Now, instead of saving the full island or player database, individual database entries are saved instead to be more efficient.
This commit is contained in:
parent
7a4ca8b48d
commit
501c3257ed
@ -25,6 +25,7 @@ import world.bentobox.bentobox.managers.AddonsManager;
|
||||
import world.bentobox.bentobox.managers.CommandsManager;
|
||||
import world.bentobox.bentobox.managers.FlagsManager;
|
||||
import world.bentobox.bentobox.managers.HooksManager;
|
||||
import world.bentobox.bentobox.managers.IslandDeleteManager;
|
||||
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||
import world.bentobox.bentobox.managers.IslandsManager;
|
||||
import world.bentobox.bentobox.managers.LocalesManager;
|
||||
@ -67,6 +68,8 @@ public class BentoBox extends JavaPlugin {
|
||||
|
||||
private boolean isLoaded;
|
||||
|
||||
private IslandDeleteManager islandDeletionManager;
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
if (!ServerCompatibility.getInstance().checkCompatibility(this).isCanLaunch()) {
|
||||
@ -142,10 +145,10 @@ public class BentoBox extends JavaPlugin {
|
||||
// Load islands from database - need to wait until all the worlds are loaded
|
||||
islandsManager.load();
|
||||
|
||||
// Save islands & players data asynchronously every X minutes
|
||||
// Save islands & players data every X minutes
|
||||
instance.getServer().getScheduler().runTaskTimer(instance, () -> {
|
||||
playersManager.save(true);
|
||||
islandsManager.save(true);
|
||||
playersManager.saveAll();
|
||||
islandsManager.saveAll();
|
||||
}, getSettings().getDatabaseBackupPeriod() * 20 * 60L, getSettings().getDatabaseBackupPeriod() * 20 * 60L);
|
||||
|
||||
// Make sure all flag listeners are registered.
|
||||
@ -169,13 +172,15 @@ public class BentoBox extends JavaPlugin {
|
||||
// Setup the Placeholders manager
|
||||
placeholdersManager = new PlaceholdersManager(this);
|
||||
|
||||
// Fire plugin ready event
|
||||
isLoaded = true;
|
||||
Bukkit.getServer().getPluginManager().callEvent(new BentoBoxReadyEvent());
|
||||
|
||||
// Show banner
|
||||
User.getInstance(Bukkit.getConsoleSender()).sendMessage("successfully-loaded",
|
||||
TextVariables.VERSION, instance.getDescription().getVersion(),
|
||||
"[time]", String.valueOf(System.currentTimeMillis() - startMillis));
|
||||
|
||||
// Fire plugin ready event - this should go last after everything else
|
||||
isLoaded = true;
|
||||
Bukkit.getServer().getPluginManager().callEvent(new BentoBoxReadyEvent());
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -196,6 +201,9 @@ public class BentoBox extends JavaPlugin {
|
||||
manager.registerEvents(new BannedVisitorCommands(this), this);
|
||||
// Death counter
|
||||
manager.registerEvents(new DeathListener(this), this);
|
||||
// Island Delete Manager
|
||||
islandDeletionManager = new IslandDeleteManager(this);
|
||||
manager.registerEvents(islandDeletionManager, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -356,4 +364,11 @@ public class BentoBox extends JavaPlugin {
|
||||
public PlaceholdersManager getPlaceholdersManager() {
|
||||
return placeholdersManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the islandDeletionManager
|
||||
*/
|
||||
public IslandDeleteManager getIslandDeletionManager() {
|
||||
return islandDeletionManager;
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,12 @@ public class AdminRegisterCommand extends ConfirmableCommand {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if this spot is still being deleted
|
||||
Location closestIsland = getClosestIsland(user.getLocation());
|
||||
if (getPlugin().getIslandDeletionManager().inDeletion(closestIsland)) {
|
||||
user.sendMessage("commands.admin.register.in-deletion");
|
||||
return false;
|
||||
}
|
||||
// Check if island is owned
|
||||
Optional<Island> island = getIslands().getIslandAt(user.getLocation());
|
||||
if (island.map(i -> i.getOwner() != null).orElse(false)) {
|
||||
@ -69,7 +75,7 @@ public class AdminRegisterCommand extends ConfirmableCommand {
|
||||
user.sendMessage("commands.admin.register.no-island-here");
|
||||
this.askConfirmation(user, () -> {
|
||||
// Make island here
|
||||
Island i = getIslands().createIsland(getClosestIsland(user.getLocation()), targetUUID);
|
||||
Island i = getIslands().createIsland(closestIsland, targetUUID);
|
||||
getIslands().setOwner(user, targetUUID, i);
|
||||
getWorld().getBlockAt(i.getCenter()).setType(Material.BEDROCK);
|
||||
user.sendMessage("commands.admin.register.registered-island", "[xyz]", Util.xyz(i.getCenter().toVector()));
|
||||
|
@ -1,12 +1,15 @@
|
||||
package world.bentobox.bentobox.api.commands.admin.range;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
public class AdminRangeResetCommand extends CompositeCommand {
|
||||
|
||||
@ -50,4 +53,15 @@ public class AdminRangeResetCommand extends CompositeCommand {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
if (args.isEmpty()) {
|
||||
// Don't show every player on the server. Require at least the first letter
|
||||
return Optional.empty();
|
||||
}
|
||||
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package world.bentobox.bentobox.api.commands.admin.range;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@ -9,6 +11,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
public class AdminRangeSetCommand extends CompositeCommand {
|
||||
|
||||
@ -72,4 +75,15 @@ public class AdminRangeSetCommand extends CompositeCommand {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
if (args.isEmpty()) {
|
||||
// Don't show every player on the server. Require at least the first letter
|
||||
return Optional.empty();
|
||||
}
|
||||
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ public class IslandTeamInviteAcceptCommand extends CompositeCommand {
|
||||
if (inviter != null) {
|
||||
inviter.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, user.getName());
|
||||
}
|
||||
getIslands().save(false);
|
||||
getIslands().save(island);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import world.bentobox.bentobox.api.events.IslandBaseEvent;
|
||||
import world.bentobox.bentobox.api.events.team.TeamEvent;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
public class IslandTeamSetownerCommand extends CompositeCommand {
|
||||
@ -59,9 +60,9 @@ public class IslandTeamSetownerCommand extends CompositeCommand {
|
||||
return false;
|
||||
}
|
||||
// Fire event so add-ons can run commands, etc.
|
||||
Island island = getIslands().getIsland(getWorld(), playerUUID);
|
||||
IslandBaseEvent event = TeamEvent.builder()
|
||||
.island(getIslands()
|
||||
.getIsland(getWorld(), playerUUID))
|
||||
.island(island)
|
||||
.reason(TeamEvent.Reason.SETOWNER)
|
||||
.involvedPlayer(targetUUID)
|
||||
.build();
|
||||
@ -70,7 +71,7 @@ public class IslandTeamSetownerCommand extends CompositeCommand {
|
||||
return false;
|
||||
}
|
||||
getIslands().setOwner(getWorld(), user, targetUUID);
|
||||
getIslands().save(true);
|
||||
getIslands().save(island);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class IslandBaseEvent extends PremadeEvent implements Cancellable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the island involved in this event
|
||||
* @return the island involved in this event. This may be null in the case of deleted islands, so use location instead
|
||||
*/
|
||||
public Island getIsland(){
|
||||
return island;
|
||||
|
@ -6,6 +6,7 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import world.bentobox.bentobox.api.events.IslandBaseEvent;
|
||||
import world.bentobox.bentobox.database.objects.DeletedIslandDO;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
|
||||
@ -58,9 +59,14 @@ public class IslandEvent extends IslandBaseEvent {
|
||||
*/
|
||||
CREATED,
|
||||
/**
|
||||
* Fired just before any island chunks are to be deleted.
|
||||
* Fired when an island is to be deleted. Note an island can be deleted without having
|
||||
* chunks removed.
|
||||
*/
|
||||
DELETE,
|
||||
/**
|
||||
* Fired when island chunks are going to be deleted
|
||||
*/
|
||||
DELETE_CHUNKS,
|
||||
/**
|
||||
* Fired after all island chunks have been deleted or set for regeneration by the server
|
||||
*/
|
||||
@ -168,14 +174,39 @@ public class IslandEvent extends IslandBaseEvent {
|
||||
super(island, player, admin, location);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Fired when an island chunks are going to be deleted.
|
||||
* May be cancelled.
|
||||
*
|
||||
*/
|
||||
public static class IslandDeleteChunksEvent extends IslandBaseEvent {
|
||||
private final DeletedIslandDO deletedIslandInfo;
|
||||
|
||||
private IslandDeleteChunksEvent(Island island, UUID player, boolean admin, Location location, DeletedIslandDO deletedIsland) {
|
||||
// Final variables have to be declared in the constructor
|
||||
super(island, player, admin, location);
|
||||
this.deletedIslandInfo = deletedIsland;
|
||||
}
|
||||
|
||||
public DeletedIslandDO getDeletedIslandInfo() {
|
||||
return deletedIslandInfo;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Fired when an island is deleted.
|
||||
*
|
||||
*/
|
||||
public static class IslandDeletedEvent extends IslandBaseEvent {
|
||||
private IslandDeletedEvent(Island island, UUID player, boolean admin, Location location) {
|
||||
private final DeletedIslandDO deletedIslandInfo;
|
||||
|
||||
private IslandDeletedEvent(Island island, UUID player, boolean admin, Location location, DeletedIslandDO deletedIsland) {
|
||||
// Final variables have to be declared in the constructor
|
||||
super(island, player, admin, location);
|
||||
this.deletedIslandInfo = deletedIsland;
|
||||
}
|
||||
|
||||
public DeletedIslandDO getDeletedIslandInfo() {
|
||||
return deletedIslandInfo;
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -256,6 +287,7 @@ public class IslandEvent extends IslandBaseEvent {
|
||||
private Reason reason = Reason.UNKNOWN;
|
||||
private boolean admin;
|
||||
private Location location;
|
||||
private DeletedIslandDO deletedIslandInfo;
|
||||
|
||||
public IslandEventBuilder island(Island island) {
|
||||
this.island = island;
|
||||
@ -295,6 +327,11 @@ public class IslandEvent extends IslandBaseEvent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public IslandEventBuilder deletedIslandInfo(DeletedIslandDO deletedIslandInfo) {
|
||||
this.deletedIslandInfo = deletedIslandInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IslandBaseEvent build() {
|
||||
// Call the generic event for developers who just want one event and use the Reason enum
|
||||
Bukkit.getServer().getPluginManager().callEvent(new IslandEvent(island, player, admin, location, reason));
|
||||
@ -316,8 +353,12 @@ public class IslandEvent extends IslandBaseEvent {
|
||||
IslandDeleteEvent delete = new IslandDeleteEvent(island, player, admin, location);
|
||||
Bukkit.getServer().getPluginManager().callEvent(delete);
|
||||
return delete;
|
||||
case DELETE_CHUNKS:
|
||||
IslandDeleteChunksEvent deleteChunks = new IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo);
|
||||
Bukkit.getServer().getPluginManager().callEvent(deleteChunks);
|
||||
return deleteChunks;
|
||||
case DELETED:
|
||||
IslandDeletedEvent deleted = new IslandDeletedEvent(island, player, admin, location);
|
||||
IslandDeletedEvent deleted = new IslandDeletedEvent(island, player, admin, location, deletedIslandInfo);
|
||||
Bukkit.getServer().getPluginManager().callEvent(deleted);
|
||||
return deleted;
|
||||
case ENTER:
|
||||
|
@ -110,4 +110,11 @@ public abstract class AbstractDatabaseHandler<T> {
|
||||
* Closes the database
|
||||
*/
|
||||
public abstract void close();
|
||||
|
||||
/**
|
||||
* Attempts to delete the object with the uniqueId
|
||||
* @param uniqueId - uniqueId of object
|
||||
* @return true if successful, false if there is no such uniqueId
|
||||
*/
|
||||
public abstract boolean deleteID(String uniqueId);
|
||||
}
|
||||
|
@ -100,6 +100,15 @@ public class Database<T> {
|
||||
return handler.objectExists(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to delete the object with the uniqueId
|
||||
* @param uniqueId - uniqueId of object
|
||||
* @return true if successful, false if there is no such uniqueId
|
||||
*/
|
||||
public boolean deleteID(String uniqueId) {
|
||||
return handler.deleteID(uniqueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete object from database
|
||||
* @param object - object to delete
|
||||
@ -120,4 +129,6 @@ public class Database<T> {
|
||||
handler.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -122,6 +122,28 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteID(String uniqueId) {
|
||||
// The filename of the JSON file is the value of uniqueId field plus .json. Sometimes the .json is already appended.
|
||||
if (!uniqueId.endsWith(JSON)) {
|
||||
uniqueId = uniqueId + JSON;
|
||||
}
|
||||
// Get the database and table folders
|
||||
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
|
||||
File tableFolder = new File(dataFolder, dataObject.getSimpleName());
|
||||
if (tableFolder.exists()) {
|
||||
// Obtain the file and delete it
|
||||
File file = new File(tableFolder, uniqueId);
|
||||
try {
|
||||
Files.delete(file.toPath());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
plugin.logError("Could not delete json database object! " + file.getName() + " - " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
// Null check
|
||||
@ -137,25 +159,7 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
// Obtain the value of uniqueId within the instance (which must be a DataObject)
|
||||
PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject);
|
||||
Method method = propertyDescriptor.getReadMethod();
|
||||
String fileName = (String) method.invoke(instance);
|
||||
|
||||
// The filename of the JSON file is the value of uniqueId field plus .json. Sometimes the .json is already appended.
|
||||
if (!fileName.endsWith(JSON)) {
|
||||
fileName = fileName + JSON;
|
||||
}
|
||||
|
||||
// Get the database and table folders
|
||||
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
|
||||
File tableFolder = new File(dataFolder, dataObject.getSimpleName());
|
||||
if (tableFolder.exists()) {
|
||||
// Obtain the file and delete it
|
||||
File file = new File(tableFolder, fileName);
|
||||
try {
|
||||
Files.delete(file.toPath());
|
||||
} catch (IOException e) {
|
||||
plugin.logError("Could not delete json database object! " + file.getName() + " - " + e.getMessage());
|
||||
}
|
||||
}
|
||||
deleteID((String) method.invoke(instance));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,6 +106,16 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteID(String uniqueId) {
|
||||
try {
|
||||
return collection.findOneAndDelete(new Document(MONGO_ID, uniqueId)) == null ? false : true;
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(T instance) {
|
||||
// Null check
|
||||
@ -117,11 +127,7 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
collection.findOneAndDelete(new Document(MONGO_ID, ((DataObject)instance).getUniqueId()));
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
deleteID(((DataObject)instance).getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,6 +141,22 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteID(String uniqueId) {
|
||||
String sb = "DELETE FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE uniqueId = ?";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
preparedStatement.execute();
|
||||
return preparedStatement.getUpdateCount() > 0;
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(T instance) {
|
||||
// Null check
|
||||
@ -152,15 +168,9 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
String sb = "DELETE FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE uniqueId = ?";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
try {
|
||||
Method getUniqueId = dataObject.getMethod("getUniqueId");
|
||||
String uniqueId = (String) getUniqueId.invoke(instance);
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
preparedStatement.execute();
|
||||
deleteID((String) getUniqueId.invoke(instance));
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package world.bentobox.bentobox.database.objects;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
* Data object to store islands in deletion
|
||||
*
|
||||
@ -17,7 +17,7 @@ public class DeletedIslandDO implements DataObject {
|
||||
private String uniqueId = "";
|
||||
|
||||
@Expose
|
||||
private World world;
|
||||
private Location location;
|
||||
|
||||
@Expose
|
||||
private int minXChunk;
|
||||
@ -33,115 +33,24 @@ public class DeletedIslandDO implements DataObject {
|
||||
|
||||
public DeletedIslandDO() {}
|
||||
|
||||
public DeletedIslandDO(Location location, int minXChunk, int maxXChunk, int minZChunk, int maxZChunk) {
|
||||
this.uniqueId = Util.getStringLocation(location);
|
||||
this.world = location.getWorld();
|
||||
this.minXChunk = minXChunk;
|
||||
this.maxXChunk = maxXChunk;
|
||||
this.minZChunk = minZChunk;
|
||||
this.maxZChunk = maxZChunk;
|
||||
}
|
||||
|
||||
public DeletedIslandDO(Island island) {
|
||||
uniqueId = Util.getStringLocation(island.getCenter());
|
||||
world = island.getCenter().getWorld();
|
||||
minXChunk = island.getMinX() >> 4;
|
||||
maxXChunk = (island.getRange() * 2 + island.getMinX() - 1) >> 4;
|
||||
minZChunk = island.getMinZ() >> 4;
|
||||
maxZChunk = (island.getRange() * 2 + island.getMinZ() - 1) >> 4;
|
||||
uniqueId = UUID.randomUUID().toString();
|
||||
location = island.getCenter();
|
||||
minXChunk = (location.getBlockX() - island.getMaxEverProtectionRange()) >> 4;
|
||||
maxXChunk = (island.getMaxEverProtectionRange() + location.getBlockX() - 1) >> 4;
|
||||
minZChunk = (location.getBlockZ() - island.getMaxEverProtectionRange()) >> 4;
|
||||
maxZChunk = (island.getMaxEverProtectionRange() + location.getBlockZ() - 1) >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueId() {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUniqueId(String uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the world
|
||||
*/
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minXChunk
|
||||
*/
|
||||
public int getMinXChunk() {
|
||||
return minXChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maxXChunk
|
||||
*/
|
||||
public int getMaxXChunk() {
|
||||
return maxXChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minZChunk
|
||||
*/
|
||||
public int getMinZChunk() {
|
||||
return minZChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maxZChunk
|
||||
*/
|
||||
public int getMaxZChunk() {
|
||||
return maxZChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param world the world to set
|
||||
*/
|
||||
public void setWorld(World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minXChunk the minXChunk to set
|
||||
*/
|
||||
public void setMinXChunk(int minXChunk) {
|
||||
public DeletedIslandDO(Location location, int minXChunk, int maxXChunk, int minZChunk, int maxZChunk) {
|
||||
this.uniqueId = UUID.randomUUID().toString();
|
||||
this.location = location;
|
||||
this.minXChunk = minXChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxXChunk the maxXChunk to set
|
||||
*/
|
||||
public void setMaxXChunk(int maxXChunk) {
|
||||
this.maxXChunk = maxXChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minZChunk the minZChunk to set
|
||||
*/
|
||||
public void setMinZChunk(int minZChunk) {
|
||||
this.minZChunk = minZChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxZChunk the maxZChunk to set
|
||||
*/
|
||||
public void setMaxZChunk(int maxZChunk) {
|
||||
this.maxZChunk = maxZChunk;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@ -167,6 +76,104 @@ public class DeletedIslandDO implements DataObject {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the location
|
||||
*/
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maxXChunk
|
||||
*/
|
||||
public int getMaxXChunk() {
|
||||
return maxXChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maxZChunk
|
||||
*/
|
||||
public int getMaxZChunk() {
|
||||
return maxZChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minXChunk
|
||||
*/
|
||||
public int getMinXChunk() {
|
||||
return minXChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minZChunk
|
||||
*/
|
||||
public int getMinZChunk() {
|
||||
return minZChunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueId() {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the world
|
||||
*/
|
||||
public World getWorld() {
|
||||
return location.getWorld();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param location the location to set
|
||||
*/
|
||||
public void setLocation(Location location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxXChunk the maxXChunk to set
|
||||
*/
|
||||
public void setMaxXChunk(int maxXChunk) {
|
||||
this.maxXChunk = maxXChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxZChunk the maxZChunk to set
|
||||
*/
|
||||
public void setMaxZChunk(int maxZChunk) {
|
||||
this.maxZChunk = maxZChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minXChunk the minXChunk to set
|
||||
*/
|
||||
public void setMinXChunk(int minXChunk) {
|
||||
this.minXChunk = minXChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minZChunk the minZChunk to set
|
||||
*/
|
||||
public void setMinZChunk(int minZChunk) {
|
||||
this.minZChunk = minZChunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUniqueId(String uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -24,11 +24,11 @@ import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.configuration.WorldSettings;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.logs.LogEntry;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.adapters.Adapter;
|
||||
import world.bentobox.bentobox.database.objects.adapters.FlagSerializer;
|
||||
import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter;
|
||||
import world.bentobox.bentobox.api.logs.LogEntry;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Pair;
|
||||
@ -60,6 +60,10 @@ public class Island implements DataObject {
|
||||
@Expose
|
||||
private int protectionRange;
|
||||
|
||||
// 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;
|
||||
@ -113,6 +117,7 @@ public class Island implements DataObject {
|
||||
center = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ());
|
||||
range = BentoBox.getInstance().getIWM().getIslandDistance(world);
|
||||
this.protectionRange = protectionRange;
|
||||
this.maxEverProtectionRange = protectionRange;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,6 +289,20 @@ public class Island implements DataObject {
|
||||
return protectionRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maxEverProtectionRange or the protection range, whichever is larger
|
||||
*/
|
||||
public int getMaxEverProtectionRange() {
|
||||
return Math.max(protectionRange, maxEverProtectionRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxEverProtectionRange the maxEverProtectionRange to set
|
||||
*/
|
||||
public void setMaxEverProtectionRange(int maxEverProtectionRange) {
|
||||
this.maxEverProtectionRange = maxEverProtectionRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the island is protected from the Purge, otherwise false
|
||||
*/
|
||||
@ -533,6 +552,10 @@ public class Island implements DataObject {
|
||||
*/
|
||||
public void setProtectionRange(int protectionRange) {
|
||||
this.protectionRange = protectionRange;
|
||||
// Ratchet up the maximum protection range
|
||||
if (protectionRange > this.maxEverProtectionRange) {
|
||||
this.maxEverProtectionRange = protectionRange;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -660,14 +683,14 @@ public class Island implements DataObject {
|
||||
// Fixes #getLastPlayed() returning 0 when it is the owner's first connection.
|
||||
long lastPlayed = (plugin.getServer().getOfflinePlayer(owner).getLastPlayed() != 0) ?
|
||||
plugin.getServer().getOfflinePlayer(owner).getLastPlayed() : plugin.getServer().getOfflinePlayer(owner).getFirstPlayed();
|
||||
user.sendMessage("commands.admin.info.last-login","[date]", new Date(lastPlayed).toString());
|
||||
user.sendMessage("commands.admin.info.last-login","[date]", new Date(lastPlayed).toString());
|
||||
|
||||
user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner)));
|
||||
String resets = String.valueOf(plugin.getPlayers().getResets(world, owner));
|
||||
String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world));
|
||||
user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total);
|
||||
// Show team members
|
||||
showMembers(user);
|
||||
user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner)));
|
||||
String resets = String.valueOf(plugin.getPlayers().getResets(world, owner));
|
||||
String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world));
|
||||
user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total);
|
||||
// Show team members
|
||||
showMembers(user);
|
||||
}
|
||||
Vector location = center.toVector();
|
||||
user.sendMessage("commands.admin.info.island-location", "[xyz]", Util.xyz(location));
|
||||
@ -675,6 +698,7 @@ public class Island implements DataObject {
|
||||
Vector to = center.toVector().add(new Vector(range-1, 0, range-1)).setY(center.getWorld().getMaxHeight());
|
||||
user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(from), "[xz2]", Util.xyz(to));
|
||||
user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(protectionRange));
|
||||
user.sendMessage("commands.admin.info.max-protection-range", "[range]", String.valueOf(maxEverProtectionRange));
|
||||
Vector pfrom = center.toVector().subtract(new Vector(protectionRange, 0, protectionRange)).setY(0);
|
||||
Vector pto = center.toVector().add(new Vector(protectionRange-1, 0, protectionRange-1)).setY(center.getWorld().getMaxHeight());
|
||||
user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(pfrom), "[xz2]", Util.xyz(pto));
|
||||
|
@ -553,6 +553,28 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteID(String uniqueId) {
|
||||
// The filename of the YAML file is the value of uniqueId field plus .yml. Sometimes the .yml is already appended.
|
||||
if (!uniqueId.endsWith(YML)) {
|
||||
uniqueId = uniqueId + YML;
|
||||
}
|
||||
// Get the database and table folders
|
||||
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
|
||||
File tableFolder = new File(dataFolder, dataObject.getSimpleName());
|
||||
if (tableFolder.exists()) {
|
||||
// Obtain the file and delete it
|
||||
File file = new File(tableFolder, uniqueId);
|
||||
try {
|
||||
Files.delete(file.toPath());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
plugin.logError("Could not delete yml database object! " + file.getName() + " - " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#deleteObject(java.lang.Object)
|
||||
*/
|
||||
@ -571,23 +593,8 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
// Obtain the value of uniqueId within the instance (which must be a DataObject)
|
||||
PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject);
|
||||
Method method = propertyDescriptor.getReadMethod();
|
||||
String fileName = (String) method.invoke(instance);
|
||||
// The filename of the YAML file is the value of uniqueId field plus .yml. Sometimes the .yml is already appended.
|
||||
if (!fileName.endsWith(YML)) {
|
||||
fileName = fileName + YML;
|
||||
}
|
||||
// Get the database and table folders
|
||||
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
|
||||
File tableFolder = new File(dataFolder, dataObject.getSimpleName());
|
||||
if (tableFolder.exists()) {
|
||||
// Obtain the file and delete it
|
||||
File file = new File(tableFolder, fileName);
|
||||
try {
|
||||
Files.delete(file.toPath());
|
||||
} catch (IOException e) {
|
||||
plugin.logError("Could not delete yml database object! " + file.getName() + " - " + e.getMessage());
|
||||
}
|
||||
}
|
||||
deleteID((String) method.invoke(instance));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,84 @@
|
||||
package world.bentobox.bentobox.managers;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.events.BentoBoxReadyEvent;
|
||||
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeleteChunksEvent;
|
||||
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeletedEvent;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.bentobox.database.objects.DeletedIslandDO;
|
||||
import world.bentobox.bentobox.util.DeleteIslandChunks;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
* Listens for island deletions and adds them to the database. Removes them when the island is deleted.
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class IslandDeleteManager implements Listener {
|
||||
|
||||
/**
|
||||
* Queue of islands to delete
|
||||
*/
|
||||
private BentoBox plugin;
|
||||
private Database<DeletedIslandDO> handler;
|
||||
private Set<Location> inDeletion;
|
||||
|
||||
|
||||
public IslandDeleteManager(BentoBox plugin) {
|
||||
this.plugin = plugin;
|
||||
handler = new Database<>(plugin, DeletedIslandDO.class);
|
||||
inDeletion = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* When BentoBox is fully loaded, load the islands that still need to be deleted and kick them off
|
||||
* @param e
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBentoBoxReady(BentoBoxReadyEvent e) {
|
||||
// Load list of islands that were mid deletion and delete them
|
||||
List<DeletedIslandDO> toBeDeleted = handler.loadObjects();
|
||||
if (toBeDeleted != null && toBeDeleted.size() > 0) {
|
||||
plugin.log("There are " + toBeDeleted.size() + " islands pending deletion.");
|
||||
toBeDeleted.forEach(di -> {
|
||||
plugin.log("Resuming deletion of island at " + di.getLocation().getWorld().getName() + " " + Util.xyz(di.getLocation().toVector()));
|
||||
inDeletion.add(di.getLocation());
|
||||
new DeleteIslandChunks(plugin, di);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onIslandDelete(IslandDeleteChunksEvent e) {
|
||||
// Store location
|
||||
inDeletion.add(e.getDeletedIslandInfo().getLocation());
|
||||
// Save to database
|
||||
handler.saveObject(e.getDeletedIslandInfo());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onIslandDeleted(IslandDeletedEvent e) {
|
||||
// Delete
|
||||
inDeletion.remove(e.getDeletedIslandInfo().getLocation());
|
||||
// Delete from database
|
||||
handler.deleteID(e.getDeletedIslandInfo().getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an island location is in deletion
|
||||
* @param location - center of location
|
||||
* @return true if island is in the process of being deleted
|
||||
*/
|
||||
public boolean inDeletion(Location location) {
|
||||
return inDeletion.contains(location);
|
||||
}
|
||||
}
|
@ -64,9 +64,6 @@ public class IslandsManager {
|
||||
// Island Cache
|
||||
private IslandCache islandCache;
|
||||
|
||||
// Async database saving semaphore
|
||||
private boolean midSave;
|
||||
|
||||
/**
|
||||
* Islands Manager
|
||||
* @param plugin - plugin
|
||||
@ -694,8 +691,7 @@ public class IslandsManager {
|
||||
* @param user - user
|
||||
*/
|
||||
public void removePlayer(World world, User user) {
|
||||
islandCache.removePlayer(world, user.getUniqueId());
|
||||
save(true);
|
||||
removePlayer(world, user.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -704,8 +700,10 @@ public class IslandsManager {
|
||||
* @param uuid - user's uuid
|
||||
*/
|
||||
public void removePlayer(World world, UUID uuid) {
|
||||
islandCache.removePlayer(world, uuid);
|
||||
save(true);
|
||||
Island island = islandCache.removePlayer(world, uuid);
|
||||
if (island != null) {
|
||||
handler.saveObject(island);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -736,36 +734,18 @@ public class IslandsManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the islands to the database
|
||||
* @param async - if true, saving will be done async
|
||||
* Save the all the islands to the database
|
||||
*/
|
||||
public void save(boolean async){
|
||||
if (midSave) {
|
||||
// If it's already saving, then do nothing
|
||||
return;
|
||||
}
|
||||
public void saveAll(){
|
||||
Collection<Island> collection = islandCache.getIslands();
|
||||
if(async) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
midSave = true;
|
||||
for(Island island : collection){
|
||||
try {
|
||||
handler.saveObject(island);
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not save island to database when running async! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
midSave = false;
|
||||
});
|
||||
} else {
|
||||
for(Island island : collection){
|
||||
try {
|
||||
handler.saveObject(island);
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not save island to database when running sync! " + e.getMessage());
|
||||
}
|
||||
for(Island island : collection){
|
||||
try {
|
||||
handler.saveObject(island);
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not save island to database when running sync! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -777,8 +757,8 @@ public class IslandsManager {
|
||||
// Add player to new island
|
||||
teamIsland.addMember(playerUUID);
|
||||
islandCache.addPlayer(playerUUID, teamIsland);
|
||||
// Save the database
|
||||
save(false);
|
||||
// Save the island
|
||||
handler.saveObject(teamIsland);
|
||||
}
|
||||
|
||||
public void setLast(Location last) {
|
||||
@ -798,7 +778,7 @@ public class IslandsManager {
|
||||
public void shutdown(){
|
||||
// Remove all coop associations
|
||||
islandCache.getIslands().stream().forEach(i -> i.getMembers().values().removeIf(p -> p == RanksManager.COOP_RANK));
|
||||
save(false);
|
||||
saveAll();
|
||||
islandCache.clear();
|
||||
handler.close();
|
||||
}
|
||||
@ -876,4 +856,13 @@ public class IslandsManager {
|
||||
islandCache.getIslands().stream().forEach(i -> i.getMembers().entrySet().removeIf(e -> e.getKey().equals(uniqueId) && e.getValue() == rank));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the island to the database
|
||||
* @param island - island
|
||||
*/
|
||||
public void save(Island island) {
|
||||
handler.saveObject(island);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package world.bentobox.bentobox.managers;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -8,7 +7,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
@ -63,20 +61,13 @@ public class PlayersManager {
|
||||
|
||||
/**
|
||||
* Save all players
|
||||
* @param async - if true, save async
|
||||
*/
|
||||
public void save(boolean async){
|
||||
Collection<Players> set = Collections.unmodifiableCollection(playerCache.values());
|
||||
if(async) {
|
||||
Runnable save = () -> set.forEach(handler::saveObject);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, save);
|
||||
} else {
|
||||
set.forEach(handler::saveObject);
|
||||
}
|
||||
public void saveAll(){
|
||||
Collections.unmodifiableCollection(playerCache.values()).forEach(handler::saveObject);
|
||||
}
|
||||
|
||||
public void shutdown(){
|
||||
save(false);
|
||||
saveAll();
|
||||
playerCache.clear();
|
||||
handler.close();
|
||||
}
|
||||
|
@ -169,8 +169,9 @@ public class IslandCache {
|
||||
* The island is removed from the islandsByUUID map, but kept in the location map.
|
||||
* @param world - world
|
||||
* @param uuid - player's UUID
|
||||
* @return island player had or null if none
|
||||
*/
|
||||
public void removePlayer(World world, UUID uuid) {
|
||||
public Island removePlayer(World world, UUID uuid) {
|
||||
world = Util.getWorld(world);
|
||||
islandsByUUID.putIfAbsent(world, new HashMap<>());
|
||||
Island island = islandsByUUID.get(world).get(uuid);
|
||||
@ -185,6 +186,7 @@ public class IslandCache {
|
||||
}
|
||||
}
|
||||
islandsByUUID.get(world).remove(uuid);
|
||||
return island;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,7 +244,7 @@ public class NewIsland {
|
||||
*/
|
||||
private Result isIsland(Location location){
|
||||
location = Util.getClosestIsland(location);
|
||||
if (plugin.getIslands().getIslandAt(location).isPresent()) {
|
||||
if (plugin.getIslands().getIslandAt(location).isPresent() || plugin.getIslandDeletionManager().inDeletion(location)) {
|
||||
return Result.ISLAND_FOUND;
|
||||
}
|
||||
|
||||
|
@ -16,34 +16,43 @@ import world.bentobox.bentobox.database.objects.DeletedIslandDO;
|
||||
*/
|
||||
public class DeleteIslandChunks {
|
||||
|
||||
/**
|
||||
* This is how many chunks per world will be done in one tick.
|
||||
*/
|
||||
private final static int SPEED = 5;
|
||||
private int x;
|
||||
private int z;
|
||||
private BukkitTask task;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public DeleteIslandChunks(BentoBox plugin, DeletedIslandDO di) {
|
||||
// Fire event
|
||||
IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETE_CHUNKS).build();
|
||||
x = di.getMinXChunk();
|
||||
z = di.getMinZChunk();
|
||||
task = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
||||
di.getWorld().regenerateChunk(x, z);
|
||||
//System.out.println("regenerating = " + x + "," + z);
|
||||
if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) {
|
||||
plugin.getIWM().getNetherWorld(di.getWorld()).regenerateChunk(x, z);
|
||||
for (int i = 0; i < SPEED; i++) {
|
||||
di.getWorld().regenerateChunk(x, z);
|
||||
if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) {
|
||||
plugin.getIWM().getNetherWorld(di.getWorld()).regenerateChunk(x, z);
|
||||
|
||||
}
|
||||
if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) {
|
||||
plugin.getIWM().getEndWorld(di.getWorld()).regenerateChunk(x, z);
|
||||
}
|
||||
z++;
|
||||
if (z > di.getMaxZChunk()) {
|
||||
z = di.getMinZChunk();
|
||||
x++;
|
||||
if (x > di.getMaxXChunk()) {
|
||||
task.cancel();
|
||||
// Fire event
|
||||
IslandEvent.builder().location(Util.getLocationString(di.getUniqueId())).reason(Reason.DELETED).build();
|
||||
}
|
||||
if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) {
|
||||
plugin.getIWM().getEndWorld(di.getWorld()).regenerateChunk(x, z);
|
||||
}
|
||||
z++;
|
||||
if (z > di.getMaxZChunk()) {
|
||||
z = di.getMinZChunk();
|
||||
x++;
|
||||
if (x > di.getMaxXChunk()) {
|
||||
task.cancel();
|
||||
// Fire event
|
||||
IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0L, 1L);
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
@ -109,6 +109,7 @@ commands:
|
||||
registered-island: "&aRegistered player to island at [xyz]."
|
||||
already-owned: "&cIsland is already owned by another player!"
|
||||
no-island-here: "&cThere is no island here. Confirm to make one."
|
||||
in-deletion: "&cThis island space is currently being deleted. Try later."
|
||||
unregister:
|
||||
parameters: "<owner>"
|
||||
description: "unregister owner from island, but keep island blocks"
|
||||
@ -128,6 +129,7 @@ commands:
|
||||
island-location: "Island location: [xyz]"
|
||||
island-coords: "Island coordinates: [xz1] to [xz2]"
|
||||
protection-range: "Protection range: [range]"
|
||||
max-protection-range: "Largest historical protection range: [range]"
|
||||
protection-coords: "Protection coordinates: [xz1] to [xz2]"
|
||||
is-spawn: "Island is a spawn island"
|
||||
banned-players: "Banned players:"
|
||||
|
@ -30,6 +30,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.managers.CommandsManager;
|
||||
import world.bentobox.bentobox.managers.IslandDeleteManager;
|
||||
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||
import world.bentobox.bentobox.managers.IslandsManager;
|
||||
import world.bentobox.bentobox.managers.LocalesManager;
|
||||
@ -50,6 +51,7 @@ public class AdminRegisterCommandTest {
|
||||
private IslandsManager im;
|
||||
private PlayersManager pm;
|
||||
private UUID notUUID;
|
||||
private IslandDeleteManager idm;
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
@ -111,11 +113,16 @@ public class AdminRegisterCommandTest {
|
||||
LocalesManager lm = mock(LocalesManager.class);
|
||||
when(lm.get(Mockito.any(), Mockito.any())).thenReturn("mock translation");
|
||||
when(plugin.getLocalesManager()).thenReturn(lm);
|
||||
|
||||
// Deletion Manager
|
||||
idm = mock(IslandDeleteManager.class);
|
||||
when(idm.inDeletion(Mockito.any())).thenReturn(false);
|
||||
when(plugin.getIslandDeletionManager()).thenReturn(idm);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test method for .
|
||||
* Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteNoTarget() {
|
||||
@ -125,7 +132,7 @@ public class AdminRegisterCommandTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for .
|
||||
* Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteUnknownPlayer() {
|
||||
@ -137,7 +144,7 @@ public class AdminRegisterCommandTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for .
|
||||
* Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testExecutePlayerHasIsland() {
|
||||
@ -151,7 +158,7 @@ public class AdminRegisterCommandTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for .
|
||||
* Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteInTeam() {
|
||||
@ -165,7 +172,7 @@ public class AdminRegisterCommandTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for .
|
||||
* Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteAlreadyOwnedIsland() {
|
||||
@ -186,6 +193,32 @@ public class AdminRegisterCommandTest {
|
||||
Mockito.verify(user).sendMessage("commands.admin.register.already-owned");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteInDeletionIsland() {
|
||||
when(idm.inDeletion(Mockito.any())).thenReturn(true);
|
||||
when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false);
|
||||
when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false);
|
||||
String[] name = {"tastybento"};
|
||||
when(pm.getUUID(Mockito.any())).thenReturn(notUUID);
|
||||
Location loc = mock(Location.class);
|
||||
|
||||
// Island has owner
|
||||
Island is = mock(Island.class);
|
||||
when(is.getOwner()).thenReturn(uuid);
|
||||
Optional<Island> opi = Optional.of(is);
|
||||
when(im.getIslandAt(Mockito.any())).thenReturn(opi);
|
||||
when(user.getLocation()).thenReturn(loc);
|
||||
AdminRegisterCommand itl = new AdminRegisterCommand(ac);
|
||||
assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name)));
|
||||
Mockito.verify(user).sendMessage("commands.admin.register.in-deletion");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link AdminRegisterCommand#execute(org.bukkit.command.CommandSender, String, String[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteSuccess() {
|
||||
when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(false);
|
||||
|
@ -23,6 +23,7 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
@ -37,6 +38,7 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Slime;
|
||||
import org.bukkit.entity.Wither;
|
||||
import org.bukkit.entity.Zombie;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -54,6 +56,7 @@ import com.google.common.collect.ImmutableSet.Builder;
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.Settings;
|
||||
import world.bentobox.bentobox.api.configuration.WorldSettings;
|
||||
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeleteEvent;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
@ -79,6 +82,7 @@ public class IslandsManagerTest {
|
||||
private IslandCache islandCache;
|
||||
private Optional<Island> optionalIsland;
|
||||
private Island is;
|
||||
private PluginManager pim;
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
@ -122,8 +126,7 @@ public class IslandsManagerTest {
|
||||
pm = mock(PlayersManager.class);
|
||||
when(plugin.getPlayers()).thenReturn(pm);
|
||||
|
||||
// Server & Scheduler
|
||||
|
||||
// Scheduler
|
||||
BukkitScheduler sch = mock(BukkitScheduler.class);
|
||||
PowerMockito.mockStatic(Bukkit.class);
|
||||
when(Bukkit.getScheduler()).thenReturn(sch);
|
||||
@ -174,6 +177,11 @@ public class IslandsManagerTest {
|
||||
// User location
|
||||
when(user.getLocation()).thenReturn(location);
|
||||
|
||||
// Server for events
|
||||
Server server = mock(Server.class);
|
||||
when(Bukkit.getServer()).thenReturn(server);
|
||||
pim = mock(PluginManager.class);
|
||||
when(server.getPluginManager()).thenReturn(pim);
|
||||
}
|
||||
|
||||
|
||||
@ -432,16 +440,37 @@ public class IslandsManagerTest {
|
||||
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#deleteIsland(world.bentobox.bentobox.database.objects.Island, boolean)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteIslandIslandBoolean() {
|
||||
public void testDeleteIslandIslandBooleanNull() {
|
||||
IslandsManager im = new IslandsManager(plugin);
|
||||
|
||||
im.deleteIsland((Island)null, true);
|
||||
Mockito.verify(pim, Mockito.never()).callEvent(Mockito.any());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#deleteIsland(world.bentobox.bentobox.database.objects.Island, boolean)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteIslandIslandBooleanNoBlockRemoval() {
|
||||
IslandsManager im = new IslandsManager(plugin);
|
||||
UUID owner = UUID.randomUUID();
|
||||
Island island = im.createIsland(location, owner);
|
||||
im.deleteIsland(island, false);
|
||||
island = im.createIsland(location, owner);
|
||||
assertNull(island.getOwner());
|
||||
Mockito.verify(pim, Mockito.times(2)).callEvent(Mockito.any(IslandDeleteEvent.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#deleteIsland(world.bentobox.bentobox.database.objects.Island, boolean)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteIslandIslandBooleanRemoveBlocks() {
|
||||
Mockito.verify(pim, Mockito.never()).callEvent(Mockito.any());
|
||||
IslandsManager im = new IslandsManager(plugin);
|
||||
UUID owner = UUID.randomUUID();
|
||||
Island island = im.createIsland(location, owner);
|
||||
im.deleteIsland(island, true);
|
||||
assertNull(island);
|
||||
assertNull(island.getOwner());
|
||||
Mockito.verify(pim, Mockito.times(4)).callEvent(Mockito.any(IslandDeleteEvent.class));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,13 +175,12 @@ public class PlayersManagerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.managers.PlayersManager#save(boolean)}.
|
||||
* Test method for {@link world.bentobox.bentobox.managers.PlayersManager#saveAll()}.
|
||||
*/
|
||||
@Test
|
||||
public void testSaveBoolean() {
|
||||
PlayersManager pm = new PlayersManager(plugin);
|
||||
pm.save(false);
|
||||
pm.save(true);
|
||||
pm.saveAll();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,152 +0,0 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package world.bentobox.bentobox.util;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||
|
||||
/**
|
||||
* Tests the island delete class
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({Bukkit.class})
|
||||
public class DeleteIslandChunksTest {
|
||||
|
||||
private BentoBox plugin;
|
||||
private Island island;
|
||||
private Location location;
|
||||
private World world;
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
PowerMockito.mockStatic(Bukkit.class);
|
||||
Server server = mock(Server.class);
|
||||
PluginManager pim = mock(PluginManager.class);
|
||||
when(server.getPluginManager()).thenReturn(pim);
|
||||
when(Bukkit.getServer()).thenReturn(server);
|
||||
plugin = mock(BentoBox.class);
|
||||
IslandWorldManager iwm = mock(IslandWorldManager.class);
|
||||
// No Nether or End by default
|
||||
when(iwm.isNetherGenerate(Mockito.any())).thenReturn(false);
|
||||
when(iwm.isNetherIslands(Mockito.any())).thenReturn(false);
|
||||
when(iwm.isEndGenerate(Mockito.any())).thenReturn(false);
|
||||
when(iwm.isEndIslands(Mockito.any())).thenReturn(false);
|
||||
|
||||
when(plugin.getIWM()).thenReturn(iwm);
|
||||
// World
|
||||
//world = mock(World.class, Mockito.withSettings().verboseLogging());
|
||||
world = mock(World.class);
|
||||
|
||||
island = new Island();
|
||||
island.setRange(64);
|
||||
|
||||
location = mock(Location.class);
|
||||
|
||||
when(location.getWorld()).thenReturn(world);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.util.DeleteIslandChunks#DeleteIslandChunks(world.bentobox.bentobox.BentoBox, world.bentobox.bentobox.database.objects.Island)}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testDeleteIslandChunksNegativeX() {
|
||||
|
||||
// Island adjacent to an island at 0,0
|
||||
Location location2 = mock(Location.class);
|
||||
|
||||
when(location2.getWorld()).thenReturn(world);
|
||||
when(location2.getBlockX()).thenReturn(-128);
|
||||
when(location2.getBlockY()).thenReturn(120);
|
||||
when(location2.getBlockZ()).thenReturn(0);
|
||||
island.setCenter(location2);
|
||||
|
||||
new DeleteIslandChunks(plugin, island);
|
||||
Mockito.verify(world, Mockito.times(64)).regenerateChunk(Mockito.anyInt(), Mockito.anyInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.util.DeleteIslandChunks#DeleteIslandChunks(world.bentobox.bentobox.BentoBox, world.bentobox.bentobox.database.objects.Island)}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testDeleteIslandChunksNegativeXX() {
|
||||
|
||||
// Island adjacent to an island at 0,0
|
||||
Location location2 = mock(Location.class);
|
||||
|
||||
when(location2.getWorld()).thenReturn(world);
|
||||
when(location2.getBlockX()).thenReturn(-256);
|
||||
when(location2.getBlockY()).thenReturn(120);
|
||||
when(location2.getBlockZ()).thenReturn(0);
|
||||
island.setCenter(location2);
|
||||
|
||||
new DeleteIslandChunks(plugin, island);
|
||||
Mockito.verify(world, Mockito.times(64)).regenerateChunk(Mockito.anyInt(), Mockito.anyInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.util.DeleteIslandChunks#DeleteIslandChunks(world.bentobox.bentobox.BentoBox, world.bentobox.bentobox.database.objects.Island)}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testDeleteIslandChunksIslandPositiveX() {
|
||||
|
||||
// Island adjacent to an island at 0,0
|
||||
Location location2 = mock(Location.class);
|
||||
|
||||
when(location2.getWorld()).thenReturn(world);
|
||||
when(location2.getBlockX()).thenReturn(0);
|
||||
when(location2.getBlockY()).thenReturn(120);
|
||||
when(location2.getBlockZ()).thenReturn(0);
|
||||
island.setCenter(location2);
|
||||
|
||||
new DeleteIslandChunks(plugin, island);
|
||||
Mockito.verify(world, Mockito.times(64)).regenerateChunk(Mockito.anyInt(), Mockito.anyInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.util.DeleteIslandChunks#DeleteIslandChunks(world.bentobox.bentobox.BentoBox, world.bentobox.bentobox.database.objects.Island)}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testDeleteIslandChunksPositiveXX() {
|
||||
|
||||
// Island adjacent to an island at 0,0
|
||||
Location location2 = mock(Location.class);
|
||||
|
||||
when(location2.getWorld()).thenReturn(world);
|
||||
when(location2.getBlockX()).thenReturn(256);
|
||||
when(location2.getBlockY()).thenReturn(120);
|
||||
when(location2.getBlockZ()).thenReturn(0);
|
||||
island.setCenter(location2);
|
||||
|
||||
new DeleteIslandChunks(plugin, island);
|
||||
Mockito.verify(world, Mockito.times(64)).regenerateChunk(Mockito.anyInt(), Mockito.anyInt());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user