mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2025-02-07 07:51:41 +01:00
Fixes bug when loaded a large number of islands.
Also attempts to fix duplicate islands during loading. See https://github.com/BentoBoxWorld/BSkyBlock/issues/144
This commit is contained in:
parent
61d9d2810b
commit
4b6ec575be
@ -50,14 +50,23 @@ public class AdminTrashCommand extends CompositeCommand {
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
user.sendMessage("commands.admin.trash.title");
|
||||
for (int i = 0; i < islands.size(); i++) {
|
||||
user.sendMessage("commands.admin.trash.count", TextVariables.NUMBER, String.valueOf(i+1));
|
||||
islands.get(i).showInfo(user);
|
||||
if (targetUUID == null) {
|
||||
showTrash(user, islands);
|
||||
} else {
|
||||
getIslands().getQuarantineCache().values().forEach(v -> showTrash(user, v));
|
||||
}
|
||||
user.sendMessage("commands.admin.trash.use-switch", TextVariables.LABEL, getTopLabel());
|
||||
user.sendMessage("commands.admin.trash.use-emptytrash", TextVariables.LABEL, getTopLabel());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void showTrash(User user, List<Island> islands) {
|
||||
user.sendMessage("commands.admin.trash.title");
|
||||
for (int i = 0; i < islands.size(); i++) {
|
||||
user.sendMessage("commands.admin.trash.count", TextVariables.NUMBER, String.valueOf(i+1));
|
||||
islands.get(i).showInfo(user);
|
||||
}
|
||||
user.sendMessage("commands.admin.trash.use-switch", TextVariables.LABEL, getTopLabel());
|
||||
user.sendMessage("commands.admin.trash.use-emptytrash", TextVariables.LABEL, getTopLabel());
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -55,9 +55,16 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
// Load each object from the file system, filtered, non-null
|
||||
for (File file: Objects.requireNonNull(tableFolder.listFiles((dir, name) -> name.toLowerCase(Locale.ENGLISH).endsWith(JSON)))) {
|
||||
try (FileReader reader = new FileReader(file)){
|
||||
list.add(getGson().fromJson(reader, dataObject));
|
||||
T object = getGson().fromJson(reader, dataObject);
|
||||
if (object == null) {
|
||||
reader.close();
|
||||
throw new IOException("JSON file created a null object: " + file.getPath());
|
||||
}
|
||||
list.add(object);
|
||||
reader.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
plugin.logError("Could not load file '" + file.getName() + "': File not found.");
|
||||
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not load objects " + file.getName() + " " + e.getMessage());
|
||||
}
|
||||
|
@ -158,6 +158,33 @@ public class Island implements DataObject {
|
||||
this.maxEverProtectionRange = protectionRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones an island object
|
||||
* @param island - island to clone
|
||||
*/
|
||||
public Island(Island island) {
|
||||
this.center = island.getCenter().clone();
|
||||
this.createdDate = island.createdDate;
|
||||
this.deleted = island.deleted;
|
||||
this.doNotLoad = island.doNotLoad;
|
||||
this.flags.putAll(island.getFlags());
|
||||
this.gameMode = island.gameMode;
|
||||
this.history.addAll(island.history);
|
||||
this.levelHandicap = island.levelHandicap;
|
||||
this.maxEverProtectionRange = island.maxEverProtectionRange;
|
||||
this.members.putAll(island.members);
|
||||
this.name = island.name;
|
||||
this.owner = island.owner;
|
||||
this.protectionRange = island.protectionRange;
|
||||
this.purgeProtected = island.purgeProtected;
|
||||
this.range = island.range;
|
||||
this.spawn = island.spawn;
|
||||
island.spawnPoint.forEach((k,v) -> this.spawnPoint.put(k, v.clone()));
|
||||
this.uniqueId = island.uniqueId;
|
||||
this.updatedDate = island.updatedDate;
|
||||
this.world = island.world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a team member. If player is on banned list, they will be removed from it.
|
||||
* @param playerUUID - the player's UUID
|
||||
@ -1019,4 +1046,17 @@ public class Island implements DataObject {
|
||||
iwm.getEndWorld(getWorld()) != null &&
|
||||
!getCenter().toVector().toLocation(iwm.getEndWorld(getWorld())).getBlock().getType().equals(Material.AIR);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Island [deleted=" + deleted + ", uniqueId=" + uniqueId + ", center=" + center + ", range=" + range
|
||||
+ ", protectionRange=" + protectionRange + ", maxEverProtectionRange=" + maxEverProtectionRange
|
||||
+ ", world=" + world + ", gameMode=" + gameMode + ", name=" + name + ", createdDate=" + createdDate
|
||||
+ ", updatedDate=" + updatedDate + ", owner=" + owner + ", members=" + members + ", spawn=" + spawn
|
||||
+ ", purgeProtected=" + purgeProtected + ", flags=" + flags + ", history=" + history
|
||||
+ ", levelHandicap=" + levelHandicap + ", spawnPoint=" + spawnPoint + ", doNotLoad=" + doNotLoad + "]";
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@ -744,12 +746,18 @@ public class IslandsManager {
|
||||
/**
|
||||
* Clear and reload all islands from database
|
||||
*/
|
||||
public void load(){
|
||||
public void load() {
|
||||
islandCache.clear();
|
||||
quarantineCache.clear();
|
||||
List<Island> toQuarantine = new ArrayList<>();
|
||||
int owned = 0;
|
||||
int unowned = 0;
|
||||
// Attempt to load islands
|
||||
handler.loadObjects().forEach(island -> {
|
||||
for (Island island : handler.loadObjects()) {
|
||||
if (island == null) {
|
||||
plugin.logWarning("Null island when loading...");
|
||||
continue;
|
||||
}
|
||||
if (island.isDeleted()) {
|
||||
// These will be deleted later
|
||||
deletedIslands.add(island.getUniqueId());
|
||||
@ -765,6 +773,11 @@ public class IslandsManager {
|
||||
// Add to quarantine cache
|
||||
island.setDoNotLoad(true);
|
||||
quarantineCache.computeIfAbsent(island.getOwner(), k -> new ArrayList<>()).add(island);
|
||||
if (island.getOwner() == null) {
|
||||
unowned++;
|
||||
} else {
|
||||
owned++;
|
||||
}
|
||||
} else if (island.isSpawn()) {
|
||||
// Success, set spawn if this is the spawn island.
|
||||
this.setSpawn(island);
|
||||
@ -779,10 +792,34 @@ public class IslandsManager {
|
||||
if (island.getGameMode() == null) {
|
||||
island.setGameMode(plugin.getIWM().getAddon(island.getWorld()).map(gm -> gm.getDescription().getName()).orElse(""));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!toQuarantine.isEmpty()) {
|
||||
plugin.logError(toQuarantine.size() + " islands could not be loaded successfully; moving to trash bin.");
|
||||
plugin.logError(unowned + " are unowned, " + owned + " are owned.");
|
||||
|
||||
toQuarantine.forEach(handler::saveObject);
|
||||
// Check if there are any islands with duplicate islands
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
Set<UUID> duplicatedUUIDRemovedSet = new HashSet<>();
|
||||
Set<UUID> duplicated = islandCache.getIslands().stream()
|
||||
.map(Island::getOwner)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(n -> !duplicatedUUIDRemovedSet.add(n))
|
||||
.collect(Collectors.toSet());
|
||||
if (duplicated.size() > 0) {
|
||||
plugin.logError("**** Owners that have more than one island = " + duplicated.size());
|
||||
for (UUID uuid : duplicated) {
|
||||
Set<Island> set = islandCache.getIslands().stream().filter(i -> uuid.equals(i.getOwner())).collect(Collectors.toSet());
|
||||
plugin.logError(plugin.getPlayers().getName(uuid) + "(" + uuid.toString() + ") has " + set.size() + " islands:");
|
||||
set.forEach(i -> {
|
||||
plugin.logError("Island at " + i.getCenter());
|
||||
plugin.logError("Island unique ID = " + i.getUniqueId());
|
||||
});
|
||||
plugin.logError("You should find out which island is real and delete the uniqueID from the database for the bogus one.");
|
||||
plugin.logError("");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import world.bentobox.bentobox.database.objects.Island;
|
||||
*/
|
||||
class IslandGrid {
|
||||
private TreeMap<Integer, TreeMap<Integer, Island>> grid = new TreeMap<>();
|
||||
private BentoBox plugin = BentoBox.getInstance();
|
||||
|
||||
/**
|
||||
* Adds island to grid
|
||||
@ -23,9 +24,41 @@ class IslandGrid {
|
||||
if (grid.containsKey(island.getMinX())) {
|
||||
TreeMap<Integer, Island> zEntry = grid.get(island.getMinX());
|
||||
if (zEntry.containsKey(island.getMinZ())) {
|
||||
BentoBox.getInstance().logError("Cannot add island to grid because there is an overlapping");
|
||||
BentoBox.getInstance().logError("island already registered at this location: " + island.getCenter());
|
||||
BentoBox.getInstance().logError("This is most likely caused by island distances changing mid-game, or an old database file");
|
||||
// There is an overlap or duplicate
|
||||
plugin.logError("Cannot load island. Overlapping: " + island.getUniqueId());
|
||||
plugin.logError("Location: " + island.getCenter());
|
||||
// Get the previously loaded island
|
||||
Island firstLoaded = zEntry.get(island.getMinZ());
|
||||
if (firstLoaded.getOwner() == null && island.getOwner() != null) {
|
||||
// This looks fishy. We prefer to load islands that have an owner. Swap the two
|
||||
plugin.logError("Duplicate island has an owner, so using that one. " + island.getOwner());
|
||||
Island clone = new Island(firstLoaded);
|
||||
firstLoaded = new Island(island);
|
||||
zEntry.put(island.getMinZ(), firstLoaded);
|
||||
island = new Island(clone);
|
||||
} else if (firstLoaded.getOwner() != null && island.getOwner() != null) {
|
||||
// Check if the owners are the same - this is a true duplicate
|
||||
if (firstLoaded.getOwner().equals(island.getOwner())) {
|
||||
// Find out which one is the original
|
||||
if (firstLoaded.getCreatedDate() > island.getCreatedDate()) {
|
||||
plugin.logError("Same owner duplicate. Swaping based on creation date.");
|
||||
// FirstLoaded is the newer
|
||||
Island clone = new Island(firstLoaded);
|
||||
firstLoaded = new Island(island);
|
||||
zEntry.put(island.getMinZ(), firstLoaded);
|
||||
island = new Island(clone);
|
||||
} else {
|
||||
plugin.logError("Same owner duplicate.");
|
||||
}
|
||||
} else {
|
||||
plugin.logError("Duplicate but different owner. Keeping first loaded.");
|
||||
plugin.logError("This is serious!");
|
||||
plugin.logError("1st loaded ID: " + firstLoaded.getUniqueId());
|
||||
plugin.logError("1st loaded owner: " + firstLoaded.getOwner());
|
||||
plugin.logError("2nd loaded ID: " + island.getUniqueId());
|
||||
plugin.logError("2nd loaded owner: " + island.getOwner());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// Add island
|
||||
|
Loading…
Reference in New Issue
Block a user