diff --git a/pom.xml b/pom.xml index 8aa65b84f..1d0d56251 100644 --- a/pom.xml +++ b/pom.xml @@ -195,6 +195,11 @@ Lumine Releases https://mvn.lumine.io/repository/maven-public/ + + + clojars + https://repo.clojars.org/ + @@ -364,6 +369,13 @@ 3.6.1 provided + + + com.github.puregero + multilib + 1.1.12 + compile + @@ -486,9 +498,10 @@ org.apache.maven.plugins maven-shade-plugin - 3.3.1-SNAPSHOT + 3.4.0 true + ${project.build.directory}/dependency-reduced-pom.xml org.bstats @@ -500,9 +513,13 @@ io.papermc.lib - world.bentobox.bentobox.paperlib + world.bentobox.bentobox.paperlib - + + com.github.puregero.multilib + world.bentobox.bentobox.multilib + + org.apache.maven.shared:* diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 235d37ef6..567c2eceb 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -210,20 +210,6 @@ public class BentoBox extends JavaPlugin implements Listener { return; } - // Save islands & players data every X minutes - Bukkit.getScheduler().runTaskTimer(instance, () -> { - if (!playersManager.isSaveTaskRunning()) { - playersManager.saveAll(true); - } else { - getLogger().warning("Tried to start a player data save task while the previous auto save was still running!"); - } - if (!islandsManager.isSaveTaskRunning()) { - islandsManager.saveAll(true); - } else { - getLogger().warning("Tried to start a island data save task while the previous auto save was still running!"); - } - }, getSettings().getDatabaseBackupPeriod() * 20 * 60L, getSettings().getDatabaseBackupPeriod() * 20 * 60L); - // Make sure all flag listeners are registered. flagsManager.registerListeners(); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminEmptyTrashCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminEmptyTrashCommand.java deleted file mode 100644 index f5c8970ff..000000000 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminEmptyTrashCommand.java +++ /dev/null @@ -1,61 +0,0 @@ -package world.bentobox.bentobox.api.commands.admin; - -import java.util.List; -import java.util.UUID; - -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.commands.ConfirmableCommand; -import world.bentobox.bentobox.api.localization.TextVariables; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.database.objects.Island; - -public class AdminEmptyTrashCommand extends ConfirmableCommand { - - /** - * Clear trash for player, or all unowned islands in trash - * @param parent - admin command - * @since 1.3.0 - */ - public AdminEmptyTrashCommand(CompositeCommand parent) { - super(parent, "emptytrash"); - } - - @Override - public void setup() { - setPermission("admin.trash"); - setOnlyPlayer(false); - setParametersHelp("commands.admin.emptytrash.parameters"); - setDescription("commands.admin.emptytrash.description"); - } - - @Override - public boolean execute(User user, String label, List args) { - if (args.size() > 1) { - // Show help - showHelp(this, user); - return false; - } - // Get target player - UUID targetUUID = args.isEmpty() ? null : getPlayers().getUUID(args.get(0)); - if (!args.isEmpty() && targetUUID == null) { - user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); - return false; - } - // Remove trash for this player - final List islands = getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID); - if (islands.isEmpty()) { - if (args.isEmpty()) { - user.sendMessage("commands.admin.trash.no-unowned-in-trash"); - } else { - user.sendMessage("commands.admin.trash.no-islands-in-trash"); - } - return false; - } else { - this.askConfirmation(user, () -> { - getIslands().deleteQuarantinedIslandByUser(getWorld(), targetUUID); - user.sendMessage("commands.admin.emptytrash.success"); - }); - return true; - } - } -} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java index ca3281996..dfb6eaf96 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java @@ -49,9 +49,6 @@ public class AdminInfoCommand extends CompositeCommand { Island island = getIslands().getIsland(getWorld(), targetUUID); if (island != null) { new IslandInfo(island).showAdminInfo(user, getAddon()); - if (!getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID).isEmpty()) { - user.sendMessage("commands.admin.info.islands-in-trash"); - } return true; } else { user.sendMessage("general.errors.player-has-no-island"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommand.java index 177d85657..1fa50c1a7 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommand.java @@ -206,11 +206,11 @@ public class AdminSettingsCommand extends CompositeCommand { switch (f.getType()) { case PROTECTION -> { island.setFlag(f, rank); - getIslands().save(island); + getIslands().updateIsland(island); } case SETTING -> { island.setSettingsFlag(f, activeState); - getIslands().save(island); + getIslands().updateIsland(island); } case WORLD_SETTING -> f.setSetting(getWorld(), activeState); default -> { diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSwitchtoCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSwitchtoCommand.java deleted file mode 100644 index 3bb91c4c4..000000000 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSwitchtoCommand.java +++ /dev/null @@ -1,87 +0,0 @@ -package world.bentobox.bentobox.api.commands.admin; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import org.apache.commons.lang.math.NumberUtils; -import org.eclipse.jdt.annotation.NonNull; - -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.commands.ConfirmableCommand; -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 AdminSwitchtoCommand extends ConfirmableCommand { - - private UUID targetUUID; - private @NonNull List islands; - - /** - * Switch player's island to the numbered one in trash - * @param parent - admin command - * @since 1.3.0 - */ - public AdminSwitchtoCommand(CompositeCommand parent) { - super(parent, "switchto"); - islands = new ArrayList<>(); - } - - @Override - public void setup() { - setPermission("admin.switchto"); - setOnlyPlayer(false); - setParametersHelp("commands.admin.switchto.parameters"); - setDescription("commands.admin.switchto.description"); - } - - @Override - public boolean canExecute(User user, String label, List args) { - if (args.size() != 2) { - // Show help - showHelp(this, user); - return false; - } - // Get target player - targetUUID = Util.getUUID(args.get(0)); - if (targetUUID == null) { - user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); - return false; - } - // Check island number - islands = getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID); - if (islands.isEmpty()) { - user.sendMessage("commands.admin.trash.no-islands-in-trash"); - return false; - } - return true; - } - - @Override - public boolean execute(User user, String label, List args) { - if (NumberUtils.isDigits(args.get(1))) { - try { - int n = Integer.parseInt(args.get(1)); - if (n < 1 || n > islands.size()) { - user.sendMessage("commands.admin.switchto.out-of-range", TextVariables.NUMBER, String.valueOf(islands.size()), TextVariables.LABEL, getTopLabel()); - return false; - } - this.askConfirmation(user, () -> { - if (getIslands().switchIsland(getWorld(), targetUUID, islands.get(n -1))) { - user.sendMessage("commands.admin.switchto.success"); - } else { - user.sendMessage("commands.admin.switchto.cannot-switch"); - } - }); - return true; - } catch (Exception e) { - showHelp(this, user); - return false; - } - } - return true; - } - -} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTrashCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTrashCommand.java deleted file mode 100644 index e17d10041..000000000 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminTrashCommand.java +++ /dev/null @@ -1,73 +0,0 @@ -package world.bentobox.bentobox.api.commands.admin; - -import java.util.List; -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.IslandInfo; - -public class AdminTrashCommand extends CompositeCommand { - - /** - * A command for viewing islands in the database trash - * @param parent - admin command - * @since 1.3.0 - */ - public AdminTrashCommand(CompositeCommand parent) { - super(parent, "trash"); - } - - @Override - public void setup() { - setPermission("admin.trash"); - setOnlyPlayer(false); - setParametersHelp("commands.admin.trash.parameters"); - setDescription("commands.admin.trash.description"); - } - - @Override - public boolean execute(User user, String label, List args) { - if (args.size() > 1) { - // Show help - showHelp(this, user); - return false; - } - // Get target player - UUID targetUUID = args.isEmpty() ? null : getPlayers().getUUID(args.get(0)); - if (!args.isEmpty() && targetUUID == null) { - user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); - return false; - } - // Show trash can info for this player - List islands = getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID); - if (islands.isEmpty()) { - if (args.isEmpty()) { - user.sendMessage("commands.admin.trash.no-unowned-in-trash"); - } else { - user.sendMessage("commands.admin.trash.no-islands-in-trash"); - } - return false; - } else { - if (targetUUID == null) { - showTrash(user, islands); - } else { - getIslands().getQuarantineCache().values().forEach(v -> showTrash(user, v)); - } - return true; - } - } - - private void showTrash(User user, List 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)); - new IslandInfo(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()); - - } -} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommand.java index 968ebf178..d0b319aee 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommand.java @@ -116,7 +116,7 @@ public class AdminUnregisterCommand extends ConfirmableCommand { targetIsland.getMembers().clear(); targetIsland.log(new LogEntry.Builder("UNREGISTER").data("player", targetUUID.toString()) .data("admin", user.getUniqueId().toString()).build()); - getIslands().save(targetIsland); + getIslands().updateIsland(targetIsland); user.sendMessage("commands.admin.unregister.unregistered-island", TextVariables.XYZ, Util.xyz(targetIsland.getCenter().toVector()), TextVariables.NAME, getPlayers().getName(targetUUID)); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/conversations/NamePrompt.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/conversations/NamePrompt.java index e0edd1b25..645be62b2 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/conversations/NamePrompt.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/conversations/NamePrompt.java @@ -40,7 +40,7 @@ public class NamePrompt extends StringPrompt { @Override public Prompt acceptInput(@NonNull ConversationContext context, String input) { if (island.renameHome(oldName, input)) { - plugin.getIslands().save(island); + plugin.getIslands().updateIsland(island); Bukkit.getScheduler().runTask(plugin, () -> user.sendMessage("general.success")); } else { Bukkit.getScheduler().runTask(plugin, () -> user.sendMessage("commands.island.renamehome.already-exists")); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java index 25cceac63..ed08f2394 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -13,6 +13,7 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.TeamInvite; import world.bentobox.bentobox.database.objects.TeamInvite.Type; +import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; @@ -197,7 +198,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { inviter.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()); } - getIslands().save(teamIsland); + IslandsManager.updateIsland(teamIsland); // Fire event TeamEvent.builder().island(teamIsland).reason(TeamEvent.Reason.JOINED).involvedPlayer(user.getUniqueId()) .build(); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java index e80580aa9..e9f1e5eae 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java @@ -91,7 +91,7 @@ public class IslandTeamSetownerCommand extends CompositeCommand { IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false) .reason(IslandEvent.Reason.RANK_CHANGE).rankChange(RanksManager.OWNER_RANK, RanksManager.SUB_OWNER_RANK) .build(); - getIslands().save(island); + getIslands().updateIsland(island); return true; } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/Island.java b/src/main/java/world/bentobox/bentobox/database/objects/Island.java index 60d95c89f..50e38334e 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -41,6 +41,7 @@ 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.IslandsManager; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Util; @@ -243,7 +244,6 @@ public class Island implements DataObject, MetaDataAble { range = BentoBox.getInstance().getIWM().getIslandDistance(world); this.protectionRange = protectionRange; this.maxEverProtectionRange = protectionRange; - this.setChanged(); } /** @@ -290,6 +290,7 @@ public class Island implements DataObject, MetaDataAble { this.updatedDate = island.getUpdatedDate(); this.world = island.getWorld(); this.bonusRanges.addAll(island.getBonusRanges()); + this.primary = island.primary; this.setChanged(); } @@ -304,8 +305,10 @@ public class Island implements DataObject, MetaDataAble { * @param playerUUID - the player's UUID */ public void addMember(@NonNull UUID playerUUID) { - setRank(playerUUID, RanksManager.MEMBER_RANK); - setChanged(); + if (getRank(playerUUID) != RanksManager.MEMBER_RANK) { + setRank(playerUUID, RanksManager.MEMBER_RANK); + setChanged(); + } } /** @@ -320,9 +323,12 @@ public class Island implements DataObject, MetaDataAble { * @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(); + if (getRank(target) != RanksManager.BANNED_RANK) { + setRank(target, RanksManager.BANNED_RANK); + log(new LogEntry.Builder("BAN").data("player", target.toString()).data("issuer", issuer.toString()) + .build()); + setChanged(); + } return true; } @@ -1005,25 +1011,30 @@ public class Island implements DataObject, MetaDataAble { * @param playerUUID - uuid of player */ public void removeMember(UUID playerUUID) { - members.remove(playerUUID); - setChanged(); + if (members.remove(playerUUID) != null) { + setChanged(); + } } /** * @param center the center to set */ public void setCenter(@NonNull Location center) { - this.world = center.getWorld(); - this.center = center; - setChanged(); + if (!center.getWorld().equals(this.center.getWorld()) || !center.equals(this.center)) { + this.world = center.getWorld(); + this.center = center; + setChanged(); + } } /** * @param createdDate - the createdDate to sets */ public void setCreatedDate(long createdDate) { - this.createdDate = createdDate; - setChanged(); + if (this.createdDate != createdDate) { + this.createdDate = createdDate; + setChanged(); + } } /** @@ -1038,21 +1049,23 @@ public class Island implements DataObject, MetaDataAble { } /** - * Set the Island Guard flag rank Also specify whether subflags are affected by - * this method call + * Set the Island Guard flag rank and set any subflags * * @param flag - flag * @param value - Use RanksManager settings, e.g. RanksManager.MEMBER * @param doSubflags - whether to set subflags + * @return true if this causes a flag change */ public void setFlag(Flag flag, int value, boolean doSubflags) { - flags.put(flag.getID(), value); + if (flags.containsKey(flag.getID()) && flags.get(flag.getID()) != value) { + flags.put(flag.getID(), value); + setChanged(); + } // 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(); } /** @@ -1097,8 +1110,10 @@ public class Island implements DataObject, MetaDataAble { * @param name The display name to set. */ public void setName(String name) { - this.name = (name != null && !name.equals("")) ? name : null; - setChanged(); + if (name == null || !name.equals(this.name)) { + this.name = (name != null && !name.equals("")) ? name : null; + setChanged(); + } } /** @@ -1130,9 +1145,11 @@ public class Island implements DataObject, MetaDataAble { * @param protectionRange the protectionRange to set */ public void setProtectionRange(int protectionRange) { - this.protectionRange = protectionRange; - this.updateMaxEverProtectionRange(); - setChanged(); + if (this.protectionRange != protectionRange) { + this.protectionRange = protectionRange; + this.updateMaxEverProtectionRange(); + setChanged(); + } } /** @@ -1164,8 +1181,10 @@ public class Island implements DataObject, MetaDataAble { * @param purgeProtected - if the island is protected from the Purge */ public void setPurgeProtected(boolean purgeProtected) { - this.purgeProtected = purgeProtected; - setChanged(); + if (this.purgeProtected != purgeProtected) { + this.purgeProtected = purgeProtected; + setChanged(); + } } /** @@ -1179,8 +1198,10 @@ public class Island implements DataObject, MetaDataAble { * @see #setProtectionRange(int) */ public void setRange(int range) { - this.range = range; - setChanged(); + if (this.range != range) { + this.range = range; + setChanged(); + } } /** @@ -1191,7 +1212,6 @@ public class Island implements DataObject, MetaDataAble { */ public void setRank(User user, int rank) { setRank(user.getUniqueId(), rank); - setChanged(); } /** @@ -1206,8 +1226,14 @@ public class Island implements DataObject, MetaDataAble { if (uuid == null) { return; // Defensive code } - members.put(uuid, rank); - setChanged(); + members.compute(uuid, (key, value) -> { + if (value == null || !value.equals(rank)) { + setChanged(); // Call setChanged only if the value is updated. + BentoBox.getInstance().logDebug("Set rank for " + uuid + " to " + rank); + return rank; + } + return value; + }); } /** @@ -1266,7 +1292,6 @@ public class Island implements DataObject, MetaDataAble { @Override public void setUniqueId(String uniqueId) { this.uniqueId = uniqueId; - setChanged(); } /** @@ -1274,7 +1299,6 @@ public class Island implements DataObject, MetaDataAble { */ public void setUpdatedDate(long updatedDate) { this.updatedDate = updatedDate; - setChanged(); } /** @@ -1347,8 +1371,13 @@ public class Island implements DataObject, MetaDataAble { * @param l - location */ public void setSpawnPoint(Environment islandType, Location l) { - spawnPoint.put(islandType, l); - setChanged(); + spawnPoint.compute(islandType, (key, value) -> { + if (value == null || !value.equals(l)) { + setChanged(); // Call setChanged only if the value is updated. + return l; + } + return value; + }); } /** @@ -1368,8 +1397,9 @@ public class Island implements DataObject, MetaDataAble { * @param rank rank value */ public void removeRank(Integer rank) { - members.values().removeIf(rank::equals); - setChanged(); + if (members.values().removeIf(rank::equals)) { + setChanged(); + } } /** @@ -1455,7 +1485,6 @@ public class Island implements DataObject, MetaDataAble { */ public void setGameMode(String gameMode) { this.gameMode = gameMode; - setChanged(); } /** @@ -1603,8 +1632,13 @@ public class Island implements DataObject, MetaDataAble { public void setRankCommand(String command, int rank) { if (this.commandRanks == null) this.commandRanks = new HashMap<>(); - this.commandRanks.put(command, rank); - setChanged(); + commandRanks.compute(command, (key, value) -> { + if (value == null || !value.equals(rank)) { + setChanged(); // Call setChanged only if the value is updated. + return rank; + } + return value; + }); } /** @@ -1624,8 +1658,10 @@ public class Island implements DataObject, MetaDataAble { * @since 1.6.0 */ public void setReserved(boolean reserved) { - this.reserved = reserved; - setChanged(); + if (this.reserved != reserved) { + this.reserved = reserved; + setChanged(); + } } /** @@ -1658,17 +1694,19 @@ public class Island implements DataObject, MetaDataAble { } /** - * Indicates the fields have been changed. Used to optimize saving on shutdown. + * Indicates the fields have been changed. Used to optimize saving on shutdown and notify other servers */ public void setChanged() { + this.setUpdatedDate(System.currentTimeMillis()); this.changed = true; + IslandsManager.updateIsland(this); } /** - * @param changed the changed to set + * Resets the changed if the island has been saved */ - public void setChanged(boolean changed) { - this.changed = changed; + public void clearChanged() { + this.changed = false; } /** @@ -1692,6 +1730,9 @@ public class Island implements DataObject, MetaDataAble { * @since 1.16.0 */ public void setProtectionCenter(Location location) throws IOException { + if (this.location.equals(location)) { + return; // nothing to do + } if (!this.inIslandSpace(location)) { throw new IOException("Location must be in island space"); } @@ -1741,6 +1782,9 @@ public class Island implements DataObject, MetaDataAble { * @since 1.16.0 */ public void addHome(String name, Location location) { + if (getHomes().containsKey(name) && getHomes().get(name).equals(location)) { + return; // nothing to do + } if (location != null) { Vector v = location.toVector(); if (!this.getBoundingBox().contains(v)) { @@ -1763,8 +1807,11 @@ public class Island implements DataObject, MetaDataAble { * @since 1.16.0 */ public boolean removeHome(String name) { - setChanged(); - return getHomes().remove(name.toLowerCase()) != null; + if (getHomes().remove(name.toLowerCase()) != null) { + setChanged(); + return true; + } + return false; } /** @@ -1774,8 +1821,11 @@ public class Island implements DataObject, MetaDataAble { * @since 1.20.0 */ public boolean removeHomes() { - setChanged(); - return getHomes().keySet().removeIf(k -> !k.isEmpty()); + if (getHomes().keySet().removeIf(k -> !k.isEmpty())) { + setChanged(); + return true; + } + return false; } /** @@ -1814,8 +1864,10 @@ public class Island implements DataObject, MetaDataAble { * @since 1.16.0 */ public void setMaxHomes(@Nullable Integer maxHomes) { - this.maxHomes = maxHomes; - setChanged(); + if (this.maxHomes != maxHomes) { + this.maxHomes = maxHomes; + setChanged(); + } } /** @@ -1834,8 +1886,10 @@ public class Island implements DataObject, MetaDataAble { * @since 1.16.0 */ public void setMaxMembers(Map maxMembers) { - this.maxMembers = maxMembers; - setChanged(); + if (this.maxMembers != maxMembers) { + this.maxMembers = maxMembers; + setChanged(); + } } /** @@ -1860,7 +1914,13 @@ public class Island implements DataObject, MetaDataAble { * @since 1.16.0 */ public void setMaxMembers(int rank, Integer maxMembers) { - getMaxMembers().put(rank, maxMembers); + getMaxMembers().compute(rank, (key, value) -> { + if (value == null || !value.equals(maxMembers)) { + setChanged(); // Call setChanged only if the value is updated. + return maxMembers; + } + return value; + }); } /** @@ -1923,8 +1983,9 @@ public class Island implements DataObject, MetaDataAble { * @param id id to identify this bonus */ public void clearBonusRange(String id) { - this.getBonusRanges().removeIf(r -> r.getUniqueId().equals(id)); - setChanged(); + if (this.getBonusRanges().removeIf(r -> r.getUniqueId().equals(id))) { + setChanged(); + } } /** @@ -1946,8 +2007,10 @@ public class Island implements DataObject, MetaDataAble { * @param primary the primary to set */ public void setPrimary(boolean primary) { - this.primary = primary; - setChanged(); + if (this.primary != primary) { + this.primary = primary; + setChanged(); + } } /** diff --git a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index f73f3dac3..7cac25a0c 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java @@ -2,6 +2,7 @@ package world.bentobox.bentobox.listeners; import java.util.Collections; import java.util.Objects; +import java.util.Set; import java.util.UUID; import org.bukkit.Bukkit; @@ -74,7 +75,6 @@ public class JoinLeaveListener implements Listener { // Set the player's name (it may have changed), but only if it isn't empty if (!user.getName().isEmpty()) { players.setPlayerName(user); - players.save(playerUUID); } else { plugin.logWarning("Player that just logged in has no name! " + playerUUID); } @@ -181,8 +181,10 @@ public class JoinLeaveListener implements Listener { user.getPlayer().getInventory().clear(); } - playerData.getPendingKicks().remove(world.getName()); - players.save(user.getUniqueId()); + Set kicks = playerData.getPendingKicks(); + kicks.remove(world.getName()); + playerData.setPendingKicks(kicks); + } } @@ -236,7 +238,6 @@ public class JoinLeaveListener implements Listener { }); // Remove any coop associations from the player logging out plugin.getIslands().clearRank(RanksManager.COOP_RANK, event.getPlayer().getUniqueId()); - players.save(event.getPlayer().getUniqueId()); User.removePlayer(event.getPlayer()); } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandCycleClick.java b/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandCycleClick.java index 8b78b725d..922c82dcd 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandCycleClick.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/clicklisteners/CommandCycleClick.java @@ -61,7 +61,7 @@ public class CommandCycleClick implements ClickHandler { // Apply change to panel panel.getInventory().setItem(slot, commandRankClickListener.getPanelItem(command, user, world).getItem()); // Save island - plugin.getIslands().save(island); + plugin.getIslands().updateIsland(island); } else { user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F); diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index 486edb811..4dcd898f7 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -37,6 +37,8 @@ import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import com.github.puregero.multilib.MultiLib; + import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.IslandBaseEvent; @@ -44,7 +46,6 @@ import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; 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.Database; import world.bentobox.bentobox.database.objects.Island; @@ -65,29 +66,18 @@ public class IslandsManager { private final BentoBox plugin; - /** - * One island can be spawn, this is the one - otherwise, this value is null - */ - @NonNull - private final Map<@NonNull World, @Nullable Island> spawn; + private Map spawns = new HashMap<>(); + + private Map last = new HashMap<>(); @NonNull - private Database handler; - - /** - * The last locations where an island were put. This is not stored persistently - * and resets when the server starts - */ - private final Map last; + private static Database handler; /** * Island Cache */ @NonNull private IslandCache islandCache; - // Quarantined islands - @NonNull - private final Map> quarantineCache; // Deleted islands @NonNull private final List deletedIslands; @@ -106,14 +96,52 @@ public class IslandsManager { // Set up the database handler to store and retrieve Island classes handler = new Database<>(plugin, Island.class); islandCache = new IslandCache(); - quarantineCache = new HashMap<>(); - spawn = new HashMap<>(); - last = new HashMap<>(); // This list should always be empty unless database deletion failed // In that case a purge utility may be required in the future deletedIslands = new ArrayList<>(); // Mid-teleport players going home goingHome = new HashSet<>(); + // Set handler in Island + + // Listen for Island Updates + MultiLib.onString(plugin, "bentobox-updateIsland", id -> { + BentoBox.getInstance().logDebug("Updating island " + id); + Island island = handler.loadObject(id); + if (island != null) { + islandCache.updateIsland(island); + } + }); + // List for new islands + MultiLib.onString(plugin, "bentobox-newIsland", id -> { + BentoBox.getInstance().logDebug("New island " + id); + Island island = handler.loadObject(id); + if (island != null) { + islandCache.addIsland(island); + } + }); + // Remove islands + MultiLib.onString(plugin, "bentobox-deleteIsland", id -> { + BentoBox.getInstance().logDebug("Delete island " + id); + Island island = handler.loadObject(id); + if (island != null) { + islandCache.removeIsland(island); + } + }); + // Set or clear spawn + MultiLib.onString(plugin, "bentobox-setspawn", sp -> { + String[] split = sp.split(","); + if (split.length == 1) { + World world = Bukkit.getWorld(split[0]); + this.clearSpawn(world); + } else if (split.length == 2) { + World world = Bukkit.getWorld(split[0]); + if (world != null) { + getIslandById(split[1]).ifPresent(i -> this.setSpawn(i)); + BentoBox.getInstance().logDebug("Setting spawn for world " + world); + } + } + + }); } /** @@ -121,8 +149,8 @@ public class IslandsManager { * * @param handler - handler */ - public void setHandler(@NonNull Database handler) { - this.handler = handler; + public void setHandler(@NonNull Database h) { + handler = h; } /** @@ -227,14 +255,13 @@ public class IslandsManager { .orElse(""); island.setGameMode(gmName); island.setUniqueId(gmName + island.getUniqueId()); - while (handler.objectExists(island.getUniqueId())) { - // This should never happen, so although this is a potential infinite loop I'm - // going to leave it here because - // it will be bad if this does occur and the server should crash. - plugin.logWarning("Duplicate island UUID occurred"); - island.setUniqueId(gmName + UUID.randomUUID()); - } if (islandCache.addIsland(island)) { + // Save to database and notify other servers + handler.saveObjectAsync(island).thenAccept(b -> { + if (b.equals(Boolean.TRUE)) { + MultiLib.notify("bentobox-newIsland", island.getUniqueId()); + } + }); return island; } return null; @@ -260,15 +287,10 @@ public class IslandsManager { if (removeBlocks) { // Remove island from the cache islandCache.deleteIslandFromCache(island); - // Log the deletion (it shouldn't matter but may be useful) - island.log(new LogEntry.Builder("DELETED").build()); - // Set the delete flag which will prevent it from being loaded even if database - // deletion fails - island.setDeleted(true); - // Save the island - handler.saveObjectAsync(island); // Delete the island handler.deleteObject(island); + // Inform other servers + MultiLib.notify("bentobox-deletedIsland", island.getUniqueId()); // Remove players from island removePlayersFromIsland(island); if (!plugin.getSettings().isKeepPreviousIslandOnReset()) { @@ -462,7 +484,7 @@ public class IslandsManager { * Get the last location where an island was created * * @param world - world - * @return location + * @return location or null if none found */ public Location getLast(@NonNull World world) { return last.get(world); @@ -486,7 +508,7 @@ public class IslandsManager { if (island.getOwner() == null) { // No owner, no rank settings island.setMaxMembers(null); - this.save(island); + updateIsland(island); return 0; } // Island max is either the world default or specified amount for this island @@ -508,7 +530,7 @@ public class IslandsManager { islandMax); } island.setMaxMembers(rank, islandMax == worldDefault ? null : islandMax); - this.save(island); + updateIsland(island); return islandMax; } @@ -547,12 +569,12 @@ public class IslandsManager { // If the island maxHomes is just the same as the world default, then set to // null island.setMaxHomes(islandMax == plugin.getIWM().getMaxHomes(island.getWorld()) ? null : islandMax); - this.save(island); + updateIsland(island); return islandMax; } /** - * Set the maximum numbber of homes allowed on this island + * Set the maximum number of homes allowed on this island * * @param island - island * @param maxHomes - max number of homes allowed, or null if the world default @@ -737,7 +759,7 @@ public class IslandsManager { public boolean setHomeLocation(@Nullable Island island, Location location, String name) { if (island != null) { island.addHome(name, location); - this.save(island); + updateIsland(island); return true; } return false; @@ -890,7 +912,7 @@ public class IslandsManager { */ @NonNull public Optional getSpawn(@NonNull World world) { - return Optional.ofNullable(spawn.get(world)); + return Optional.ofNullable(spawns.get(world)); } /** @@ -901,7 +923,7 @@ public class IslandsManager { */ @Nullable public Location getSpawnPoint(@NonNull World world) { - return spawn.containsKey(world) ? spawn.get(world).getSpawnPoint(world.getEnvironment()) : null; + return getSpawn(world).map(i -> i.getSpawnPoint(world.getEnvironment())).orElse(null); } /** @@ -1132,7 +1154,7 @@ public class IslandsManager { * @return true if they are, false if they are not, or spawn does not exist */ public boolean isAtSpawn(Location playerLoc) { - return spawn.containsKey(playerLoc.getWorld()) && spawn.get(playerLoc.getWorld()).onIsland(playerLoc); + return getSpawn(playerLoc.getWorld()).map(i -> i.onIsland(playerLoc)).orElse(false); } /** @@ -1144,19 +1166,12 @@ public class IslandsManager { * @param spawn the Island to set as spawn. Must not be null. */ public void setSpawn(@NonNull Island spawn) { - // Checking if there is already a spawn set for this world - if (this.spawn.containsKey(spawn.getWorld()) && this.spawn.get(spawn.getWorld()) != null) { - Island oldSpawn = this.spawn.get(spawn.getWorld()); - if (oldSpawn.equals(spawn)) { - return; // The spawn is already the current spawn - no need to update anything. - } else { - oldSpawn.setSpawn(false); - } - } - this.spawn.put(spawn.getWorld(), spawn); - spawn.setSpawn(true); + spawns.put(Util.getWorld(spawn.getWorld()), spawn); + // Tell other servers + MultiLib.notify("bentobox-setspawn", spawn.getWorld().getUID().toString() + "," + spawn.getUniqueId()); } + /** * Clears the spawn island for this world * @@ -1164,11 +1179,9 @@ public class IslandsManager { * @since 1.8.0 */ public void clearSpawn(World world) { - Island spawnIsland = spawn.get(Util.getWorld(world)); - if (spawnIsland != null) { - spawnIsland.setSpawn(false); - } - this.spawn.remove(world); + spawns.remove(world); + // Tell other servers + MultiLib.notify("bentobox-setspawn", world.getUID().toString()); } /** @@ -1192,7 +1205,6 @@ public class IslandsManager { */ public void load() throws IOException { islandCache.clear(); - quarantineCache.clear(); List toQuarantine = new ArrayList<>(); int owned = 0; int unowned = 0; @@ -1206,9 +1218,6 @@ public class IslandsManager { if (island.isDeleted()) { // These will be deleted later deletedIslands.add(island.getUniqueId()); - } else if (island.isDoNotLoad() && island.getWorld() != null && island.getCenter() != null) { - // Add to quarantine cache - quarantineCache.computeIfAbsent(island.getOwner(), k -> new ArrayList<>()).add(island); } // Check island distance and if incorrect stop BentoBox else if (island.getWorld() != null && plugin.getIWM().inWorld(island.getWorld()) && island.getRange() != plugin.getIWM().getIslandDistance(island.getWorld())) { @@ -1219,18 +1228,9 @@ public class IslandsManager { } else { // Fix island center if it is off fixIslandCenter(island); - if (!islandCache.addIsland(island)) { - // Quarantine the offending island - toQuarantine.add(island); - // Add to quarantine cache - island.setDoNotLoad(true); - quarantineCache.computeIfAbsent(island.getOwner(), k -> new ArrayList<>()).add(island); - if (island.isUnowned()) { - unowned++; - } else { - owned++; - } - } else if (island.isSpawn()) { + islandCache.addIsland(island); + + if (island.isSpawn()) { // Success, set spawn if this is the spawn island. this.setSpawn(island); } else { @@ -1394,15 +1394,10 @@ public class IslandsManager { homeTeleportAsync(w, p); } else { // Move player to spawn - if (spawn.containsKey(w)) { - // go to island spawn - Location sp = spawn.get(w).getSpawnPoint(w.getEnvironment()); - if (sp != null) { - PaperLib.teleportAsync(p, sp); - } else { - plugin.logWarning("Spawn exists but its location is null!"); - } - } + getSpawn(w).map(i -> i.getSpawnPoint(w.getEnvironment())).filter(Objects::nonNull) + .ifPresentOrElse(sp -> PaperLib.teleportAsync(p, sp), + () -> plugin.logWarning("Spawn exists but its location is null!")); + } }); } @@ -1473,9 +1468,13 @@ public class IslandsManager { teamIsland.addMember(playerUUID); islandCache.addPlayer(playerUUID, teamIsland); // Save the island - handler.saveObjectAsync(teamIsland); + updateIsland(teamIsland); } + /** + * Set the last island location + * @param last location + */ public void setLast(Location last) { this.last.put(last.getWorld(), last); } @@ -1608,12 +1607,17 @@ public class IslandsManager { } /** - * Save the island to the database + * Update island data in database * * @param island - island */ - public void save(Island island) { - handler.saveObjectAsync(island); + public static void updateIsland(Island island) { + if (handler.objectExists(island.getUniqueId())) { + island.clearChanged(); + handler.saveObjectAsync(island) + .thenAccept(b -> MultiLib.notify("bentobox-updateIsland", island.getUniqueId())); + BentoBox.getInstance().logDebug("Saved island " + island.getUniqueId()); + } } /** @@ -1628,108 +1632,6 @@ public class IslandsManager { return Optional.ofNullable(islandCache.getIslandById(uniqueId)); } - /** - * Try to get an unmodifiable list of quarantined islands owned by uuid in this - * world - * - * @param world - world - * @param uuid - target player's UUID, or null = unowned islands - * @return list of islands; may be empty - * @since 1.3.0 - */ - @NonNull - public List getQuarantinedIslandByUser(@NonNull World world, @Nullable UUID uuid) { - return quarantineCache.getOrDefault(uuid, Collections.emptyList()).stream() - .filter(i -> i.getWorld().equals(world)).toList(); - } - - /** - * Delete quarantined islands owned by uuid in this world - * - * @param world - world - * @param uuid - target player's UUID, or null = unowned islands - * @since 1.3.0 - */ - public void deleteQuarantinedIslandByUser(World world, @Nullable UUID uuid) { - if (quarantineCache.containsKey(uuid)) { - quarantineCache.get(uuid).stream().filter(i -> i.getWorld().equals(world)) - .forEach(i -> handler.deleteObject(i)); - quarantineCache.get(uuid).removeIf(i -> i.getWorld().equals(world)); - } - } - - /** - * @return the quarantineCache - * @since 1.3.0 - */ - @NonNull - public Map> getQuarantineCache() { - return quarantineCache; - } - - /** - * Remove a quarantined island and delete it from the database completely. This - * is NOT recoverable unless you have database backups. - * - * @param island island - * @return {@code true} if island is quarantined and removed - * @since 1.3.0 - */ - public boolean purgeQuarantinedIsland(Island island) { - if (quarantineCache.containsKey(island.getOwner()) && quarantineCache.get(island.getOwner()).remove(island)) { - handler.deleteObject(island); - return true; - } - return false; - } - - /** - * Switches active island and island in trash - * - * @param world - game world - * @param target - target player's UUID - * @param island - island in trash - * @return true if successful, otherwise false - * @since 1.3.0 - */ - public boolean switchIsland(World world, UUID target, Island island) { - // Remove trashed island from trash - if (!quarantineCache.containsKey(island.getOwner()) || !quarantineCache.get(island.getOwner()).remove(island)) { - plugin.logError("Could not remove island from trash"); - return false; - } - // Remove old island from cache if it exists - if (this.hasIsland(world, target)) { - Island oldIsland = islandCache.get(world, target); - islandCache.removeIsland(oldIsland); - - // Set old island to trash - oldIsland.setDoNotLoad(true); - - // Put old island into trash - quarantineCache.computeIfAbsent(target, k -> new ArrayList<>()).add(oldIsland); - // Save old island - handler.saveObjectAsync(oldIsland).thenAccept(result -> { - if (Boolean.FALSE.equals(result)) - plugin.logError("Could not save trashed island in database"); - }); - } - // Restore island from trash - island.setDoNotLoad(false); - // Add new island to cache - if (!islandCache.addIsland(island)) { - plugin.logError("Could not add recovered island to cache"); - return false; - } - // Save new island - handler.saveObjectAsync(island).thenAccept(result -> { - if (Boolean.FALSE.equals(result)) { - plugin.logError("Could not save recovered island to database"); - } - }); - return true; - } - /** * Resets all flags to gamemode config.yml default * diff --git a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java index 007d67ba1..cd6187c75 100644 --- a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java @@ -2,18 +2,13 @@ package world.bentobox.bentobox.managers; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Queue; import java.util.Set; import java.util.UUID; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.entity.Tameable; -import org.bukkit.scheduler.BukkitRunnable; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; @@ -32,8 +27,7 @@ public class PlayersManager { private Database handler; private final Database names; - private final Map playerCache; - private final Set inTeleport; + private final Set inTeleport; // this needs databasing private boolean isSaveTaskRunning; @@ -50,7 +44,6 @@ public class PlayersManager { handler = new Database<>(plugin, Players.class); // Set up the names database names = new Database<>(plugin, Names.class); - playerCache = new HashMap<>(); inTeleport = new HashSet<>(); } @@ -66,63 +59,14 @@ public class PlayersManager { * Load all players - not normally used as to load all players into memory will be wasteful */ public void load(){ - playerCache.clear(); inTeleport.clear(); - handler.loadObjects().forEach(p -> playerCache.put(p.getPlayerUUID(), p)); } public boolean isSaveTaskRunning() { return isSaveTaskRunning; } - /** - * Save all players - */ - public void saveAll() { - saveAll(false); - } - - /** - * Save all players - * @param schedule true if we should let the task run over multiple ticks to reduce lag spikes - */ - public void saveAll(boolean schedule){ - if (!schedule) { - for (Players player : playerCache.values()) { - try { - handler.saveObjectAsync(player); - } catch (Exception e) { - plugin.logError("Could not save player to database when running sync! " + e.getMessage()); - } - } - return; - } - - isSaveTaskRunning = true; - Queue queue = new LinkedList<>(playerCache.values()); - new BukkitRunnable() { - @Override - public void run() { - for (int i = 0; i < plugin.getSettings().getMaxSavedPlayersPerTick(); i++) { - Players player = queue.poll(); - if (player == null) { - isSaveTaskRunning = false; - cancel(); - return; - } - try { - handler.saveObjectAsync(player); - } catch (Exception e) { - plugin.logError("Could not save player to database when running sync! " + e.getMessage()); - } - } - } - }.runTaskTimer(plugin, 0, 1); - } - public void shutdown(){ - saveAll(); - playerCache.clear(); handler.close(); } @@ -133,10 +77,7 @@ public class PlayersManager { */ @Nullable public Players getPlayer(UUID uuid){ - if (!playerCache.containsKey(uuid)) { - addPlayer(uuid); - } - return playerCache.get(uuid); + return addPlayer(uuid); } /** @@ -146,7 +87,7 @@ public class PlayersManager { */ @NonNull public Collection getPlayers() { - return Collections.unmodifiableCollection(playerCache.values()); + return Collections.unmodifiableCollection(handler.loadObjects()); } /* @@ -154,29 +95,20 @@ public class PlayersManager { */ /** - * Adds a player to the cache. If the UUID does not exist, a new player is made + * Adds a player to the database. If the UUID does not exist, a new player is made * @param playerUUID - the player's UUID */ - public void addPlayer(UUID playerUUID) { - if (playerUUID == null) { - return; - } - if (!playerCache.containsKey(playerUUID)) { - Players player; + public Players addPlayer(UUID playerUUID) { // If the player is in the database, load it, otherwise create a new player if (handler.objectExists(playerUUID.toString())) { - player = handler.loadObject(playerUUID.toString()); - if (player == null) { - player = new Players(plugin, playerUUID); - // Corrupted database entry - plugin.logError("Corrupted player database entry for " + playerUUID + " - unrecoverable. Recreated."); - player.setUniqueId(playerUUID.toString()); + Players player = handler.loadObject(playerUUID.toString()); + if (player != null) { + return player; } - } else { - player = new Players(plugin, playerUUID); } - playerCache.put(playerUUID, player); - } + Players player = new Players(plugin, playerUUID); + handler.saveObject(player); + return player; } /** @@ -187,7 +119,7 @@ public class PlayersManager { * @return true if player is known, otherwise false */ public boolean isKnown(UUID uniqueID) { - return uniqueID != null && (playerCache.containsKey(uniqueID) || handler.objectExists(uniqueID.toString())); + return handler.objectExists(uniqueID.toString()); } /** @@ -206,11 +138,8 @@ public class PlayersManager { // Not used } } - // Look in the name cache, then the data base and then give up - return playerCache.values().stream() - .filter(p -> p.getPlayerName().equalsIgnoreCase(name)).findFirst() - .map(p -> UUID.fromString(p.getUniqueId())) - .orElseGet(() -> names.objectExists(name) ? names.loadObject(name).getUuid() : null); + return names.loadObjects().stream().filter(n -> n.getUniqueId().equalsIgnoreCase(name)).findFirst() + .map(Names::getUuid).orElse(null); } /** @@ -218,8 +147,9 @@ public class PlayersManager { * @param user - the User */ public void setPlayerName(@NonNull User user) { - addPlayer(user.getUniqueId()); - playerCache.get(user.getUniqueId()).setPlayerName(user.getName()); + Players player = addPlayer(user.getUniqueId()); + player.setPlayerName(user.getName()); + handler.saveObject(player); Names newName = new Names(user.getName(), user.getUniqueId()); // Add to names database names.saveObjectAsync(newName); @@ -237,8 +167,8 @@ public class PlayersManager { if (playerUUID == null) { return ""; } - addPlayer(playerUUID); - return playerCache.get(playerUUID).getPlayerName(); + return names.loadObjects().stream().filter(n -> n.getUuid().equals(playerUUID)).findFirst() + .map(Names::getUniqueId).orElse(null); } /** @@ -248,8 +178,7 @@ public class PlayersManager { * @return number of resets */ public int getResets(World world, UUID playerUUID) { - addPlayer(playerUUID); - return playerCache.get(playerUUID).getResets(world); + return addPlayer(playerUUID).getResets(world); } /** @@ -277,8 +206,9 @@ public class PlayersManager { * @param resets number of resets to set */ public void setResets(World world, UUID playerUUID, int resets) { - addPlayer(playerUUID); - playerCache.get(playerUUID).setResets(world, resets); + Players p = addPlayer(playerUUID); + p.setResets(world, resets); + handler.saveObject(p); } /** @@ -287,11 +217,7 @@ public class PlayersManager { * @return name of the locale this player uses */ public String getLocale(UUID playerUUID) { - addPlayer(playerUUID); - if (playerUUID == null) { - return ""; - } - return playerCache.get(playerUUID).getLocale(); + return addPlayer(playerUUID).getLocale(); } /** @@ -300,8 +226,9 @@ public class PlayersManager { * @param localeName - locale name, e.g., en-US */ public void setLocale(UUID playerUUID, String localeName) { - addPlayer(playerUUID); - playerCache.get(playerUUID).setLocale(localeName); + Players p = addPlayer(playerUUID); + p.setLocale(localeName); + handler.saveObject(p); } /** @@ -310,8 +237,9 @@ public class PlayersManager { * @param playerUUID - the player's UUID */ public void addDeath(World world, UUID playerUUID) { - addPlayer(playerUUID); - playerCache.get(playerUUID).addDeath(world); + Players p = addPlayer(playerUUID); + p.addDeath(world); + handler.saveObject(p); } /** @@ -321,8 +249,9 @@ public class PlayersManager { * @param deaths - number of deaths */ public void setDeaths(World world, UUID playerUUID, int deaths) { - addPlayer(playerUUID); - playerCache.get(playerUUID).setDeaths(world, deaths); + Players p = addPlayer(playerUUID); + p.setDeaths(world, deaths); + handler.saveObject(p); } /** @@ -332,8 +261,7 @@ public class PlayersManager { * @return number of deaths */ public int getDeaths(World world, UUID playerUUID) { - addPlayer(playerUUID); - return playerCache.get(playerUUID) == null ? 0 : playerCache.get(playerUUID).getDeaths(world); + return addPlayer(playerUUID).getDeaths(world); } /** @@ -360,16 +288,6 @@ public class PlayersManager { return inTeleport.contains(uniqueId); } - /** - * Saves the player to the database - * @param playerUUID - the player's UUID - */ - public void save(UUID playerUUID) { - if (playerCache.containsKey(playerUUID)) { - handler.saveObjectAsync(playerCache.get(playerUUID)); - } - } - /** * Tries to get the user from his name * @param name - name @@ -395,8 +313,9 @@ public class PlayersManager { * @param playerUUID player's UUID */ public void addReset(World world, UUID playerUUID) { - addPlayer(playerUUID); - playerCache.get(playerUUID).addReset(world); + Players p = addPlayer(playerUUID); + p.addReset(world); + handler.saveObject(p); } /** @@ -406,8 +325,9 @@ public class PlayersManager { * @since 1.6.0 */ public void setFlagsDisplayMode(UUID playerUUID, Flag.Mode displayMode) { - addPlayer(playerUUID); - playerCache.get(playerUUID).setFlagsDisplayMode(displayMode); + Players p = addPlayer(playerUUID); + p.setFlagsDisplayMode(displayMode); + handler.saveObject(p); } /** @@ -417,19 +337,15 @@ public class PlayersManager { * @since 1.6.0 */ public Flag.Mode getFlagsDisplayMode(UUID playerUUID) { - addPlayer(playerUUID); - return playerCache.get(playerUUID).getFlagsDisplayMode(); + return addPlayer(playerUUID).getFlagsDisplayMode(); } /** - * Remove player from cache. Clears players with the same name or UUID + * Remove player from database * @param player player to remove */ public void removePlayer(Player player) { - // Clear any players with the same name - playerCache.values().removeIf(p -> player.getName().equalsIgnoreCase(p.getPlayerName())); - // Remove if the player's UUID is the same - playerCache.values().removeIf(p -> player.getUniqueId().toString().equals(p.getUniqueId())); + handler.deleteID(player.getUniqueId().toString()); } /** @@ -495,8 +411,6 @@ public class PlayersManager { // Player total XP (not displayed) target.getPlayer().setTotalExperience(0); } - // Save player - save(target.getUniqueId()); } } diff --git a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java index a72560ded..ad1d29072 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java @@ -54,6 +54,18 @@ public class IslandCache { grids = new HashMap<>(); } + public void updateIsland(@NonNull Island island) { + if (island.getCenter() != null || island.getWorld() != null) { + islandsByLocation.put(island.getCenter(), island); + islandsById.put(island.getUniqueId(), island); + // Only add islands to this map if they are owned + if (island.isOwned()) { + islandsByUUID.computeIfAbsent(island.getOwner(), k -> new HashSet<>()).add(island); + island.getMemberSet().forEach(member -> addPlayer(member, island)); + } + } + } + /** * Adds an island to the grid * @@ -62,12 +74,7 @@ public class IslandCache { */ public boolean addIsland(@NonNull Island island) { if (island.getCenter() == null || island.getWorld() == null) { - /* - * Special handling - return true. The island will not be quarantined, but just - * not loaded This can occur when a gamemode is removed temporarily from the - * server TODO: have an option to remove these when the purge command is added - */ - return true; + return false; } if (addToGrid(island)) { islandsByLocation.put(island.getCenter(), island); @@ -122,8 +129,10 @@ public class IslandCache { islandsById.remove(island.getUniqueId()); removeFromIslandsByUUID(island); // Remove from grid - grids.putIfAbsent(island.getWorld(), new IslandGrid()); - return grids.get(island.getWorld()).removeFromGrid(island); + if (grids.containsKey(island.getWorld())) { + return grids.get(island.getWorld()).removeFromGrid(island); + } + return false; } private void removeFromIslandsByUUID(Island island) { @@ -144,12 +153,11 @@ public class IslandCache { * * @param uniqueId - island unique ID */ - public void deleteIslandFromCache(@NonNull String uniqueId) { - islandsById.remove(uniqueId); - islandsByLocation.values().removeIf(i -> i.getUniqueId().equals(uniqueId)); - for (Set set : islandsByUUID.values()) { - set.removeIf(i -> i.getUniqueId().equals(uniqueId)); + public boolean deleteIslandFromCache(@NonNull String uniqueId) { + if (islandsById.containsKey(uniqueId)) { + return deleteIslandFromCache(islandsById.get(uniqueId)); } + return false; } /** diff --git a/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java b/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java index db404f1a3..7fb73425d 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java @@ -13,48 +13,21 @@ import world.bentobox.bentobox.database.objects.Island; */ class IslandGrid { private final TreeMap> grid = new TreeMap<>(); - private final BentoBox plugin = BentoBox.getInstance(); - /** * Adds island to grid * @param island - island to add * @return true if successfully added, false if island already exists, or there is an overlap */ public boolean addToGrid(Island island) { + // Check if we know about this island already if (grid.containsKey(island.getMinX())) { TreeMap zEntry = grid.get(island.getMinX()); if (zEntry.containsKey(island.getMinZ())) { - // 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()); - firstLoaded = new Island(island); - zEntry.put(island.getMinZ(), firstLoaded); - } 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. Swapping based on creation date."); - // FirstLoaded is the newer - firstLoaded = new Island(island); - zEntry.put(island.getMinZ(), firstLoaded); - } 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()); - } + if (island.getUniqueId().equals(zEntry.get(island.getMinZ()).getUniqueId())) { + BentoBox.getInstance().logDebug("I already know about this island"); + return true; } + BentoBox.getInstance().logDebug("Overlapping island"); return false; } else { // Add island diff --git a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java index d9a73819f..465bd7ef4 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java @@ -217,7 +217,7 @@ public class NewIsland { // Register metrics plugin.getMetrics().ifPresent(BStats::increaseIslandsCreatedCount); // Save island - plugin.getIslands().save(island); + plugin.getIslands().updateIsland(island); } /** @@ -266,8 +266,6 @@ public class NewIsland { plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range", island.getProtectionRange())); - // Save the player so that if the server crashes weird things won't happen - plugin.getPlayers().save(user.getUniqueId()); } /** diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommandTest.java index 63d6f1bd8..4057855cc 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommandTest.java @@ -276,7 +276,7 @@ public class IslandTeamSetownerCommandTest { assertTrue(its.canExecute(user, "", List.of("tastybento"))); assertTrue(its.execute(user, "", List.of("tastybento"))); verify(im).setOwner(any(), eq(user), eq(target)); - verify(im).save(island); + verify(im).updateIsland(island); } /** @@ -292,7 +292,7 @@ public class IslandTeamSetownerCommandTest { assertTrue(its.canExecute(user, "", List.of("tastybento"))); assertTrue(its.execute(user, "", List.of("tastybento"))); verify(im).setOwner(any(), eq(user), eq(target)); - verify(im).save(island); + verify(im).updateIsland(island); } /** diff --git a/src/test/java/world/bentobox/bentobox/database/objects/IslandTest.java b/src/test/java/world/bentobox/bentobox/database/objects/IslandTest.java index 527eef8f4..a94ac7af4 100644 --- a/src/test/java/world/bentobox/bentobox/database/objects/IslandTest.java +++ b/src/test/java/world/bentobox/bentobox/database/objects/IslandTest.java @@ -1105,15 +1105,6 @@ public class IslandTest { assertTrue(ii.isChanged()); } - /** - * Test method for {@link world.bentobox.bentobox.database.objects.Island#setChanged(boolean)}. - */ - @Test - public void testSetChangedBoolean() { - i.setChanged(false); - assertFalse(i.isChanged()); - } - /** * Test method for {@link world.bentobox.bentobox.database.objects.Island#getProtectionCenter()}. */ diff --git a/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java index 3d1582420..d1aa59bf1 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java @@ -236,7 +236,6 @@ public class JoinLeaveListenerTest { jll.onPlayerJoin(event); // Verify verify(pm, times(2)).addPlayer(any()); - verify(pm, times(2)).save(any()); verify(player, never()).sendMessage(anyString()); // Verify resets verify(pm).setResets(eq(world), any(), eq(0)); @@ -245,7 +244,6 @@ public class JoinLeaveListenerTest { verify(chest).clear(); verify(inv).clear(); assertTrue(set.isEmpty()); - verify(pm, times(2)).save(any()); } /** @@ -262,7 +260,6 @@ public class JoinLeaveListenerTest { verify(chest, never()).clear(); verify(inv, never()).clear(); assertFalse(set.isEmpty()); - verify(pm).save(any()); } /** @@ -356,7 +353,6 @@ public class JoinLeaveListenerTest { jll.onPlayerJoin(event); // Verify verify(pm, times(2)).addPlayer(any()); - verify(pm, times(2)).save(any()); verify(player).sendMessage(eq("commands.island.create.on-first-login")); } @@ -372,7 +368,6 @@ public class JoinLeaveListenerTest { verify(chest).clear(); verify(inv).clear(); assertTrue(set.isEmpty()); - verify(pm).save(any()); } /** @@ -387,7 +382,6 @@ public class JoinLeaveListenerTest { verify(chest, never()).clear(); verify(inv, never()).clear(); assertFalse(set.isEmpty()); - verify(pm, never()).save(any()); } /** diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index 7931f9df7..cf6b51d09 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -970,7 +970,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { /** * Test method for - * {@link world.bentobox.bentobox.managers.IslandsManager#save(Island)}. + * {@link world.bentobox.bentobox.managers.IslandsManager#updateIsland(Island)}. */ @Test public void testSave() { diff --git a/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java index 96790efba..b8bd1dc8c 100644 --- a/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java @@ -596,43 +596,6 @@ public class PlayersManagerTest { assertNull(pm.getUUID("tastybeto")); } - /** - * Test method for - * {@link world.bentobox.bentobox.managers.PlayersManager#saveAll()}. - */ - @Test - public void testSaveAll() { - pm.setHandler(db); - pm.addPlayer(uuid); - pm.saveAll(); - verify(db).saveObjectAsync(any()); - } - - /** - * Test method for - * {@link world.bentobox.bentobox.managers.PlayersManager#saveAll(boolean)}. - */ - @Test - public void testSaveAllBoolean() { - pm.setHandler(db); - pm.addPlayer(uuid); - pm.saveAll(true); - assertTrue(pm.isSaveTaskRunning()); - } - - /** - * Test method for - * {@link world.bentobox.bentobox.managers.PlayersManager#save(java.util.UUID)}. - */ - @Test - public void testSave() { - pm.setHandler(db); - // Add a player - pm.addPlayer(uuid); - pm.save(uuid); - verify(db).saveObjectAsync(any()); - } - /** * Test method for * {@link world.bentobox.bentobox.managers.PlayersManager#setPlayerName(world.bentobox.bentobox.api.user.User)}. diff --git a/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java b/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java index 70a723e2a..89f0e995f 100644 --- a/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java @@ -205,7 +205,7 @@ public class NewIslandTest { NewIsland.builder().addon(addon).name(NAME).player(user).noPaste().reason(Reason.CREATE).oldIsland(oldIsland) .build(); // Verifications - verify(im).save(eq(island)); + verify(im).updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -224,7 +224,7 @@ public class NewIslandTest { when(builder.build()).thenReturn(ire); NewIsland.builder().addon(addon).name(NAME).player(user).noPaste().reason(Reason.RESET).oldIsland(oldIsland).build(); // Verifications - verify(im).save(eq(island)); + verify(im).updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -243,7 +243,7 @@ public class NewIslandTest { public void testBuilderNoOldIsland() throws Exception { NewIsland.builder().addon(addon).name(NAME).player(user).noPaste().reason(Reason.CREATE).build(); // Verifications - verify(im).save(eq(island)); + verify(im).updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -262,7 +262,7 @@ public class NewIslandTest { when(location.distance(any())).thenReturn(30D); NewIsland.builder().addon(addon).name(NAME).player(user).reason(Reason.CREATE).build(); // Verifications - verify(im).save(eq(island)); + verify(im).updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(bpm).paste(eq(addon), eq(island), eq(NAME), any(Runnable.class), eq(false)); verify(builder, times(2)).build(); @@ -281,7 +281,7 @@ public class NewIslandTest { NewIsland.builder().addon(addon).name(NAME).player(user).reason(Reason.CREATE).build(); PowerMockito.mockStatic(Bukkit.class); // Verifications - verify(im).save(island); + verify(im).updateIsland(island); verify(island).setFlagsDefaults(); verify(bpm).paste(eq(addon), eq(island), eq(NAME), any(Runnable.class), eq(true)); verify(builder, times(2)).build(); @@ -299,7 +299,7 @@ public class NewIslandTest { when(im.hasIsland(any(), any(User.class))).thenReturn(true); NewIsland.builder().addon(addon).name(NAME).player(user).noPaste().reason(Reason.CREATE).oldIsland(oldIsland).build(); // Verifications - verify(im).save(eq(island)); + verify(im).updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -320,7 +320,7 @@ public class NewIslandTest { when(im.hasIsland(any(), any(User.class))).thenReturn(true); NewIsland.builder().addon(addon).name(NAME).player(user).noPaste().reason(Reason.CREATE).oldIsland(oldIsland).build(); // Verifications - verify(im).save(eq(island)); + verify(im).updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -342,7 +342,7 @@ public class NewIslandTest { when(im.hasIsland(any(), any(User.class))).thenReturn(true); NewIsland.builder().addon(addon).name(NAME).player(user).noPaste().reason(Reason.CREATE).oldIsland(oldIsland).build(); // Verifications - verify(im).save(eq(island)); + verify(im).updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build();