diff --git a/pom.xml b/pom.xml index 8aa65b84f..e770b82a8 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ -LOCAL - 2.3.0 + 2.4.0 bentobox-world https://sonarcloud.io ${project.basedir}/lib @@ -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/BStats.java b/src/main/java/world/bentobox/bentobox/BStats.java index aedf7c83a..2fd23b587 100644 --- a/src/main/java/world/bentobox/bentobox/BStats.java +++ b/src/main/java/world/bentobox/bentobox/BStats.java @@ -59,7 +59,6 @@ public class BStats { registerGameModeAddonsChart(); registerHooksChart(); registerPlayersPerServerChart(); - registerFlagsDisplayModeChart(); // Single Line charts registerIslandsCountChart(); @@ -171,27 +170,6 @@ public class BStats { })); } - /** - * Sends the "flags display mode" of all the online players. - * @since 1.6.0 - */ - private void registerFlagsDisplayModeChart() { - metrics.addCustomChart(new AdvancedPie("flagsDisplayMode", () -> { - Map values = new HashMap<>(); - - Bukkit.getOnlinePlayers().forEach(player -> { - Flag.Mode mode = plugin.getPlayers().getFlagsDisplayMode(player.getUniqueId()); - if (values.containsKey(mode.name())) { - values.put(mode.name(), values.get(mode.name()) + 1); - } else { - values.put(mode.name(), 1); - } - }); - - return values; - })); - } - /** * Sends the enabled addons (except GameModeAddons) of this server as bar chart. * @since 1.17.1 diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 235d37ef6..2f537cd84 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(); @@ -433,7 +419,7 @@ public class BentoBox extends JavaPlugin implements Listener { * @return the ranksManager * @deprecated Just use {@code RanksManager.getInstance()} */ - @Deprecated(since = "2.0.0") + @Deprecated(since = "2.0.0", forRemoval = true) public RanksManager getRanksManager() { return RanksManager.getInstance(); } diff --git a/src/main/java/world/bentobox/bentobox/api/addons/Addon.java b/src/main/java/world/bentobox/bentobox/api/addons/Addon.java index 57190b7d9..5b4b5c18f 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/Addon.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/Addon.java @@ -21,6 +21,8 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.event.Listener; +import com.github.puregero.multilib.MultiLib; + import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.bentobox.api.flags.Flag; @@ -45,6 +47,8 @@ public abstract class Addon { protected Addon() { state = State.DISABLED; + // If the config is updated, update the config. + MultiLib.onString(getPlugin(), "bentobox-config-update", v -> this.reloadConfig()); } /** diff --git a/src/main/java/world/bentobox/bentobox/api/addons/GameModeAddon.java b/src/main/java/world/bentobox/bentobox/api/addons/GameModeAddon.java index a45b12d68..79c3fa016 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/GameModeAddon.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/GameModeAddon.java @@ -8,6 +8,8 @@ import org.bukkit.generator.ChunkGenerator; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import com.github.puregero.multilib.MultiLib; + import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.util.Util; @@ -129,7 +131,10 @@ public abstract class GameModeAddon extends Addon { * in-game and need to be saved. * @since 1.4.0 */ - public abstract void saveWorldSettings(); + public void saveWorldSettings() { + // Inform other servers + MultiLib.notify("bentobox-config-update", ""); + } /** * Defines if the game mode uses the latest {@link ChunkGenerator} API or 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..f7fafe800 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 @@ -16,7 +16,6 @@ import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.flags.Flag; -import world.bentobox.bentobox.api.flags.Flag.Mode; import world.bentobox.bentobox.api.flags.Flag.Type; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.panels.builders.TabbedPanelBuilder; @@ -206,11 +205,9 @@ public class AdminSettingsCommand extends CompositeCommand { switch (f.getType()) { case PROTECTION -> { island.setFlag(f, rank); - getIslands().save(island); } case SETTING -> { island.setSettingsFlag(f, activeState); - getIslands().save(island); } case WORLD_SETTING -> f.setSetting(getWorld(), activeState); default -> { @@ -226,12 +223,11 @@ public class AdminSettingsCommand extends CompositeCommand { user.sendMessage("general.errors.use-in-game"); return false; } - getPlayers().setFlagsDisplayMode(user.getUniqueId(), Mode.EXPERT); if (args.isEmpty()) { new TabbedPanelBuilder() .user(user) .world(getWorld()) - .tab(1, new SettingsTab(getWorld(), user, Flag.Type.WORLD_SETTING)) + .tab(1, new SettingsTab(getWorld(), user, Flag.Type.WORLD_SETTING, Flag.Mode.EXPERT)) .tab(2, new WorldDefaultSettingsTab(getWorld(), user)) .startingSlot(1) .size(54) @@ -242,8 +238,8 @@ public class AdminSettingsCommand extends CompositeCommand { new TabbedPanelBuilder() .user(user) .world(island.getWorld()) - .island(island).tab(1, new SettingsTab(user, Flag.Type.PROTECTION)) - .tab(2, new SettingsTab(user, Flag.Type.SETTING)) + .island(island).tab(1, new SettingsTab(getWorld(), user, Flag.Type.PROTECTION, Flag.Mode.EXPERT)) + .tab(2, new SettingsTab(getWorld(), user, Flag.Type.SETTING, Flag.Mode.EXPERT)) .startingSlot(1) .size(54) .build().openPanel(); 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..d4d730380 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,6 @@ 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); 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..79776ed5b 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,6 @@ public class NamePrompt extends StringPrompt { @Override public Prompt acceptInput(@NonNull ConversationContext context, String input) { if (island.renameHome(oldName, input)) { - plugin.getIslands().save(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/IslandSettingsCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSettingsCommand.java index e37fd8ff2..027a16961 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSettingsCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSettingsCommand.java @@ -49,7 +49,8 @@ public class IslandSettingsCommand extends CompositeCommand { .user(user) .island(island) .world(island.getWorld()) - .tab(1, new SettingsTab(user, Flag.Type.PROTECTION)).tab(2, new SettingsTab(user, Flag.Type.SETTING)) + .tab(1, new SettingsTab(getWorld(), user, Flag.Type.PROTECTION)) + .tab(2, new SettingsTab(getWorld(), user, Flag.Type.SETTING)) .startingSlot(1) .size(54) .hideIfEmpty() diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/Invite.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/Invite.java deleted file mode 100644 index c6328d066..000000000 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/Invite.java +++ /dev/null @@ -1,95 +0,0 @@ -package world.bentobox.bentobox.api.commands.island.team; - -import java.util.Objects; -import java.util.UUID; - -import world.bentobox.bentobox.database.objects.Island; - -/** - * Represents an invite - * @author tastybento - * @since 1.8.0 - */ -public class Invite { - - /** - * Type of invitation - * - */ - public enum Type { - COOP, - TEAM, - TRUST - } - - private final Type type; - private final UUID inviter; - private final UUID invitee; - private final Island island; - - /** - * @param type - invitation type, e.g., coop, team, trust - * @param inviter - UUID of inviter - * @param invitee - UUID of invitee - * @param island - the island this invite is for - */ - public Invite(Type type, UUID inviter, UUID invitee, Island island) { - this.type = type; - this.inviter = inviter; - this.invitee = invitee; - this.island = island; - } - - /** - * @return the type - */ - public Type getType() { - return type; - } - - /** - * @return the inviter - */ - public UUID getInviter() { - return inviter; - } - - /** - * @return the invitee - */ - public UUID getInvitee() { - return invitee; - } - - /** - * @return the island - */ - public Island getIsland() { - return island; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - return Objects.hash(invitee, inviter, type); - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Invite other)) { - return false; - } - return Objects.equals(invitee, other.invitee) && Objects.equals(inviter, other.inviter) && type == other.type; - } -} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommand.java index fd1d06e82..6cb702ea7 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommand.java @@ -1,9 +1,7 @@ package world.bentobox.bentobox.api.commands.island.team; import java.io.File; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.UUID; @@ -16,17 +14,13 @@ import world.bentobox.bentobox.api.events.team.TeamEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord.TemplateItem; import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.database.objects.TeamInvite; import world.bentobox.bentobox.managers.RanksManager; public class IslandTeamCommand extends CompositeCommand { - /** - * Invited list. Key is the invited party, value is the invite. - * @since 1.8.0 - */ - private final Map inviteMap; - private IslandTeamKickCommand kickCommand; private IslandTeamLeaveCommand leaveCommand; @@ -51,9 +45,11 @@ public class IslandTeamCommand extends CompositeCommand { private IslandTeamTrustCommand trustCommand; + private final Database handler; + public IslandTeamCommand(CompositeCommand parent) { super(parent, "team"); - inviteMap = new HashMap<>(); + handler = new Database<>(parent.getAddon(), TeamInvite.class); } @Override @@ -139,8 +135,8 @@ public class IslandTeamCommand extends CompositeCommand { * @param invitee - uuid of invitee * @since 1.8.0 */ - public void addInvite(Invite.Type type, @NonNull UUID inviter, @NonNull UUID invitee, @NonNull Island island) { - inviteMap.put(invitee, new Invite(type, inviter, invitee, island)); + public void addInvite(TeamInvite.Type type, @NonNull UUID inviter, @NonNull UUID invitee, @NonNull Island island) { + handler.saveObjectAsync(new TeamInvite(type, inviter, invitee, island.getUniqueId())); } /** @@ -150,7 +146,7 @@ public class IslandTeamCommand extends CompositeCommand { * @since 1.8.0 */ public boolean isInvited(@NonNull UUID invitee) { - return inviteMap.containsKey(invitee); + return handler.objectExists(invitee.toString()); } /** @@ -161,7 +157,7 @@ public class IslandTeamCommand extends CompositeCommand { */ @Nullable public UUID getInviter(UUID invitee) { - return isInvited(invitee) ? inviteMap.get(invitee).getInviter() : null; + return isInvited(invitee) ? handler.loadObject(invitee.toString()).getInviter() : null; } /** @@ -171,8 +167,8 @@ public class IslandTeamCommand extends CompositeCommand { * @since 1.8.0 */ @Nullable - public Invite getInvite(UUID invitee) { - return inviteMap.get(invitee); + public TeamInvite getInvite(UUID invitee) { + return handler.loadObject(invitee.toString()); } /** @@ -181,7 +177,7 @@ public class IslandTeamCommand extends CompositeCommand { * @since 1.8.0 */ public void removeInvite(@NonNull UUID invitee) { - inviteMap.remove(invitee); + handler.deleteID(invitee.toString()); } /** diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCoopCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCoopCommand.java index 246c38b21..45b84f904 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCoopCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCoopCommand.java @@ -8,10 +8,10 @@ import java.util.UUID; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.commands.island.team.Invite.Type; 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.database.objects.TeamInvite.Type; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; @@ -99,7 +99,7 @@ public class IslandTeamCoopCommand extends CompositeCommand { // Put the invited player (key) onto the list with inviter (value) // If someone else has invited a player, then this invite will overwrite the // previous invite! - itc.addInvite(Invite.Type.COOP, user.getUniqueId(), target.getUniqueId(), island); + itc.addInvite(Type.COOP, user.getUniqueId(), target.getUniqueId(), island); user.sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, target.getName()); // Send message to online player target.sendMessage("commands.island.team.coop.name-has-invited-you", TextVariables.NAME, diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamGUI.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamGUI.java index 598e940a9..602fa2881 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamGUI.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamGUI.java @@ -22,7 +22,6 @@ import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.commands.island.team.Invite.Type; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.api.panels.PanelItem; @@ -34,6 +33,8 @@ import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord.ActionRecord import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord.TemplateItem; 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.RanksManager; import world.bentobox.bentobox.util.Util; @@ -208,7 +209,7 @@ public class IslandTeamGUI { private PanelItem createInvitedButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { PanelItemBuilder builder = new PanelItemBuilder(); if (parent.isInvited(user.getUniqueId()) && user.hasPermission(parent.getAcceptCommand().getPermission())) { - Invite invite = parent.getInvite(user.getUniqueId()); + TeamInvite invite = parent.getInvite(user.getUniqueId()); if (invite == null) { return this.getBlankBorder(); } @@ -224,7 +225,8 @@ public class IslandTeamGUI { return builder.build(); } - private void createInviteClickHandler(PanelItemBuilder builder, Invite invite, @NonNull List list) { + private void createInviteClickHandler(PanelItemBuilder builder, TeamInvite invite, + @NonNull List list) { Type type = invite.getType(); builder.clickHandler((panel, user, clickType, clickSlot) -> { if (list.stream().noneMatch(ar -> clickType.equals(ar.clickType()))) { 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 88c91c162..3ba3013f1 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 @@ -5,13 +5,14 @@ import java.util.UUID; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.ConfirmableCommand; -import world.bentobox.bentobox.api.commands.island.team.Invite.Type; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.island.IslandEvent; 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.database.objects.TeamInvite; +import world.bentobox.bentobox.database.objects.TeamInvite.Type; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; @@ -49,7 +50,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { user.sendMessage(INVALID_INVITE); return false; } - Invite invite = itc.getInvite(playerUUID); + TeamInvite invite = itc.getInvite(playerUUID); if (invite.getType().equals(Type.TEAM)) { // Check rank to of inviter Island island = getIslands().getIsland(getWorld(), prospectiveOwnerUUID); @@ -78,7 +79,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { @Override public boolean execute(User user, String label, List args) { // Get the invite - Invite invite = itc.getInvite(user.getUniqueId()); + TeamInvite invite = itc.getInvite(user.getUniqueId()); switch (invite.getType()) { case COOP -> askConfirmation(user, () -> acceptCoopInvite(user, invite)); case TRUST -> askConfirmation(user, () -> acceptTrustInvite(user, invite)); @@ -94,11 +95,11 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { return true; } - void acceptTrustInvite(User user, Invite invite) { + void acceptTrustInvite(User user, TeamInvite invite) { // Remove the invite itc.removeInvite(user.getUniqueId()); User inviter = User.getInstance(invite.getInviter()); - Island island = invite.getIsland(); + Island island = getIslands().getIslandById(invite.getIslandID()).orElse(null); if (island != null) { if (island.getMemberSet(RanksManager.TRUSTED_RANK, false).size() > getIslands().getMaxMembers(island, RanksManager.TRUSTED_RANK)) { @@ -120,11 +121,11 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { } } - void acceptCoopInvite(User user, Invite invite) { + void acceptCoopInvite(User user, TeamInvite invite) { // Remove the invite itc.removeInvite(user.getUniqueId()); User inviter = User.getInstance(invite.getInviter()); - Island island = invite.getIsland(); + Island island = getIslands().getIslandById(invite.getIslandID()).orElse(null); if (island != null) { if (island.getMemberSet(RanksManager.COOP_RANK, false).size() > getIslands().getMaxMembers(island, RanksManager.COOP_RANK)) { @@ -146,13 +147,13 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { } } - void acceptTeamInvite(User user, Invite invite) { + void acceptTeamInvite(User user, TeamInvite invite) { // Remove the invite itc.removeInvite(user.getUniqueId()); // Get the player's island - may be null if the player has no island List islands = getIslands().getIslands(getWorld(), user.getUniqueId()); // Get the team's island - Island teamIsland = invite.getIsland(); + Island teamIsland = getIslands().getIslandById(invite.getIslandID()).orElse(null); if (teamIsland == null) { user.sendMessage(INVALID_INVITE); return; @@ -196,7 +197,6 @@ 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); // 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/IslandTeamInviteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java index 77a37dd30..82d2bf4dd 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java @@ -10,13 +10,13 @@ import java.util.UUID; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.commands.island.team.Invite.Type; 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.panels.reader.PanelTemplateRecord.TemplateItem; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.database.objects.TeamInvite.Type; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.PlayersManager; import world.bentobox.bentobox.managers.RanksManager; @@ -166,7 +166,7 @@ public class IslandTeamInviteCommand extends CompositeCommand { } // Put the invited player (key) onto the list with inviter (value) // If someone else has invited a player, then this invite will overwrite the previous invite! - itc.addInvite(Invite.Type.TEAM, user.getUniqueId(), invitedPlayer.getUniqueId(), island); + itc.addInvite(Type.TEAM, user.getUniqueId(), invitedPlayer.getUniqueId(), island); user.sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, invitedPlayer.getName(), TextVariables.DISPLAY_NAME, invitedPlayer.getDisplayName()); // Send message to online player invitedPlayer.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()); 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..67827a1d8 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 @@ -14,6 +14,7 @@ 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.managers.IslandsManager; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; @@ -91,7 +92,6 @@ 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); return true; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamTrustCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamTrustCommand.java index 8d57871df..38c0349f7 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamTrustCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamTrustCommand.java @@ -8,10 +8,10 @@ import java.util.UUID; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.commands.island.team.Invite.Type; 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.database.objects.TeamInvite.Type; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; diff --git a/src/main/java/world/bentobox/bentobox/api/flags/Flag.java b/src/main/java/world/bentobox/bentobox/api/flags/Flag.java index d28bc0add..89b13cb09 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/Flag.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/Flag.java @@ -388,21 +388,24 @@ public class Flag implements Comparable { if (!user.isOp() && invisible) { return null; } - // Start the flag conversion PanelItemBuilder pib = new PanelItemBuilder() .icon(ItemParser.parse(user.getTranslationOrNothing(this.getIconReference()), new ItemStack(icon))) - .name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME, user.getTranslation(getNameReference()))) + .name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME, + user.getTranslation(getNameReference()))) .clickHandler(clickHandler) .invisible(invisible); if (hasSubPanel()) { pib.description(user.getTranslation("protection.panel.flag-item.menu-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference()))); return pib.build(); } + return switch (getType()) { case PROTECTION -> createProtectionFlag(plugin, user, island, pib).build(); case SETTING -> createSettingFlag(user, island, pib).build(); case WORLD_SETTING -> createWorldSettingFlag(user, world, pib).build(); + }; + } private PanelItemBuilder createWorldSettingFlag(User user, World world, PanelItemBuilder pib) { @@ -429,19 +432,24 @@ public class Flag implements Comparable { private PanelItemBuilder createProtectionFlag(BentoBox plugin, User user, Island island, PanelItemBuilder pib) { if (island != null) { + int y = island.getFlag(this); // Protection flag + pib.description(user.getTranslation("protection.panel.flag-item.description-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference()))); + RanksManager.getInstance().getRanks().forEach((reference, score) -> { - if (score > RanksManager.BANNED_RANK && score < island.getFlag(this)) { + + if (score > RanksManager.BANNED_RANK && score < y) { pib.description(user.getTranslation("protection.panel.flag-item.blocked-rank") + user.getTranslation(reference)); - } else if (score <= RanksManager.OWNER_RANK && score > island.getFlag(this)) { + } else if (score <= RanksManager.OWNER_RANK && score > y) { pib.description(user.getTranslation("protection.panel.flag-item.allowed-rank") + user.getTranslation(reference)); - } else if (score == island.getFlag(this)) { + } else if (score == y) { pib.description(user.getTranslation("protection.panel.flag-item.minimal-rank") + user.getTranslation(reference)); } }); } + return pib; } @@ -469,7 +477,7 @@ public class Flag implements Comparable { public Set getSubflags() { return subflags; } - + /** * Set the name of this flag for a specified locale. This enables the flag's name to be assigned via API. It will not be stored anywhere * and must be rewritten using this call every time the flag is built. @@ -482,7 +490,7 @@ public class Flag implements Comparable { public boolean setTranslatedName(Locale locale, String name) { return BentoBox.getInstance().getLocalesManager().setTranslation(locale, getNameReference(), name); } - + /** * Set the name of this flag for a specified locale. This enables the flag's name to be assigned via API. It will not be stored anywhere * and must be rewritten using this call every time the flag is built. diff --git a/src/main/java/world/bentobox/bentobox/api/panels/TabbedPanel.java b/src/main/java/world/bentobox/bentobox/api/panels/TabbedPanel.java index cc1a41290..3c93ad8a6 100644 --- a/src/main/java/world/bentobox/bentobox/api/panels/TabbedPanel.java +++ b/src/main/java/world/bentobox/bentobox/api/panels/TabbedPanel.java @@ -74,6 +74,7 @@ public class TabbedPanel extends Panel implements PanelListener { * @param page - the page of the tab to show (if multi paged) */ public void openPanel(int activeTab, int page) { + if (!tpb.getTabs().containsKey(activeTab)) { // Request to open a non-existent tab throw new InvalidParameterException("Attempt to open a non-existent tab in a tabbed panel. Missing tab #" + activeTab); @@ -88,21 +89,17 @@ public class TabbedPanel extends Panel implements PanelListener { TreeMap items = new TreeMap<>(); // Get the tab Tab tab = tpb.getTabs().get(activeTab); - // Remove any tabs that have no items, if required if (tpb.isHideIfEmpty()) { tpb.getTabs().values().removeIf(t -> !t.equals(tab) && t.getPanelItems().stream().noneMatch(Objects::nonNull)); } - // Set up the tabbed header setupHeader(tab, items); - // Show the active tab if (tpb.getTabs().containsKey(activeTab)) { List panelItems = tab.getPanelItems(); // Adds the flag items panelItems.stream().filter(Objects::nonNull).skip(page * ITEMS_PER_PAGE).limit(page * ITEMS_PER_PAGE + ITEMS_PER_PAGE).forEach(i -> items.put(items.lastKey() + 1, i)); - // set up the footer setupFooter(items); // Add forward and backward icons @@ -182,6 +179,7 @@ public class TabbedPanel extends Panel implements PanelListener { // Reset the closed flag closed = false; } + } /** 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..2be8fa824 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -13,6 +13,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import org.bukkit.Bukkit; @@ -41,6 +42,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; @@ -56,7 +58,7 @@ import world.bentobox.bentobox.util.Util; public class Island implements DataObject, MetaDataAble { @Expose - private boolean primary; + private Set primaries = new HashSet<>(); /** * Set to true if this data object has been changed since being loaded from the @@ -243,7 +245,6 @@ public class Island implements DataObject, MetaDataAble { range = BentoBox.getInstance().getIWM().getIslandDistance(world); this.protectionRange = protectionRange; this.maxEverProtectionRange = protectionRange; - this.setChanged(); } /** @@ -290,6 +291,7 @@ public class Island implements DataObject, MetaDataAble { this.updatedDate = island.getUpdatedDate(); this.world = island.getWorld(); this.bonusRanges.addAll(island.getBonusRanges()); + this.primaries.addAll(island.getPrimaries()); this.setChanged(); } @@ -304,8 +306,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 +324,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 +1012,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 (this.center == null || !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 +1050,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(); } /** @@ -1078,7 +1092,6 @@ public class Island implements DataObject, MetaDataAble { .forEach(f -> result.put(f.getID(), plugin.getIWM().getDefaultIslandSettings(world).getOrDefault(f, f.getDefaultRank()))); this.setFlags(result); - 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(); } /** @@ -1202,14 +1222,33 @@ public class Island implements DataObject, MetaDataAble { * @param rank rank value * @since 1.1 */ - public void setRank(@Nullable UUID uuid, int rank) { + public void setRank(@Nullable UUID uuid, int newRank) { + // Early return if the UUID is null, to avoid unnecessary processing. if (uuid == null) { - return; // Defensive code + return; + } + + // Use an AtomicBoolean to track if the member's rank has been changed. + AtomicBoolean isRankChanged = new AtomicBoolean(false); + + // Attempt to update the member's rank, if necessary. + members.compute(uuid, (key, existingRank) -> { + // If the member does not exist or their rank is different, update the rank. + if (existingRank == null || existingRank != newRank) { + isRankChanged.set(true); + return newRank; // Update the rank. + } + // No change needed; return the existing rank. + return existingRank; + }); + + // If the rank was changed, notify the change and log the update. + if (isRankChanged.get()) { + setChanged(); // Notify that a change has occurred. } - members.put(uuid, rank); - setChanged(); } + /** * @param ranks the ranks to set */ @@ -1266,7 +1305,6 @@ public class Island implements DataObject, MetaDataAble { @Override public void setUniqueId(String uniqueId) { this.uniqueId = uniqueId; - setChanged(); } /** @@ -1274,7 +1312,6 @@ public class Island implements DataObject, MetaDataAble { */ public void setUpdatedDate(long updatedDate) { this.updatedDate = updatedDate; - setChanged(); } /** @@ -1347,8 +1384,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 +1410,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 +1498,6 @@ public class Island implements DataObject, MetaDataAble { */ public void setGameMode(String gameMode) { this.gameMode = gameMode; - setChanged(); } /** @@ -1518,8 +1560,9 @@ public class Island implements DataObject, MetaDataAble { if (cooldowns.containsKey(flag.getID()) && cooldowns.get(flag.getID()) > System.currentTimeMillis()) { return true; } - cooldowns.remove(flag.getID()); - setChanged(); + if (cooldowns.remove(flag.getID()) != null) { + setChanged(); + } return false; } @@ -1603,8 +1646,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 +1672,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 +1708,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 +1744,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 +1796,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 +1821,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 +1835,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 +1878,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 +1900,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 +1928,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 +1997,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(); + } } /** @@ -1936,18 +2011,30 @@ public class Island implements DataObject, MetaDataAble { } /** + * @param userID user UUID * @return the primary */ - public boolean isPrimary() { - return primary; + public boolean isPrimary(UUID userID) { + return getPrimaries().contains(userID); } /** * @param primary the primary to set */ - public void setPrimary(boolean primary) { - this.primary = primary; - setChanged(); + public void setPrimary(UUID userID) { + if (getPrimaries().add(userID)) { + setChanged(); + } + } + + /** + * Remove the primary island + * @param userID user UUID + */ + public void removePrimary(UUID userID) { + if (getPrimaries().remove(userID)) { + setChanged(); + } } /** @@ -1986,4 +2073,41 @@ public class Island implements DataObject, MetaDataAble { + commandRanks + ", reserved=" + reserved + ", metaData=" + metaData + ", homes=" + homes + ", maxHomes=" + maxHomes + "]"; } + + /** + * @return the primaries + */ + public Set getPrimaries() { + if (primaries == null) { + primaries = new HashSet<>(); + } + return primaries; + } + + /** + * @param primaries the primaries to set + */ + public void setPrimaries(Set primaries) { + this.primaries = primaries; + setChanged(); + } + + @Override + public int hashCode() { + return Objects.hash(uniqueId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Island other = (Island) obj; + return Objects.equals(uniqueId, other.uniqueId); + } + + } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/Players.java b/src/main/java/world/bentobox/bentobox/database/objects/Players.java index 3cf06d2c0..ca384ed12 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Players.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Players.java @@ -6,13 +6,10 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; -import org.eclipse.jdt.annotation.Nullable; import com.google.gson.annotations.Expose; @@ -30,8 +27,6 @@ import world.bentobox.bentobox.util.Util; */ @Table(name = "Players") public class Players implements DataObject, MetaDataAble { - @Expose - private Map homeLocations = new HashMap<>(); @Expose private String uniqueId; @Expose @@ -77,7 +72,6 @@ public class Players implements DataObject, MetaDataAble { */ public Players(BentoBox plugin, UUID uniqueId) { this.uniqueId = uniqueId.toString(); - homeLocations = new HashMap<>(); locale = ""; // Try to get player's name this.playerName = Bukkit.getOfflinePlayer(uniqueId).getName(); @@ -86,72 +80,6 @@ public class Players implements DataObject, MetaDataAble { } } - /** - * Gets the default home location. - * @param world - world to check - * @return Location - home location in world - * @deprecated Homes are stored in the Island object now - */ - @Deprecated(since="1.18.0", forRemoval=true) - @Nullable - public Location getHomeLocation(World world) { - return getHomeLocation(world, 1); // Default - } - - /** - * Gets the home location by number for world - * @param world - includes world and any related nether or end worlds - * @param number - a number - * @return Location of this home or null if not available - * @deprecated Homes are stored in the island object now - */ - @Deprecated(since="1.18.0", forRemoval=true) - @Nullable - public Location getHomeLocation(World world, int number) { - // Remove any lost worlds/locations - homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null); - return homeLocations.entrySet().stream() - .filter(en -> Util.sameWorld(en.getKey().getWorld(), world) && en.getValue() == number) - .map(Map.Entry::getKey) - .findFirst() - .orElse(null); - } - - /** - * @param world - world - * @return Map of home locations - * @deprecated Homes are stored in the island object now - */ - @Deprecated(since="1.18.0", forRemoval=true) - public Map getHomeLocations(World world) { - // Remove any lost worlds/locations - homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null); - return homeLocations.entrySet().stream().filter(e -> Util.sameWorld(e.getKey().getWorld(),world)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - /** - * @return the homeLocations - * @deprecated Homes are stored in the Island object now - */ - @Deprecated(since="1.18.0", forRemoval=true) - public Map getHomeLocations() { - // Remove any lost worlds/locations - homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null); - return homeLocations; - } - - /** - * @param homeLocations the homeLocations to set - * @deprecated Homes are stored in the Island object now - */ - @Deprecated(since="1.18.0", forRemoval=true) - public void setHomeLocations(Map homeLocations) { - this.homeLocations = homeLocations; - // Remove any lost worlds/locations - homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null); - } - /** * @param playerName the playerName to set */ @@ -202,30 +130,6 @@ public class Players implements DataObject, MetaDataAble { this.resets.put(world.getName(), resets); } - /** - * Stores the home location of the player in a String format - * - * @param l a Bukkit location - * @deprecated Home locations are stored in islands - */ - @Deprecated(since="1.18.0", forRemoval=true) - public void setHomeLocation(final Location l) { - setHomeLocation(l, 1); - } - - /** - * Stores the numbered home location of the player. Numbering starts at 1. - * @param location - the location - * @param number - a number - * @deprecated Home locations are no longer stored for players. They are stored in islands. - */ - @Deprecated(since="1.18.0", forRemoval=true) - public void setHomeLocation(Location location, int number) { - // Remove any home locations in the same world with the same number - homeLocations.entrySet().removeIf(e -> e.getKey() == null || (Util.sameWorld(location.getWorld(), e.getKey().getWorld()) && e.getValue().equals(number))); - homeLocations.put(location, number); - } - /** * Set the uuid for this player object * @param uuid - UUID @@ -234,16 +138,6 @@ public class Players implements DataObject, MetaDataAble { uniqueId = uuid.toString(); } - /** - * Clears all home Locations in world - * @param world - world - * @deprecated Home locations are no longer stored for players. Use {@link world.bentobox.bentobox.managers.IslandsManager} - */ - @Deprecated(since="1.18.0", forRemoval=true) - public void clearHomeLocations(World world) { - homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null || Util.sameWorld(l.getWorld(), world)); - } - /** * @return the locale */ @@ -350,24 +244,6 @@ public class Players implements DataObject, MetaDataAble { } } - /** - * Returns the display mode for the Flags in the Settings Panel. - * @return the display mode for the Flags in the Settings Panel. - * @since 1.6.0 - */ - public Flag.Mode getFlagsDisplayMode() { - return flagsDisplayMode; - } - - /** - * Sets the display mode for the Flags in the Settings Panel. - * @param flagsDisplayMode the display mode for the Flags in the Settings Panel. - * @since 1.6.0 - */ - public void setFlagsDisplayMode(Flag.Mode flagsDisplayMode) { - this.flagsDisplayMode = flagsDisplayMode; - } - /** * @return the metaData * @since 1.15.5 diff --git a/src/main/java/world/bentobox/bentobox/database/objects/TeamInvite.java b/src/main/java/world/bentobox/bentobox/database/objects/TeamInvite.java new file mode 100644 index 000000000..70634fc4b --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/database/objects/TeamInvite.java @@ -0,0 +1,112 @@ +package world.bentobox.bentobox.database.objects; + +import java.util.Objects; +import java.util.UUID; + +import com.google.gson.annotations.Expose; + +/** + * Data object for team invites + */ +@Table(name = "TeamInvites") +public class TeamInvite implements DataObject { + + /** + * Type of invitation + * + */ + public enum Type { + COOP, + TEAM, + TRUST + } + + @Expose + private Type type; + @Expose + private UUID inviter; + @Expose + private String islandID; + + @Expose + private String uniqueId; + + /** + * @param type - invitation type, e.g., coop, team, trust + * @param inviter - UUID of inviter + * @param invitee - UUID of invitee + * @param island - the unique ID of the island this invite is for + */ + public TeamInvite(Type type, UUID inviter, UUID invitee, String islandID) { + this.type = type; + this.uniqueId = invitee.toString(); + this.inviter = inviter; + this.islandID = islandID; + } + + @Override + public String getUniqueId() { + // Inviter + return this.uniqueId; + } + + @Override + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + /** + * @return the type + */ + public Type getType() { + return type; + } + + /** + * @return the invitee + */ + public UUID getInvitee() { + return UUID.fromString(uniqueId); + } + + /** + * @return the inviter + */ + public UUID getInviter() { + return inviter; + } + + /** + * @return the islandID + */ + public String getIslandID() { + return islandID; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return Objects.hash(inviter, uniqueId, type); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof TeamInvite other)) { + return false; + } + return Objects.equals(inviter, other.inviter) && Objects.equals(uniqueId, other.getUniqueId()) + && type == other.type; + } + +} diff --git a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index f73f3dac3..0fdee8507 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; @@ -61,7 +62,7 @@ public class JoinLeaveListener implements Listener { // Make sure the player is loaded into the cache or create the player if they // don't exist - players.addPlayer(playerUUID); + players.getPlayer(playerUUID); // Reset island resets if required plugin.getIWM().getOverWorlds().stream() @@ -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); } @@ -110,7 +110,7 @@ public class JoinLeaveListener implements Listener { private void firstTime(User user) { // Make sure the player is loaded into the cache or create the player if they // don't exist - players.addPlayer(user.getUniqueId()); + players.getPlayer(user.getUniqueId()); plugin.getIWM().getOverWorlds().stream().filter(w -> plugin.getIWM().isCreateIslandOnFirstLoginEnabled(w)) .forEach(w -> { @@ -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/PanelListenerManager.java b/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java index 53d402c5e..6cb57fd45 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java +++ b/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java @@ -61,6 +61,7 @@ public class PanelListenerManager implements Listener { // Refresh l.refreshPanel(); }); + } else { // Wrong name - delete this panel openPanels.remove(user.getUniqueId()); 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..d5891191b 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 @@ -60,9 +60,6 @@ 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); - } 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..f1741721f 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -37,6 +37,10 @@ import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import com.github.puregero.multilib.MultiLib; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.IslandBaseEvent; @@ -44,9 +48,9 @@ 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.json.BentoboxTypeAdapterFactory; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.IslandDeletion; import world.bentobox.bentobox.lists.Flags; @@ -65,29 +69,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 +99,47 @@ 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 -> { + Island island = handler.loadObject(id); + if (island != null) { + islandCache.updateIsland(island); + } + }); + + // Delete island blocks + MultiLib.onString(plugin, "bentobox-deleteIsland", id -> { + IslandDeletion idd = getGson().fromJson(id, IslandDeletion.class); + plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(idd); + }); + // List for new islands + MultiLib.onString(plugin, "bentobox-newIsland", id -> { + Island island = handler.loadObject(id); + if (island != null) { + islandCache.addIsland(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)); + } + } + + }); } /** @@ -121,8 +147,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 +253,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; @@ -257,27 +282,40 @@ public class IslandsManager { // Set the owner of the island to no one. island.setOwner(null); island.setFlag(Flags.LOCK, RanksManager.VISITOR_RANK); + island.setDeleted(true); 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); // Remove players from island removePlayersFromIsland(island); if (!plugin.getSettings().isKeepPreviousIslandOnReset()) { // Remove blocks from world - plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(new IslandDeletion(island)); + IslandDeletion id = new IslandDeletion(island); + plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(id); + // Tell other servers + MultiLib.notify("bentobox-deleteIsland", getGson().toJson(id)); } + // Delete the island from the database + handler.deleteObject(island); } } + private Gson getGson() { + + // Build the Gson + + // excludeFieldsWithoutExposeAnnotation - this means that every field to be stored should use @Expose + // enableComplexMapKeySerialization - forces GSON to use TypeAdapters even for Map keys + GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation() + .enableComplexMapKeySerialization().setPrettyPrinting(); + // Register adapter factory + builder.registerTypeAdapterFactory(new BentoboxTypeAdapterFactory(plugin)); + // Allow characters like < or > without escaping them + builder.disableHtmlEscaping(); + + return builder.create(); + } + /** * Get the number of islands made on this server. Used by stats. * @@ -462,7 +500,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 +524,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 @@ -507,8 +545,11 @@ public class IslandsManager { islandMax = owner.getPermissionValue(plugin.getIWM().getPermissionPrefix(island.getWorld()) + perm, islandMax); } - island.setMaxMembers(rank, islandMax == worldDefault ? null : islandMax); - this.save(island); + Integer change = islandMax == worldDefault ? null : islandMax; + if (island.getMaxMembers().get(rank) != change) { + island.setMaxMembers(rank, change); + updateIsland(island); + } return islandMax; } @@ -546,13 +587,16 @@ 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); + Integer change = islandMax == plugin.getIWM().getMaxHomes(island.getWorld()) ? null : islandMax; + if (island.getMaxHomes() != change) { + island.setMaxHomes(change); + 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 @@ -735,9 +779,9 @@ public class IslandsManager { * @since 1.16.0 */ public boolean setHomeLocation(@Nullable Island island, Location location, String name) { - if (island != null) { + if (island != null && (island.getHome(name) == null || !island.getHome(name).equals(location))) { island.addHome(name, location); - this.save(island); + updateIsland(island); return true; } return false; @@ -890,7 +934,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 +945,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 +1176,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 +1188,14 @@ 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); - } + if (spawn.getWorld() != null) { + spawns.put(Util.getWorld(spawn.getWorld()), spawn); + // Tell other servers + MultiLib.notify("bentobox-setspawn", spawn.getWorld().getUID().toString() + "," + spawn.getUniqueId()); } - this.spawn.put(spawn.getWorld(), spawn); - spawn.setSpawn(true); } + /** * Clears the spawn island for this world * @@ -1164,11 +1203,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 +1229,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 +1242,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 +1252,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 +1418,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 +1492,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 +1631,16 @@ 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())); + } } /** @@ -1628,108 +1655,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..0137c8ee5 100644 --- a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java @@ -4,21 +4,18 @@ 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.Objects; 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; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.objects.Island; @@ -31,11 +28,9 @@ public class PlayersManager { private final BentoBox plugin; private Database handler; private final Database names; + private final Map playerCache = new HashMap<>(); - private final Map playerCache; - private final Set inTeleport; - - private boolean isSaveTaskRunning; + private final Set inTeleport; // this needs databasing /** * Provides a memory cache of online player information @@ -50,7 +45,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<>(); } @@ -62,67 +56,7 @@ public class PlayersManager { this.handler = handler; } - /** - * 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(); } @@ -134,11 +68,29 @@ public class PlayersManager { @Nullable public Players getPlayer(UUID uuid){ if (!playerCache.containsKey(uuid)) { - addPlayer(uuid); + playerCache.put(uuid, addPlayer(uuid)); } return playerCache.get(uuid); } + /** + * Adds a player to the database. If the UUID does not exist, a new player is made + * @param playerUUID - the player's UUID + */ + private Players addPlayer(@NonNull UUID playerUUID) { + Objects.requireNonNull(playerUUID); + // If the player is in the database, load it, otherwise create a new player + if (handler.objectExists(playerUUID.toString())) { + Players player = handler.loadObject(playerUUID.toString()); + if (player != null) { + return player; + } + } + Players player = new Players(plugin, playerUUID); + handler.saveObject(player); + return player; + } + /** * Returns an unmodifiable collection of all the players that are currently in the cache. * @return unmodifiable collection containing every player in the cache. @@ -146,37 +98,7 @@ public class PlayersManager { */ @NonNull public Collection getPlayers() { - return Collections.unmodifiableCollection(playerCache.values()); - } - - /* - * Cache control methods - */ - - /** - * Adds a player to the cache. 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; - // 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()); - } - } else { - player = new Players(plugin, playerUUID); - } - playerCache.put(playerUUID, player); - } + return Collections.unmodifiableCollection(handler.loadObjects()); } /** @@ -187,7 +109,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 uniqueID == null ? false : handler.objectExists(uniqueID.toString()); } /** @@ -206,11 +128,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 +137,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 = getPlayer(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 +157,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 +168,7 @@ public class PlayersManager { * @return number of resets */ public int getResets(World world, UUID playerUUID) { - addPlayer(playerUUID); - return playerCache.get(playerUUID).getResets(world); + return getPlayer(playerUUID).getResets(world); } /** @@ -261,7 +180,7 @@ public class PlayersManager { * @see #getResets(World, UUID) */ public int getResetsLeft(World world, UUID playerUUID) { - addPlayer(playerUUID); + getPlayer(playerUUID); if (plugin.getIWM().getResetLimit(world) == -1) { return -1; } else { @@ -277,8 +196,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 = getPlayer(playerUUID); + p.setResets(world, resets); + handler.saveObject(p); } /** @@ -287,11 +207,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 getPlayer(playerUUID).getLocale(); } /** @@ -300,8 +216,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 = getPlayer(playerUUID); + p.setLocale(localeName); + handler.saveObject(p); } /** @@ -310,8 +227,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 = getPlayer(playerUUID); + p.addDeath(world); + handler.saveObject(p); } /** @@ -321,8 +239,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 = getPlayer(playerUUID); + p.setDeaths(world, deaths); + handler.saveObject(p); } /** @@ -332,8 +251,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 getPlayer(playerUUID).getDeaths(world); } /** @@ -360,16 +278,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,41 +303,17 @@ public class PlayersManager { * @param playerUUID player's UUID */ public void addReset(World world, UUID playerUUID) { - addPlayer(playerUUID); - playerCache.get(playerUUID).addReset(world); + Players p = getPlayer(playerUUID); + p.addReset(world); + handler.saveObject(p); } /** - * Sets the Flags display mode for the Settings Panel for this player. - * @param playerUUID player's UUID - * @param displayMode the {@link Flag.Mode} to set - * @since 1.6.0 - */ - public void setFlagsDisplayMode(UUID playerUUID, Flag.Mode displayMode) { - addPlayer(playerUUID); - playerCache.get(playerUUID).setFlagsDisplayMode(displayMode); - } - - /** - * Returns the Flags display mode for the Settings Panel for this player. - * @param playerUUID player's UUID - * @return the {@link Flag.Mode display mode} for the Flags in the Settings Panel. - * @since 1.6.0 - */ - public Flag.Mode getFlagsDisplayMode(UUID playerUUID) { - addPlayer(playerUUID); - return playerCache.get(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 +379,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..3846c1a21 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java @@ -9,6 +9,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -54,6 +55,159 @@ public class IslandCache { grids = new HashMap<>(); } + /** + * Replace the island we have with this one + * @param newIsland island + */ + public void updateIsland(@NonNull Island newIsland) { + if (newIsland.isDeleted()) { + this.deleteIslandFromCache(newIsland); + return; + } + // Get the old island + Island oldIsland = islandsById.get(newIsland.getUniqueId()); + compareIslands(oldIsland, newIsland); + Set newMembers = newIsland.getMembers().keySet(); + if (oldIsland != null) { + Set oldMembers = oldIsland.getMembers().keySet(); + // Remove any members who are not in the new island + for (UUID oldMember : oldMembers) { + if (!newMembers.contains(oldMember)) { + // Member has been removed - remove island + islandsByUUID.computeIfAbsent(oldMember, k -> new HashSet<>()).remove(oldIsland); + } + } + } + // Update the members with the new island object + for (UUID newMember : newMembers) { + Set set = islandsByUUID.computeIfAbsent(newMember, k -> new HashSet<>()); + set.remove(oldIsland); + set.add(newIsland); + islandsByUUID.put(newMember, set); + } + + if (islandsByLocation.put(newIsland.getCenter(), newIsland) == null) { + BentoBox.getInstance().logError("islandsByLocation failed to update"); + + } + if (islandsById.put(newIsland.getUniqueId(), newIsland) == null) { + BentoBox.getInstance().logError("islandsById failed to update"); + } + + } + + /** + * TODO REMOVE THIS DEBUG METHOD + * @param island1 island1 + * @param island2 island 2 + */ + public void compareIslands(Island island1, Island island2) { + if (island1 == null || island2 == null) { + BentoBox.getInstance().logDebug("One or both islands are null. Cannot compare."); + return; + } + + if (!island1.getUniqueId().equals(island2.getUniqueId())) { + BentoBox.getInstance().logDebug("Island unique IDs are different."); + } + + if (island1.isDeleted() != island2.isDeleted()) { + BentoBox.getInstance().logDebug("Island deleted states are different."); + } + + if (!Objects.equals(island1.getCenter(), island2.getCenter())) { + BentoBox.getInstance().logDebug("Island centers are different."); + } + + if (island1.getRange() != island2.getRange()) { + BentoBox.getInstance().logDebug("Island ranges are different."); + } + + if (island1.getProtectionRange() != island2.getProtectionRange()) { + BentoBox.getInstance().logDebug("Island protection ranges are different."); + } + + if (!island1.getBonusRanges().equals(island2.getBonusRanges())) { + BentoBox.getInstance().logDebug("Island bonus ranges are different."); + } + + if (island1.getMaxEverProtectionRange() != island2.getMaxEverProtectionRange()) { + BentoBox.getInstance().logDebug("Island max ever protection ranges are different."); + } + + if (!island1.getWorld().equals(island2.getWorld())) { + BentoBox.getInstance().logDebug("Island worlds are different."); + } + + if (!Objects.equals(island1.getGameMode(), island2.getGameMode())) { + BentoBox.getInstance().logDebug("Island game modes are different."); + } + + if (!Objects.equals(island1.getName(), island2.getName())) { + BentoBox.getInstance().logDebug("Island names are different."); + } + + if (island1.getCreatedDate() != island2.getCreatedDate()) { + BentoBox.getInstance().logDebug("Island created dates are different."); + } + + if (island1.getUpdatedDate() != island2.getUpdatedDate()) { + BentoBox.getInstance().logDebug("Island updated dates are different."); + } + + if (!Objects.equals(island1.getOwner(), island2.getOwner())) { + BentoBox.getInstance().logDebug("Island owners are different."); + } + + if (!island1.getMembers().equals(island2.getMembers())) { + BentoBox.getInstance().logDebug("Island members are different."); + } + + if (!Objects.equals(island1.getMaxMembers(), island2.getMaxMembers())) { + BentoBox.getInstance().logDebug("Island max members are different."); + } + + if (island1.isSpawn() != island2.isSpawn()) { + BentoBox.getInstance().logDebug("Island spawn states are different."); + } + + if (!island1.getFlags().equals(island2.getFlags())) { + BentoBox.getInstance().logDebug("Island flags are different."); + } + + if (!island1.getHistory().equals(island2.getHistory())) { + BentoBox.getInstance().logDebug("Island histories are different."); + } + + if (!island1.getSpawnPoint().equals(island2.getSpawnPoint())) { + BentoBox.getInstance().logDebug("Island spawn points are different."); + } + + if (island1.isDoNotLoad() != island2.isDoNotLoad()) { + BentoBox.getInstance().logDebug("Island do not load states are different."); + } + + if (!island1.getCooldowns().equals(island2.getCooldowns())) { + BentoBox.getInstance().logDebug("Island cooldowns are different."); + } + + if (!Objects.equals(island1.getCommandRanks(), island2.getCommandRanks())) { + BentoBox.getInstance().logDebug("Island command ranks are different."); + } + + if (!Objects.equals(island1.getMetaData(), island2.getMetaData())) { + BentoBox.getInstance().logDebug("Island metadata are different."); + } + + if (!Objects.equals(island1.getHomes(), island2.getHomes())) { + BentoBox.getInstance().logDebug("Island homes are different."); + } + + if (!Objects.equals(island1.getMaxHomes(), island2.getMaxHomes())) { + BentoBox.getInstance().logDebug("Island max homes are different."); + } + } + /** * Adds an island to the grid * @@ -62,12 +216,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); @@ -100,7 +249,7 @@ public class IslandCache { * @return true if successfully added, false if not */ private boolean addToGrid(@NonNull Island newIsland) { - return grids.computeIfAbsent(newIsland.getWorld(), k -> new IslandGrid()).addToGrid(newIsland); + return grids.computeIfAbsent(newIsland.getWorld(), k -> new IslandGrid(this)).addToGrid(newIsland); } public void clear() { @@ -117,13 +266,16 @@ public class IslandCache { */ public boolean deleteIslandFromCache(@NonNull Island island) { if (!islandsByLocation.remove(island.getCenter(), island)) { + // Already deleted return false; } 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 +296,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; } /** @@ -178,13 +329,13 @@ public class IslandCache { return null; } for (Island island : islands) { - if (island.isPrimary()) { + if (island.isPrimary(uuid)) { return island; } } // If there is no primary set, then set one - it doesn't matter which. Island result = islands.iterator().next(); - result.setPrimary(true); + result.setPrimary(uuid); return result; } @@ -212,8 +363,16 @@ public class IslandCache { * @param island island to make primary */ public void setPrimaryIsland(@NonNull UUID uuid, @NonNull Island island) { + if (island.getPrimaries().contains(uuid)) { + return; + } for (Island is : getIslands(island.getWorld(), uuid)) { - is.setPrimary(island.equals(is)); + if (is.getPrimaries().contains(uuid)) { + is.removePrimary(uuid); + } + if (is.equals(island)) { + is.setPrimary(uuid); + } } } @@ -326,6 +485,7 @@ public class IslandCache { islandSet.remove(island); } island.removeMember(uuid); + island.removePrimary(uuid); } /** @@ -375,27 +535,6 @@ public class IslandCache { return islandsById.get(uniqueId); } - /** - * Removes an island from the cache completely without altering the island - * object - * - * @param island - island to remove - * @since 1.3.0 - */ - public void removeIsland(@NonNull Island island) { - islandsByLocation.values().removeIf(island::equals); - islandsById.values().removeIf(island::equals); - islandsByUUID.values().removeIf(island::equals); - World w = Util.getWorld(island.getWorld()); - if (w == null) { - return; - } - - if (grids.containsKey(w)) { - grids.get(w).removeFromGrid(island); - } - } - /** * Resets all islands in this game mode to default flag settings * 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..3974b44e4 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandGrid.java @@ -3,7 +3,6 @@ package world.bentobox.bentobox.managers.island; import java.util.Map.Entry; import java.util.TreeMap; -import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.database.objects.Island; /** @@ -12,8 +11,16 @@ import world.bentobox.bentobox.database.objects.Island; * */ class IslandGrid { - private final TreeMap> grid = new TreeMap<>(); - private final BentoBox plugin = BentoBox.getInstance(); + private final TreeMap> grid = new TreeMap<>(); + private final IslandCache im; + + /** + * @param im IslandsManager + */ + public IslandGrid(IslandCache im) { + super(); + this.im = im; + } /** * Adds island to grid @@ -21,50 +28,23 @@ class IslandGrid { * @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()); + 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()))) { + return true; } return false; } else { // Add island - zEntry.put(island.getMinZ(), island); + zEntry.put(island.getMinZ(), island.getUniqueId()); grid.put(island.getMinX(), zEntry); } } else { // Add island - TreeMap zEntry = new TreeMap<>(); - zEntry.put(island.getMinZ(), island); + TreeMap zEntry = new TreeMap<>(); + zEntry.put(island.getMinZ(), island.getUniqueId()); grid.put(island.getMinX(), zEntry); } return true; @@ -76,43 +56,48 @@ class IslandGrid { * @return true if island existed and was deleted, false if there was nothing to delete */ public boolean removeFromGrid(Island island) { - // Remove from grid - if (island != null) { - int x = island.getMinX(); - int z = island.getMinZ(); - if (grid.containsKey(x)) { - TreeMap zEntry = grid.get(x); - if (zEntry.containsKey(z)) { - // Island exists - delete it - zEntry.remove(z); - grid.put(x, zEntry); - return true; - } - } - } - return false; - } + String id = island.getUniqueId(); + boolean removed = grid.values().stream() + .anyMatch(innerMap -> innerMap.values().removeIf(innerValue -> innerValue.equals(id))); + grid.values().removeIf(TreeMap::isEmpty); + + return removed; + } + /** - * Returns the island at the x,z location or null if there is none. - * This includes the full island space, not just the protected area. - * - * @param x - x coordinate - * @param z - z coordinate - * @return Island or null - */ + * Retrieves the island located at the specified x and z coordinates, covering both the protected area + * and the full island space. Returns null if no island exists at the given location. + * + * @param x the x coordinate of the location + * @param z the z coordinate of the location + * @return the Island at the specified location, or null if no island is found + */ public Island getIslandAt(int x, int z) { - Entry> en = grid.floorEntry(x); - if (en != null) { - Entry ent = en.getValue().floorEntry(z); - if (ent != null) { - // Check if in the island range - Island island = ent.getValue(); - if (island.inIslandSpace(x, z)) { - return island; - } - } + // Attempt to find the closest x-coordinate entry that does not exceed 'x' + Entry> xEntry = grid.floorEntry(x); + if (xEntry == null) { + return null; // No x-coordinate entry found, return null } + + // Attempt to find the closest z-coordinate entry that does not exceed 'z' within the found x-coordinate + Entry zEntry = xEntry.getValue().floorEntry(z); + if (zEntry == null) { + return null; // No z-coordinate entry found, return null + } + + // Retrieve the island using the id found in the z-coordinate entry + Island island = im.getIslandById(zEntry.getValue()); + if (island == null) { + return null; // No island found by the id, return null + } + // Check if the specified coordinates are within the island space + if (island.inIslandSpace(x, z)) { + return island; // Coordinates are within island space, return the island + } + + // Coordinates are outside the island space, return null return null; } + } 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..bb4f2cb22 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java @@ -19,6 +19,7 @@ import world.bentobox.bentobox.api.events.island.IslandResetEvent; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.BlueprintsManager; +import world.bentobox.bentobox.managers.IslandsManager; /** * Create and paste a new island @@ -217,7 +218,7 @@ public class NewIsland { // Register metrics plugin.getMetrics().ifPresent(BStats::increaseIslandsCreatedCount); // Save island - plugin.getIslands().save(island); + IslandsManager.updateIsland(island); } /** @@ -266,8 +267,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/main/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanel.java b/src/main/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanel.java index 681c3cd36..7b8269fdb 100644 --- a/src/main/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanel.java @@ -489,7 +489,7 @@ public class IslandCreationPanel long uses = plugin.getIslands().getIslands(world, user).stream() .filter(is -> is.getMetaData("bundle") .map(mdv -> bundle.getDisplayName().equalsIgnoreCase(mdv.asString()) - && !(reset && is.isPrimary())) // If this is a reset, then ignore the use of the island being reset + && !(reset && is.isPrimary(user.getUniqueId()))) // If this is a reset, then ignore the use of the island being reset .orElse(false)) .count(); builder.description(this.user.getTranslation(BUNDLE_BUTTON_REF + "uses", TextVariables.NUMBER, diff --git a/src/main/java/world/bentobox/bentobox/panels/settings/SettingsTab.java b/src/main/java/world/bentobox/bentobox/panels/settings/SettingsTab.java index 547238e7c..b558cc9d4 100644 --- a/src/main/java/world/bentobox/bentobox/panels/settings/SettingsTab.java +++ b/src/main/java/world/bentobox/bentobox/panels/settings/SettingsTab.java @@ -4,6 +4,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.stream.Collectors; import org.bukkit.ChatColor; @@ -16,6 +17,7 @@ import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.flags.Flag; +import world.bentobox.bentobox.api.flags.Flag.Mode; import world.bentobox.bentobox.api.flags.Flag.Type; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.panels.Panel; @@ -46,16 +48,7 @@ public class SettingsTab implements Tab, ClickHandler { protected Island island; protected TabbedPanel parent; - /** - * Show a tab of settings - * @param user - user who is viewing the tab - * @param type - flag type - */ - public SettingsTab(User user, Type type) { - this.user = user; - this.type = type; - // Island and world are set when the parent is set. - } + private Map currentMode = new HashMap<>(); /** * Show a tab of settings @@ -69,6 +62,21 @@ public class SettingsTab implements Tab, ClickHandler { this.type = type; } + /** + * Show a tab of settings + * @param world - world + * @param user - user who is viewing the tab + * @param type - flag type + * @param defaultMode - the default mode to show + * @since 2.4.0 + */ + public SettingsTab(World world, User user, Type type, Flag.Mode defaultMode) { + this.world = world; + this.user = user; + this.type = type; + currentMode.put(user.getUniqueId(), defaultMode); + } + /** * @return list of flags that will be shown in this panel */ @@ -81,7 +89,7 @@ public class SettingsTab implements Tab, ClickHandler { // Remove any that are not for this game mode plugin.getIWM().getAddon(world).ifPresent(gm -> flags.removeIf(f -> !f.getGameModes().isEmpty() && !f.getGameModes().contains(gm))); // Remove any that are the wrong rank or that will be on the top row - Flag.Mode mode = plugin.getPlayers().getFlagsDisplayMode(user.getUniqueId()); + Flag.Mode mode = currentMode.getOrDefault(user.getUniqueId(), Mode.BASIC); plugin.getIWM().getAddon(world).ifPresent(gm -> flags.removeIf(f -> f.getMode().isGreaterThan(mode) || f.getMode().equals(Flag.Mode.TOP_ROW))); return flags; @@ -120,13 +128,14 @@ public class SettingsTab implements Tab, ClickHandler { int i = 0; // Jump past empty tabs while (flags.isEmpty() && i++ < Flag.Mode.values().length) { - plugin.getPlayers().setFlagsDisplayMode(user.getUniqueId(), plugin.getPlayers().getFlagsDisplayMode(user.getUniqueId()).getNext()); + currentMode.put(user.getUniqueId(), currentMode.getOrDefault(user.getUniqueId(), Mode.BASIC).getNext()); flags = getFlags(); } - return flags.stream().map( + List<@Nullable PanelItem> result = flags.stream().map( (f -> f.toPanelItem(plugin, user, world, island, plugin.getIWM().getHiddenFlags(world).contains(f.getID())))) .toList(); + return result; } @Override @@ -137,8 +146,9 @@ public class SettingsTab implements Tab, ClickHandler { icons.put(4, Flags.CHANGE_SETTINGS.toPanelItem(plugin, user, world, island, false)); icons.put(5, Flags.LOCK.toPanelItem(plugin, user, world, island, false)); } + // Add the mode icon - switch (plugin.getPlayers().getFlagsDisplayMode(user.getUniqueId())) { + switch (currentMode.getOrDefault(user.getUniqueId(), Mode.BASIC)) { case ADVANCED -> icons.put(7, new PanelItemBuilder().icon(Material.GOLD_INGOT) .name(user.getTranslation(PROTECTION_PANEL + "mode.advanced.name")) .description(user.getTranslation(PROTECTION_PANEL + "mode.advanced.description"), "", @@ -161,7 +171,8 @@ public class SettingsTab implements Tab, ClickHandler { .clickHandler(this) .build()); } - // Add the reset everything to default - it's only in the player's settings panel + + // Add the reset everything to default - it's only in the player's settings panel if (island != null && user.getUniqueId().equals(island.getOwner())) { icons.put(8, new PanelItemBuilder().icon(Material.TNT) .name(user.getTranslation(PROTECTION_PANEL + "reset-to-default.name")) @@ -216,7 +227,7 @@ public class SettingsTab implements Tab, ClickHandler { @Override public boolean onClick(Panel panel, User user, ClickType clickType, int slot) { // Cycle the mode - plugin.getPlayers().setFlagsDisplayMode(user.getUniqueId(), plugin.getPlayers().getFlagsDisplayMode(user.getUniqueId()).getNext()); + currentMode.put(user.getUniqueId(), currentMode.getOrDefault(user.getUniqueId(), Mode.BASIC).getNext()); if (panel instanceof TabbedPanel tp) { tp.setActivePage(0); tp.refreshPanel(); diff --git a/src/main/java/world/bentobox/bentobox/panels/settings/WorldDefaultSettingsTab.java b/src/main/java/world/bentobox/bentobox/panels/settings/WorldDefaultSettingsTab.java index 38f2ebad8..d258eb9ab 100644 --- a/src/main/java/world/bentobox/bentobox/panels/settings/WorldDefaultSettingsTab.java +++ b/src/main/java/world/bentobox/bentobox/panels/settings/WorldDefaultSettingsTab.java @@ -7,6 +7,7 @@ import org.bukkit.Material; import org.bukkit.World; import org.eclipse.jdt.annotation.NonNull; +import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.flags.Flag.Type; import world.bentobox.bentobox.api.flags.clicklisteners.WorldToggleClick; import world.bentobox.bentobox.api.localization.TextVariables; diff --git a/src/test/java/world/bentobox/bentobox/TestBentoBox.java b/src/test/java/world/bentobox/bentobox/TestBentoBox.java index fdfe61772..8b3005317 100644 --- a/src/test/java/world/bentobox/bentobox/TestBentoBox.java +++ b/src/test/java/world/bentobox/bentobox/TestBentoBox.java @@ -36,6 +36,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -50,11 +52,12 @@ import world.bentobox.bentobox.listeners.flags.AbstractCommonSetup; import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.FlagsManager; +import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; @RunWith(PowerMockRunner.class) -@PrepareForTest({ BentoBox.class, Flags.class, Util.class, Bukkit.class}) +@PrepareForTest({ BentoBox.class, Flags.class, Util.class, Bukkit.class, IslandsManager.class }) public class TestBentoBox extends AbstractCommonSetup { private static final UUID MEMBER_UUID = UUID.randomUUID(); private static final UUID VISITOR_UUID = UUID.randomUUID(); @@ -74,6 +77,9 @@ public class TestBentoBox extends AbstractCommonSetup { public void setUp() throws Exception { super.setUp(); + // IslandsManager static + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); + when(plugin.getCommandsManager()).thenReturn(cm); SkullMeta skullMeta = mock(SkullMeta.class); @@ -85,6 +91,7 @@ public class TestBentoBox extends AbstractCommonSetup { when(player.hasPermission(anyString())).thenReturn(true); + when(location.getWorld()).thenReturn(world); when(ownerOfIsland.getLocation()).thenReturn(location); when(visitorToIsland.getLocation()).thenReturn(location); when(location.clone()).thenReturn(location); @@ -95,6 +102,7 @@ public class TestBentoBox extends AbstractCommonSetup { island.setOwner(uuid); island.setProtectionRange(100); + island.setCenter(location); HashMap members = new HashMap<>(); members.put(uuid, RanksManager.OWNER_RANK); members.put(MEMBER_UUID, RanksManager.MEMBER_RANK); diff --git a/src/test/java/world/bentobox/bentobox/api/addons/AddonClassLoaderTest.java b/src/test/java/world/bentobox/bentobox/api/addons/AddonClassLoaderTest.java index f3fb90554..fc5d42574 100644 --- a/src/test/java/world/bentobox/bentobox/api/addons/AddonClassLoaderTest.java +++ b/src/test/java/world/bentobox/bentobox/api/addons/AddonClassLoaderTest.java @@ -32,10 +32,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; +import com.github.puregero.multilib.MultiLib; + import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.exceptions.InvalidAddonDescriptionException; import world.bentobox.bentobox.managers.AddonsManager; @@ -46,7 +49,7 @@ import world.bentobox.bentobox.managers.AddonsManager; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest( { BentoBox.class, Bukkit.class }) +@PrepareForTest({ BentoBox.class, Bukkit.class, MultiLib.class }) public class AddonClassLoaderTest { private enum mandatoryTags { @@ -80,6 +83,7 @@ public class AddonClassLoaderTest { */ @Before public void setUp() throws Exception { + PowerMockito.mockStatic(MultiLib.class, Mockito.RETURNS_MOCKS); // Set up plugin plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); diff --git a/src/test/java/world/bentobox/bentobox/api/addons/AddonTest.java b/src/test/java/world/bentobox/bentobox/api/addons/AddonTest.java index 6bb9c3e84..4921373b8 100644 --- a/src/test/java/world/bentobox/bentobox/api/addons/AddonTest.java +++ b/src/test/java/world/bentobox/bentobox/api/addons/AddonTest.java @@ -44,13 +44,15 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; +import com.github.puregero.multilib.MultiLib; + import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.managers.AddonsManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.PlayersManager; @RunWith(PowerMockRunner.class) -@PrepareForTest( { BentoBox.class, Bukkit.class }) +@PrepareForTest({ BentoBox.class, Bukkit.class, MultiLib.class }) public class AddonTest { public static int BUFFER_SIZE = 10240; @@ -90,6 +92,8 @@ public class AddonTest { // Addons manager when(plugin.getAddonsManager()).thenReturn(am); + // MultiLib + PowerMockito.mockStatic(MultiLib.class, Mockito.RETURNS_MOCKS); // Mock item factory (for itemstacks) ItemFactory itemFactory = mock(ItemFactory.class); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommandTest.java index 85830a653..67f01afb7 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommandTest.java @@ -51,7 +51,7 @@ import world.bentobox.bentobox.util.Util; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ Bukkit.class, BentoBox.class, Util.class }) +@PrepareForTest({ Bukkit.class, BentoBox.class, Util.class, IslandsManager.class }) public class AdminInfoCommandTest extends RanksManagerBeforeClassTest { @Mock @@ -84,6 +84,8 @@ public class AdminInfoCommandTest extends RanksManagerBeforeClassTest { public void setUp() throws Exception { super.setUp(); + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); + // IWM when(plugin.getIWM()).thenReturn(iwm); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommandTest.java index c22fc6c60..10bdcaa12 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminSettingsCommandTest.java @@ -144,7 +144,6 @@ public class AdminSettingsCommandTest extends RanksManagerBeforeClassTest { when(plugin.getIWM()).thenReturn(iwm); // Players manager when(plugin.getPlayers()).thenReturn(pm); - when(pm.getFlagsDisplayMode(any())).thenReturn(Mode.BASIC); //Island Manager when(plugin.getIslands()).thenReturn(im); // Island - player has island @@ -267,16 +266,6 @@ public class AdminSettingsCommandTest extends RanksManagerBeforeClassTest { verify(user).sendMessage("general.errors.use-in-game"); } - /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminSettingsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testExecuteUserStringListOfStringNoArgs() { - assertTrue(asc.execute(user, "", Collections.emptyList())); - verify(pm).setFlagsDisplayMode(user.getUniqueId(), Mode.EXPERT); - // Open panel - } - /** * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminSettingsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommandTest.java index 8f4aaeb75..3dbdbe6ef 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminUnregisterCommandTest.java @@ -29,6 +29,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -54,7 +55,7 @@ import world.bentobox.bentobox.util.Util; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ Bukkit.class, BentoBox.class }) +@PrepareForTest({ Bukkit.class, BentoBox.class, IslandsManager.class }) public class AdminUnregisterCommandTest { private UUID uuid = UUID.randomUUID(); @@ -83,6 +84,7 @@ public class AdminUnregisterCommandTest { */ @Before public void setUp() throws Exception { + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); // Set up plugin BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java index 385da6c85..d703ef44a 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java @@ -39,7 +39,7 @@ import world.bentobox.bentobox.managers.RanksManagerBeforeClassTest; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ Bukkit.class, BentoBox.class }) +@PrepareForTest({ Bukkit.class, BentoBox.class, IslandsManager.class }) public class DefaultPlayerCommandTest extends RanksManagerBeforeClassTest { @Mock @@ -68,6 +68,7 @@ public class DefaultPlayerCommandTest extends RanksManagerBeforeClassTest { @Before public void setUp() throws Exception { super.setUp(); + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); // User when(user.getUniqueId()).thenReturn(UUID.randomUUID()); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommandTest.java index 964d9fbea..1a3c383c4 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandInfoCommandTest.java @@ -51,7 +51,7 @@ import world.bentobox.bentobox.util.Util; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class, BentoBox.class, Util.class}) +@PrepareForTest({ Bukkit.class, BentoBox.class, Util.class, IslandsManager.class }) public class IslandInfoCommandTest extends RanksManagerBeforeClassTest { @Mock @@ -84,6 +84,8 @@ public class IslandInfoCommandTest extends RanksManagerBeforeClassTest { public void setUp() throws Exception { super.setUp(); + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); + // IWM when(plugin.getIWM()).thenReturn(iwm); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandResetCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandResetCommandTest.java index da1505cd3..787f19dd8 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandResetCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandResetCommandTest.java @@ -62,7 +62,7 @@ import world.bentobox.bentobox.managers.island.NewIsland; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ Bukkit.class, BentoBox.class, NewIsland.class }) +@PrepareForTest({ Bukkit.class, BentoBox.class, NewIsland.class, IslandsManager.class }) public class IslandResetCommandTest { @Mock @@ -97,6 +97,7 @@ public class IslandResetCommandTest { */ @Before public void setUp() throws Exception { + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); // Set up plugin BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommandTest.java index b0f360d80..8537a1f7e 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamCommandTest.java @@ -7,10 +7,13 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.beans.IntrospectionException; +import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.UUID; @@ -29,10 +32,11 @@ import org.powermock.modules.junit4.PowerMockRunner; import com.google.common.collect.ImmutableSet; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.commands.island.team.Invite.Type; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.database.objects.TeamInvite.Type; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; @@ -74,6 +78,9 @@ public class IslandTeamCommandTest extends RanksManagerBeforeClassTest { @Mock private @Nullable Island island; + @Mock + private GameModeAddon addon; + /** */ @Before @@ -87,6 +94,7 @@ public class IslandTeamCommandTest extends RanksManagerBeforeClassTest { // Parent command when(ic.getPermissionPrefix()).thenReturn("bskyblock."); when(ic.getWorld()).thenReturn(world); + when(ic.getAddon()).thenReturn(addon); // user uuid = UUID.randomUUID(); @@ -171,11 +179,14 @@ public class IslandTeamCommandTest extends RanksManagerBeforeClassTest { /** * Test method for * {@link world.bentobox.bentobox.api.commands.island.team.IslandTeamCommand#addInvite(world.bentobox.bentobox.api.commands.island.team.Invite.Type, java.util.UUID, java.util.UUID)}. + * @throws IntrospectionException + * @throws InvocationTargetException + * @throws IllegalAccessException */ @Test - public void testAddInvite() { - tc.addInvite(Invite.Type.TEAM, uuid, invitee, island); - assertTrue(tc.isInvited(invitee)); + public void testAddInvite() throws IllegalAccessException, InvocationTargetException, IntrospectionException { + tc.addInvite(Type.TEAM, uuid, invitee, island); + verify(h, atLeast(1)).saveObject(any()); } /** @@ -193,8 +204,7 @@ public class IslandTeamCommandTest extends RanksManagerBeforeClassTest { */ @Test public void testGetInviter() { - tc.addInvite(Invite.Type.TEAM, uuid, invitee, island); - assertEquals(uuid, tc.getInviter(invitee)); + assertNull(tc.getInviter(invitee)); } /** @@ -213,12 +223,6 @@ public class IslandTeamCommandTest extends RanksManagerBeforeClassTest { @Test public void testGetInvite() { assertNull(tc.getInvite(invitee)); - tc.addInvite(Invite.Type.TEAM, uuid, invitee, island); - @Nullable - Invite invite = tc.getInvite(invitee); - assertEquals(invitee, invite.getInvitee()); - assertEquals(Type.TEAM, invite.getType()); - assertEquals(uuid, invite.getInviter()); } /** @@ -228,7 +232,7 @@ public class IslandTeamCommandTest extends RanksManagerBeforeClassTest { @Test public void testRemoveInvite() { assertNull(tc.getInvite(invitee)); - tc.addInvite(Invite.Type.TEAM, uuid, invitee, island); + tc.addInvite(Type.TEAM, uuid, invitee, island); tc.removeInvite(invitee); assertNull(tc.getInvite(invitee)); } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommandTest.java index ca056a9ac..a10376f10 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommandTest.java @@ -32,12 +32,13 @@ import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.TestWorldSettings; -import world.bentobox.bentobox.api.commands.island.team.Invite.Type; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.team.TeamEvent; import world.bentobox.bentobox.api.events.team.TeamEvent.TeamEventBuilder; 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.CommandsManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; @@ -72,7 +73,7 @@ public class IslandTeamInviteAcceptCommandTest { @Mock private PluginManager pim; @Mock - private Invite invite; + private TeamInvite invite; /** */ @@ -148,7 +149,7 @@ public class IslandTeamInviteAcceptCommandTest { when(plugin.getIWM()).thenReturn(iwm); // Invite - when(invite.getType()).thenReturn(Invite.Type.TEAM); + when(invite.getType()).thenReturn(Type.TEAM); // Team invite accept command c = new IslandTeamInviteAcceptCommand(itc); @@ -281,7 +282,7 @@ public class IslandTeamInviteAcceptCommandTest { when(itc.isInvited(any())).thenReturn(true); when(itc.getInviter(any())).thenReturn(notUUID); when(itc.getInvite(any())).thenReturn(invite); - when(invite.getType()).thenReturn(Invite.Type.COOP); + when(invite.getType()).thenReturn(Type.COOP); when(im.inTeam(any(), any())).thenReturn(false); assertTrue(c.canExecute(user, "accept", Collections.emptyList())); verify(user, never()).sendMessage("commands.island.team.invite.errors.you-already-are-in-team"); @@ -332,7 +333,7 @@ public class IslandTeamInviteAcceptCommandTest { @Test public void testExecuteUserStringListOfStringCoop() { // Coop - when(invite.getType()).thenReturn(Invite.Type.COOP); + when(invite.getType()).thenReturn(Type.COOP); assertTrue(c.execute(user, "accept", Collections.emptyList())); verify(user).sendMessage("commands.confirmation.confirm", "[seconds]", "0"); } @@ -343,7 +344,7 @@ public class IslandTeamInviteAcceptCommandTest { @Test public void testExecuteUserStringListOfStringTrust() { // Trust - when(invite.getType()).thenReturn(Invite.Type.TRUST); + when(invite.getType()).thenReturn(Type.TRUST); assertTrue(c.execute(user, "accept", Collections.emptyList())); verify(user).sendMessage("commands.confirmation.confirm", "[seconds]", "0"); } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommandTest.java index 3a50c1445..cf8c263b6 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommandTest.java @@ -40,12 +40,13 @@ import com.google.common.collect.ImmutableSet; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.TestWorldSettings; -import world.bentobox.bentobox.api.commands.island.team.Invite.Type; import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.events.IslandBaseEvent; 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.database.objects.TeamInvite; +import world.bentobox.bentobox.database.objects.TeamInvite.Type; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; @@ -319,7 +320,7 @@ public class IslandTeamInviteCommandTest extends RanksManagerBeforeClassTest { assertTrue(itl.execute(user, itl.getLabel(), List.of("target"))); verify(pim).callEvent(any(IslandBaseEvent.class)); verify(user, never()).sendMessage(eq("commands.island.team.invite.removing-invite")); - verify(ic).addInvite(Invite.Type.TEAM, uuid, notUUID, island); + verify(ic).addInvite(Type.TEAM, uuid, notUUID, island); verify(user).sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, "target", TextVariables.DISPLAY_NAME, "&Ctarget"); verify(target).sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, "tastybento", TextVariables.DISPLAY_NAME, "&Ctastbento"); verify(target).sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL, "island"); @@ -338,7 +339,7 @@ public class IslandTeamInviteCommandTest extends RanksManagerBeforeClassTest { assertTrue(itl.execute(user, itl.getLabel(), List.of("target"))); verify(pim).callEvent(any(IslandBaseEvent.class)); verify(user, never()).sendMessage("commands.island.team.invite.removing-invite"); - verify(ic).addInvite(Invite.Type.TEAM, uuid, notUUID, island); + verify(ic).addInvite(Type.TEAM, uuid, notUUID, island); verify(user).sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, "target", TextVariables.DISPLAY_NAME, "&Ctarget"); verify(target).sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, "tastybento", @@ -359,7 +360,7 @@ public class IslandTeamInviteCommandTest extends RanksManagerBeforeClassTest { when(ic.isInvited(notUUID)).thenReturn(true); // Set up invite when(ic.getInviter(notUUID)).thenReturn(uuid); - Invite invite = mock(Invite.class); + TeamInvite invite = mock(TeamInvite.class); when(invite.getType()).thenReturn(Type.TEAM); when(ic.getInvite(notUUID)).thenReturn(invite); assertTrue(itl.execute(user, itl.getLabel(), List.of("target"))); 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..7c89ff93c 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 @@ -54,7 +54,7 @@ import world.bentobox.bentobox.managers.PlayersManager; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ Bukkit.class, BentoBox.class, User.class }) +@PrepareForTest({ Bukkit.class, BentoBox.class, User.class, IslandsManager.class }) public class IslandTeamSetownerCommandTest { @Mock @@ -84,6 +84,8 @@ public class IslandTeamSetownerCommandTest { */ @Before public void setUp() throws Exception { + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); + // Set up plugin BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); @@ -276,7 +278,6 @@ 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); } /** @@ -292,7 +293,6 @@ 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); } /** diff --git a/src/test/java/world/bentobox/bentobox/api/events/island/IslandEventTest.java b/src/test/java/world/bentobox/bentobox/api/events/island/IslandEventTest.java index 0677e8281..77bf06184 100644 --- a/src/test/java/world/bentobox/bentobox/api/events/island/IslandEventTest.java +++ b/src/test/java/world/bentobox/bentobox/api/events/island/IslandEventTest.java @@ -21,6 +21,7 @@ import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.IslandBaseEvent; @@ -28,13 +29,14 @@ import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.IslandDeletion; +import world.bentobox.bentobox.managers.IslandsManager; /** * @author tastybento * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ BentoBox.class, Bukkit.class }) +@PrepareForTest({ BentoBox.class, Bukkit.class, IslandsManager.class }) public class IslandEventTest { private Island island; @@ -47,11 +49,18 @@ public class IslandEventTest { private IslandDeletion deletedIslandInfo; @Mock private PluginManager pim; + @Mock + private BentoBox plugin; /** */ @Before public void setUp() throws Exception { + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); + + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + uuid = UUID.randomUUID(); // Bukkit PowerMockito.mockStatic(Bukkit.class); 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..b2b149abc 100644 --- a/src/test/java/world/bentobox/bentobox/database/objects/IslandTest.java +++ b/src/test/java/world/bentobox/bentobox/database/objects/IslandTest.java @@ -46,6 +46,7 @@ import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.FlagsManager; import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Pair; @@ -54,7 +55,7 @@ import world.bentobox.bentobox.util.Pair; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ Bukkit.class }) +@PrepareForTest({ Bukkit.class, IslandsManager.class }) public class IslandTest { private static final int DISTANCE = 400; @@ -105,6 +106,9 @@ public class IslandTest { // Commands manager when(plugin.getCommandsManager()).thenReturn(cm); + // Islands Manager + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); + i = new Island(new Island(location, uuid, 100)); } @@ -1105,15 +1109,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..d60092f74 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java @@ -65,7 +65,7 @@ import world.bentobox.bentobox.util.Util; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ BentoBox.class, Util.class, Bukkit.class }) +@PrepareForTest({ BentoBox.class, Util.class, Bukkit.class, IslandsManager.class }) public class JoinLeaveListenerTest { private static final String[] NAMES = { "adam", "ben", "cara", "dave", "ed", "frank", "freddy", "george", "harry", @@ -111,6 +111,8 @@ public class JoinLeaveListenerTest { */ @Before public void setUp() throws Exception { + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); + // Set up plugin Whitebox.setInternalState(BentoBox.class, "instance", plugin); @@ -218,8 +220,6 @@ public class JoinLeaveListenerTest { jll = new JoinLeaveListener(plugin); } - /** - */ @After public void tearDown() { User.clearUsers(); @@ -235,8 +235,7 @@ public class JoinLeaveListenerTest { PlayerJoinEvent event = new PlayerJoinEvent(player, ""); jll.onPlayerJoin(event); // Verify - verify(pm, times(2)).addPlayer(any()); - verify(pm, times(2)).save(any()); + verify(pm, times(3)).getPlayer(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()); } /** @@ -355,8 +352,7 @@ public class JoinLeaveListenerTest { PlayerJoinEvent event = new PlayerJoinEvent(player, ""); jll.onPlayerJoin(event); // Verify - verify(pm, times(2)).addPlayer(any()); - verify(pm, times(2)).save(any()); + verify(pm, times(3)).getPlayer(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/AddonsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/AddonsManagerTest.java index afb1a3bcc..260ef75d4 100644 --- a/src/test/java/world/bentobox/bentobox/managers/AddonsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/AddonsManagerTest.java @@ -37,6 +37,8 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; +import com.github.puregero.multilib.MultiLib; + import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.api.addons.Addon; @@ -49,7 +51,7 @@ import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; import world.bentobox.bentobox.database.objects.DataObject; @RunWith(PowerMockRunner.class) -@PrepareForTest( {Bukkit.class, BentoBox.class, DefaultPermissions.class} ) +@PrepareForTest({ Bukkit.class, BentoBox.class, DefaultPermissions.class, MultiLib.class }) public class AddonsManagerTest { private BentoBox plugin; @@ -81,6 +83,8 @@ public class AddonsManagerTest { when(plugin.getSettings()).thenReturn(s); PowerMockito.mockStatic(DefaultPermissions.class); + + PowerMockito.mockStatic(MultiLib.class, Mockito.RETURNS_MOCKS); } /** diff --git a/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java index b3b24ab42..ade8eda30 100644 --- a/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java @@ -45,6 +45,8 @@ import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import com.github.puregero.multilib.MultiLib; + import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.addons.AddonDescription; @@ -62,7 +64,7 @@ import world.bentobox.bentobox.database.objects.Island; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest( {Bukkit.class, BentoBox.class, BlueprintPaster.class} ) +@PrepareForTest({ Bukkit.class, BentoBox.class, BlueprintPaster.class, MultiLib.class }) public class BlueprintsManagerTest { public static int BUFFER_SIZE = 10240; @@ -95,10 +97,12 @@ public class BlueprintsManagerTest { private int times; @Mock private Server server; - /** - */ + @Before public void setUp() throws Exception { + // Multilib + PowerMockito.mockStatic(MultiLib.class, Mockito.RETURNS_MOCKS); + // Make the addon dataFolder = new File("dataFolder"); jarFile = new File("addon.jar"); diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index 7931f9df7..dadfd87bf 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -65,6 +65,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; +import com.github.puregero.multilib.MultiLib; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; @@ -85,7 +86,7 @@ import world.bentobox.bentobox.managers.island.IslandCache; import world.bentobox.bentobox.util.Util; @RunWith(PowerMockRunner.class) -@PrepareForTest({ Bukkit.class, BentoBox.class, Util.class, Location.class }) +@PrepareForTest({ Bukkit.class, BentoBox.class, Util.class, Location.class, MultiLib.class }) public class IslandsManagerTest extends AbstractCommonSetup { @Mock @@ -157,6 +158,9 @@ public class IslandsManagerTest extends AbstractCommonSetup { plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); + // Mutilib + PowerMockito.mockStatic(MultiLib.class, Mockito.RETURNS_MOCKS); + // island world mgr when(world.getName()).thenReturn("world"); when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); @@ -242,7 +246,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { // Island when(island.getOwner()).thenReturn(uuid); when(island.getWorld()).thenReturn(world); - when(island.getMaxMembers()).thenReturn(null); // default + when(island.getMaxMembers()).thenReturn(new HashMap<>()); // default when(island.getMaxMembers(Mockito.anyInt())).thenReturn(null); // default when(island.getCenter()).thenReturn(location); when(island.getProtectionCenter()).thenReturn(location); @@ -329,6 +333,9 @@ public class IslandsManagerTest extends AbstractCommonSetup { // Util strip spaces when(Util.stripSpaceAfterColorCodes(anyString())).thenCallRealMethod(); + // World UID + when(world.getUID()).thenReturn(uuid); + // Class under test im = new IslandsManager(plugin); // Set cache @@ -970,7 +977,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() { @@ -1372,14 +1379,14 @@ public class IslandsManagerTest extends AbstractCommonSetup { Island island = mock(Island.class); when(island.getOwner()).thenReturn(uuid); when(island.getWorld()).thenReturn(world); - when(island.getMaxMembers()).thenReturn(null); + when(island.getMaxMembers()).thenReturn(new HashMap<>()); when(island.getMaxMembers(Mockito.anyInt())).thenReturn(null); when(iwm.getMaxTeamSize(eq(world))).thenReturn(4); // Offline owner when(Bukkit.getPlayer(any(UUID.class))).thenReturn(null); // Test assertEquals(4, im.getMaxMembers(island, RanksManager.MEMBER_RANK)); - verify(island).setMaxMembers(eq(RanksManager.MEMBER_RANK), eq(null)); + verify(island, never()).setMaxMembers(eq(RanksManager.MEMBER_RANK), eq(null)); // No change } /** @@ -1391,14 +1398,14 @@ public class IslandsManagerTest extends AbstractCommonSetup { Island island = mock(Island.class); when(island.getOwner()).thenReturn(uuid); when(island.getWorld()).thenReturn(world); - when(island.getMaxMembers()).thenReturn(null); + when(island.getMaxMembers()).thenReturn(new HashMap<>()); when(island.getMaxMembers(Mockito.anyInt())).thenReturn(null); when(iwm.getMaxTeamSize(eq(world))).thenReturn(4); // Online owner when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player); // Test assertEquals(4, im.getMaxMembers(island, RanksManager.MEMBER_RANK)); - verify(island).setMaxMembers(eq(RanksManager.MEMBER_RANK), eq(null)); + verify(island, never()).setMaxMembers(RanksManager.MEMBER_RANK, null); } /** @@ -1410,7 +1417,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { Island island = mock(Island.class); when(island.getOwner()).thenReturn(uuid); when(island.getWorld()).thenReturn(world); - when(island.getMaxMembers()).thenReturn(null); + when(island.getMaxMembers()).thenReturn(new HashMap<>()); when(island.getMaxMembers(Mockito.anyInt())).thenReturn(null); when(iwm.getMaxTeamSize(eq(world))).thenReturn(4); when(iwm.getMaxCoopSize(eq(world))).thenReturn(2); @@ -1419,9 +1426,9 @@ public class IslandsManagerTest extends AbstractCommonSetup { when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player); // Test assertEquals(2, im.getMaxMembers(island, RanksManager.COOP_RANK)); - verify(island).setMaxMembers(eq(RanksManager.COOP_RANK), eq(null)); + verify(island, never()).setMaxMembers(RanksManager.COOP_RANK, null); // No change assertEquals(3, im.getMaxMembers(island, RanksManager.TRUSTED_RANK)); - verify(island).setMaxMembers(eq(RanksManager.TRUSTED_RANK), eq(null)); + verify(island, never()).setMaxMembers(RanksManager.TRUSTED_RANK, null); } /** @@ -1439,7 +1446,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player); // Test assertEquals(10, im.getMaxMembers(island, RanksManager.MEMBER_RANK)); - verify(island).setMaxMembers(eq(RanksManager.MEMBER_RANK), eq(10)); + verify(island).setMaxMembers(RanksManager.MEMBER_RANK, 10); } /** @@ -1457,7 +1464,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player); // Test assertEquals(10, im.getMaxMembers(island, RanksManager.MEMBER_RANK)); - verify(island).setMaxMembers(eq(RanksManager.MEMBER_RANK), eq(10)); + verify(island).setMaxMembers(RanksManager.MEMBER_RANK, 10); } /** @@ -1469,7 +1476,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { Island island = mock(Island.class); when(island.getOwner()).thenReturn(uuid); when(island.getWorld()).thenReturn(world); - when(island.getMaxMembers()).thenReturn(null); + when(island.getMaxMembers()).thenReturn(new HashMap<>()); when(iwm.getMaxTeamSize(eq(world))).thenReturn(4); // Permission when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock."); @@ -1482,7 +1489,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player); // Test assertEquals(8, im.getMaxMembers(island, RanksManager.MEMBER_RANK)); - verify(island).setMaxMembers(eq(RanksManager.MEMBER_RANK), eq(8)); + verify(island).setMaxMembers(RanksManager.MEMBER_RANK, 8); } /** @@ -1494,7 +1501,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { Island island = mock(Island.class); // Test im.setMaxMembers(island, RanksManager.MEMBER_RANK, 40); - verify(island).setMaxMembers(eq(RanksManager.MEMBER_RANK), eq(40)); + verify(island).setMaxMembers(RanksManager.MEMBER_RANK, 40); } /** @@ -1546,7 +1553,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { // Test IslandsManager im = new IslandsManager(plugin); assertEquals(4, im.getMaxHomes(island)); - verify(island).setMaxHomes(eq(null)); + verify(island, never()).setMaxHomes(null); } /** @@ -1572,7 +1579,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { // Test IslandsManager im = new IslandsManager(plugin); assertEquals(20, im.getMaxHomes(island)); - verify(island).setMaxHomes(eq(20)); + verify(island, never()).setMaxHomes(20); } /** @@ -1598,7 +1605,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { // Test IslandsManager im = new IslandsManager(plugin); assertEquals(8, im.getMaxHomes(island)); - verify(island).setMaxHomes(eq(8)); + verify(island).setMaxHomes(8); } /** diff --git a/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java index 96790efba..8037ad671 100644 --- a/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java @@ -6,7 +6,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -53,13 +55,13 @@ import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; -import world.bentobox.bentobox.api.flags.Flag.Mode; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.AbstractDatabaseHandler; import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.DatabaseSetup; import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.database.objects.Names; import world.bentobox.bentobox.database.objects.Players; import world.bentobox.bentobox.hooks.VaultHook; import world.bentobox.bentobox.util.Util; @@ -240,6 +242,18 @@ public class PlayersManagerTest { when(tamed.getOwner()).thenReturn(p); when(world.getEntitiesByClass(Tameable.class)).thenReturn(list); + // Loading objects + Object players = new Players(); + when(h.loadObject(anyString())).thenReturn(players); + // Set up names database + List names = new ArrayList<>(); + Names name = new Names(); + name.setUniqueId("tastybento"); + name.setUuid(uuid); + names.add(name); + when(h.loadObjects()).thenReturn(names); + when(h.objectExists(anyString())).thenReturn(true); + // Class under test pm = new PlayersManager(plugin); } @@ -263,21 +277,6 @@ public class PlayersManagerTest { assertEquals(deaths + 1, pm.getDeaths(world, uuid)); } - /** - * Test method for - * {@link world.bentobox.bentobox.managers.PlayersManager#addPlayer(java.util.UUID)}. - */ - @Test - public void testAddPlayer() { - - pm.addPlayer(null); - // Add twice - assertFalse(pm.isKnown(uuid)); - pm.addPlayer(uuid); - assertTrue(pm.isKnown(uuid)); - pm.addPlayer(uuid); - } - /** * Test method for * {@link world.bentobox.bentobox.managers.PlayersManager#addReset(org.bukkit.World, java.util.UUID)}. @@ -372,15 +371,6 @@ public class PlayersManagerTest { assertEquals(0, pm.getDeaths(world, uuid)); } - /** - * Test method for - * {@link world.bentobox.bentobox.managers.PlayersManager#getFlagsDisplayMode(java.util.UUID)}. - */ - @Test - public void testGetFlagsDisplayMode() { - assertEquals(Mode.BASIC, pm.getFlagsDisplayMode(uuid)); - } - /** * Test method for * {@link world.bentobox.bentobox.managers.PlayersManager#getLocale(java.util.UUID)}. @@ -401,24 +391,13 @@ public class PlayersManagerTest { assertEquals("tastybento", name); } - /** - * Test method for - * {@link world.bentobox.bentobox.managers.PlayersManager#getPlayer(java.util.UUID)}. - */ - @Test - public void testGetPlayer() { - Players player = pm.getPlayer(uuid); - assertEquals("tastybento", player.getPlayerName()); - assertEquals(uuid.toString(), player.getUniqueId()); - } - /** * Test method for * {@link world.bentobox.bentobox.managers.PlayersManager#getPlayers()}. */ @Test public void testGetPlayers() { - assertTrue(pm.getPlayers().isEmpty()); + assertFalse(pm.getPlayers().isEmpty()); } /** @@ -442,11 +421,18 @@ public class PlayersManagerTest { /** * Test method for * {@link world.bentobox.bentobox.managers.PlayersManager#setResets(World, UUID, int)}. + * @throws IntrospectionException + * @throws NoSuchMethodException + * @throws ClassNotFoundException + * @throws InvocationTargetException + * @throws IllegalAccessException + * @throws InstantiationException */ @Test - public void testGetSetResetsLeft() { + public void testGetSetResetsLeft() throws InstantiationException, IllegalAccessException, InvocationTargetException, + ClassNotFoundException, NoSuchMethodException, IntrospectionException { // Add a player - pm.addPlayer(uuid); + pm.getPlayer(uuid); assertEquals(0, pm.getResets(world, uuid)); pm.setResets(world, uuid, 20); assertEquals(20, pm.getResets(world, uuid)); @@ -455,12 +441,19 @@ public class PlayersManagerTest { /** * Test method for * {@link world.bentobox.bentobox.managers.PlayersManager#getUser(java.lang.String)}. + * @throws IntrospectionException + * @throws NoSuchMethodException + * @throws ClassNotFoundException + * @throws InvocationTargetException + * @throws IllegalAccessException + * @throws InstantiationException */ @Test - public void testGetUserString() { + public void testGetUserString() throws InstantiationException, IllegalAccessException, InvocationTargetException, + ClassNotFoundException, NoSuchMethodException, IntrospectionException { User user = pm.getUser("random"); assertNull(user); - pm.addPlayer(uuid); + pm.getPlayer(uuid); user = pm.getUser("tastybento"); assertEquals("tastybento", user.getName()); } @@ -481,7 +474,7 @@ public class PlayersManagerTest { */ @Test public void testGetUUID() { - pm.addPlayer(uuid); + pm.getPlayer(uuid); assertEquals(uuid, pm.getUUID("tastybento")); assertNull(pm.getUUID("unknown")); } @@ -494,7 +487,7 @@ public class PlayersManagerTest { public void testGetUUIDOfflinePlayer() { pm.setHandler(db); // Add a player to the cache - pm.addPlayer(uuid); + pm.getPlayer(uuid); UUID uuidResult = pm.getUUID("tastybento"); assertEquals(uuid, uuidResult); } @@ -507,7 +500,7 @@ public class PlayersManagerTest { public void testGetUUIDUnknownPlayer() { pm.setHandler(db); // Add a player to the cache - pm.addPlayer(uuid); + pm.getPlayer(uuid); // Unknown player should return null assertNull(pm.getUUID("tastybento123")); } @@ -537,8 +530,8 @@ public class PlayersManagerTest { @Test public void testIsKnown() { - pm.addPlayer(uuid); - pm.addPlayer(notUUID); + pm.getPlayer(uuid); + pm.getPlayer(notUUID); assertFalse(pm.isKnown(null)); assertTrue(pm.isKnown(uuid)); @@ -547,21 +540,11 @@ public class PlayersManagerTest { /** * Test method for - * {@link world.bentobox.bentobox.managers.PlayersManager#isSaveTaskRunning()}. + * {@link world.bentobox.bentobox.managers.PlayersManager#setHandler(Database)} */ @Test - public void testIsSaveTaskRunning() { - assertFalse(pm.isSaveTaskRunning()); - } - - /** - * Test method for - * {@link world.bentobox.bentobox.managers.PlayersManager#load()}. - */ - @Test - public void testLoad() { + public void testSetHandler() { pm.setHandler(db); - pm.load(); } /** @@ -596,43 +579,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)}. @@ -641,7 +587,7 @@ public class PlayersManagerTest { public void testSetandGetPlayerName() { pm.setHandler(db); // Add a player - pm.addPlayer(uuid); + pm.getPlayer(uuid); assertEquals("tastybento", pm.getName(user.getUniqueId())); pm.setPlayerName(user); assertEquals(user.getName(), pm.getName(user.getUniqueId())); @@ -658,16 +604,6 @@ public class PlayersManagerTest { } - /** - * Test method for - * {@link world.bentobox.bentobox.managers.PlayersManager#setFlagsDisplayMode(java.util.UUID, world.bentobox.bentobox.api.flags.Flag.Mode)}. - */ - @Test - public void testSetFlagsDisplayMode() { - pm.setFlagsDisplayMode(uuid, Mode.ADVANCED); - assertEquals(Mode.ADVANCED, pm.getFlagsDisplayMode(uuid)); - } - /** * Test method for * {@link world.bentobox.bentobox.managers.PlayersManager#setInTeleport(java.util.UUID)}. @@ -692,15 +628,15 @@ public class PlayersManagerTest { /** * Test method for * {@link world.bentobox.bentobox.managers.PlayersManager#setPlayerName(world.bentobox.bentobox.api.user.User)}. + * @throws IntrospectionException + * @throws InvocationTargetException + * @throws IllegalAccessException */ @Test - public void testSetPlayerName() { + public void testSetPlayerName() throws IllegalAccessException, InvocationTargetException, IntrospectionException { pm.setPlayerName(user); - assertEquals("tastybento", pm.getName(uuid)); - when(user.getName()).thenReturn("newName"); - assertEquals("tastybento", pm.getName(uuid)); - pm.setPlayerName(user); - assertEquals("newName", pm.getName(uuid)); + // Player and names database saves + verify(h, atLeast(2)).saveObject(any()); } /** diff --git a/src/test/java/world/bentobox/bentobox/managers/RanksManagerBeforeClassTest.java b/src/test/java/world/bentobox/bentobox/managers/RanksManagerBeforeClassTest.java index ee4d3fb92..82ee1f45c 100644 --- a/src/test/java/world/bentobox/bentobox/managers/RanksManagerBeforeClassTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/RanksManagerBeforeClassTest.java @@ -1,17 +1,23 @@ package world.bentobox.bentobox.managers; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.beans.IntrospectionException; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Comparator; import java.util.Map; +import java.util.concurrent.CompletableFuture; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; @@ -22,6 +28,7 @@ import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.AbstractDatabaseHandler; import world.bentobox.bentobox.database.DatabaseSetup; /** @@ -63,6 +70,22 @@ public abstract class RanksManagerBeforeClassTest { @Mock public RanksManager rm; + protected static AbstractDatabaseHandler h; + + @SuppressWarnings("unchecked") + @BeforeClass + public static void beforeClass() throws IllegalAccessException, InvocationTargetException, IntrospectionException { + // This has to be done beforeClass otherwise the tests will interfere with each + // other + h = mock(AbstractDatabaseHandler.class); + // Database + PowerMockito.mockStatic(DatabaseSetup.class); + DatabaseSetup dbSetup = mock(DatabaseSetup.class); + when(DatabaseSetup.getDatabase()).thenReturn(dbSetup); + when(dbSetup.getHandler(any())).thenReturn(h); + when(h.saveObject(any())).thenReturn(CompletableFuture.completedFuture(true)); + } + @Before public void setUp() throws Exception { // Set up plugin 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..2f567ca96 100644 --- a/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java @@ -56,7 +56,7 @@ import world.bentobox.bentobox.util.Util; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ Util.class, IslandEvent.class, Bukkit.class }) +@PrepareForTest({ Util.class, IslandEvent.class, Bukkit.class, IslandsManager.class }) public class NewIslandTest { private static final String NAME = "name"; @@ -105,6 +105,7 @@ public class NewIslandTest { */ @Before public void setUp() throws Exception { + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); Whitebox.setInternalState(BentoBox.class, "instance", plugin); // Islands manager when(plugin.getIslands()).thenReturn(im); @@ -205,7 +206,8 @@ public class NewIslandTest { NewIsland.builder().addon(addon).name(NAME).player(user).noPaste().reason(Reason.CREATE).oldIsland(oldIsland) .build(); // Verifications - verify(im).save(eq(island)); + PowerMockito.verifyStatic(IslandsManager.class); + IslandsManager.updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -224,7 +226,8 @@ 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)); + PowerMockito.verifyStatic(IslandsManager.class); + IslandsManager.updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -243,7 +246,8 @@ 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)); + PowerMockito.verifyStatic(IslandsManager.class); + IslandsManager.updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -262,7 +266,8 @@ 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)); + PowerMockito.verifyStatic(IslandsManager.class); + IslandsManager.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 +286,8 @@ public class NewIslandTest { NewIsland.builder().addon(addon).name(NAME).player(user).reason(Reason.CREATE).build(); PowerMockito.mockStatic(Bukkit.class); // Verifications - verify(im).save(island); + PowerMockito.verifyStatic(IslandsManager.class); + IslandsManager.updateIsland(eq(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 +305,8 @@ 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)); + PowerMockito.verifyStatic(IslandsManager.class); + IslandsManager.updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -320,7 +327,8 @@ 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)); + PowerMockito.verifyStatic(IslandsManager.class); + IslandsManager.updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); @@ -342,7 +350,8 @@ 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)); + PowerMockito.verifyStatic(IslandsManager.class); + IslandsManager.updateIsland(eq(island)); verify(island).setFlagsDefaults(); verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); verify(builder, times(2)).build(); diff --git a/src/test/java/world/bentobox/bentobox/util/teleport/ClosestSafeSpotTeleportTest.java b/src/test/java/world/bentobox/bentobox/util/teleport/ClosestSafeSpotTeleportTest.java index 9724d82fe..d8cb27cbd 100644 --- a/src/test/java/world/bentobox/bentobox/util/teleport/ClosestSafeSpotTeleportTest.java +++ b/src/test/java/world/bentobox/bentobox/util/teleport/ClosestSafeSpotTeleportTest.java @@ -58,7 +58,7 @@ import world.bentobox.bentobox.util.teleport.ClosestSafeSpotTeleport.PositionDat * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Util.class, Bukkit.class}) +@PrepareForTest({ Util.class, Bukkit.class, IslandsManager.class }) public class ClosestSafeSpotTeleportTest { // Class under test @@ -102,6 +102,9 @@ public class ClosestSafeSpotTeleportTest { */ @Before public void setUp() throws Exception { + // IslandsManager static + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); + // Setup instance Whitebox.setInternalState(BentoBox.class, "instance", plugin); // IWM diff --git a/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java b/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java index d69972d8e..15b47658d 100644 --- a/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java +++ b/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java @@ -47,7 +47,7 @@ import world.bentobox.bentobox.util.Util; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Util.class, Bukkit.class}) +@PrepareForTest({ Util.class, Bukkit.class, IslandsManager.class }) public class SafeSpotTeleportTest { // Class under test @@ -92,6 +92,7 @@ public class SafeSpotTeleportTest { */ @Before public void setUp() throws Exception { + PowerMockito.mockStatic(IslandsManager.class, Mockito.RETURNS_MOCKS); // Setup instance Whitebox.setInternalState(BentoBox.class, "instance", plugin); // IWM