diff --git a/pom.xml b/pom.xml index 981054aa8..329505f39 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ -LOCAL - 1.20.0 + 1.20.1 bentobox-world https://sonarcloud.io 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 ed543bce2..bc5ceb271 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 @@ -20,6 +20,7 @@ import world.bentobox.bentobox.util.Util; */ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { + private static final String INVALID_INVITE = "commands.island.team.invite.errors.invalid-invite"; private final IslandTeamCommand itc; private UUID playerUUID; private UUID prospectiveOwnerUUID; @@ -47,7 +48,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { // Get the island owner prospectiveOwnerUUID = itc.getInviter(playerUUID); if (prospectiveOwnerUUID == null) { - user.sendMessage("commands.island.team.invite.errors.invalid-invite"); + user.sendMessage(INVALID_INVITE); return false; } Invite invite = itc.getInvite(playerUUID); @@ -56,7 +57,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { Island island = getIslands().getIsland(getWorld(), prospectiveOwnerUUID); String inviteUsage = getParent().getSubCommand("invite").map(CompositeCommand::getUsage).orElse(""); if (island == null || island.getRank(prospectiveOwnerUUID) < island.getRankCommand(inviteUsage)) { - user.sendMessage("commands.island.team.invite.errors.invalid-invite"); + user.sendMessage(INVALID_INVITE); itc.removeInvite(playerUUID); return false; } @@ -149,7 +150,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { // Get the team's island Island teamIsland = getIslands().getIsland(getWorld(), prospectiveOwnerUUID); if (teamIsland == null) { - user.sendMessage("commands.island.team.invite.errors.invalid-invite"); + user.sendMessage(INVALID_INVITE); return; } if (teamIsland.getMemberSet(RanksManager.MEMBER_RANK, true).size() > getIslands().getMaxMembers(teamIsland, RanksManager.MEMBER_RANK)) { diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java index 2aab070fa..44ca46783 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java @@ -42,10 +42,6 @@ public class IslandTeamKickCommand extends ConfirmableCommand { user.sendMessage("general.errors.no-team"); return false; } - if (!user.getUniqueId().equals(getOwner(getWorld(), user))) { - user.sendMessage("general.errors.not-owner"); - return false; - } // Check rank to use command Island island = getIslands().getIsland(getWorld(), user); int rank = Objects.requireNonNull(island).getRank(user); @@ -72,6 +68,14 @@ public class IslandTeamKickCommand extends ConfirmableCommand { user.sendMessage("general.errors.not-in-team"); return false; } + + int targetRank = Objects.requireNonNull(island).getRank(targetUUID); + if (rank <= targetRank) { + user.sendMessage("commands.island.team.kick.cannot-kick-rank", + TextVariables.NAME, getPlayers().getName(targetUUID)); + return false; + } + if (!getSettings().isKickConfirmation()) { kick(user, targetUUID); return true; @@ -93,7 +97,9 @@ public class IslandTeamKickCommand extends ConfirmableCommand { if (event.isCancelled()) { return; } - target.sendMessage("commands.island.team.kick.owner-kicked", TextVariables.GAMEMODE, getAddon().getDescription().getName()); + target.sendMessage("commands.island.team.kick.player-kicked", + TextVariables.GAMEMODE, getAddon().getDescription().getName(), + TextVariables.NAME, user.getName()); getIslands().removePlayer(getWorld(), targetUUID); // Clean the target player diff --git a/src/main/java/world/bentobox/bentobox/api/panels/reader/TemplateReader.java b/src/main/java/world/bentobox/bentobox/api/panels/reader/TemplateReader.java index d98d501ad..4407ae8d2 100644 --- a/src/main/java/world/bentobox/bentobox/api/panels/reader/TemplateReader.java +++ b/src/main/java/world/bentobox/bentobox/api/panels/reader/TemplateReader.java @@ -41,6 +41,12 @@ public class TemplateReader private static final String BORDER = "border"; private static final String FORCE_SHOWN = "force-shown"; private static final String FALLBACK = "fallback"; + private static final String YML = ".yml"; + private static final String ACTIONS = "actions"; + private static final String TOOLTIP = "tooltip"; + private static final String CLICK_TYPE = "click-type"; + private static final String CONTENT = "content"; + private static final String TYPE = "type"; /** @@ -73,7 +79,7 @@ public class TemplateReader return null; } - File file = new File(panelLocation, templateName.endsWith(".yml") ? templateName : templateName + ".yml"); + File file = new File(panelLocation, templateName.endsWith(YML) ? templateName : templateName + YML); if (!file.exists()) { @@ -82,7 +88,7 @@ public class TemplateReader } final String panelKey = file.getAbsolutePath() + ":" + panelName; - + // Check if panel is already crafted. if (TemplateReader.loadedPanels.containsKey(panelKey)) { @@ -125,7 +131,7 @@ public class TemplateReader String title = configurationSection.getString(TITLE); Panel.Type type = - Enums.getIfPresent(Panel.Type.class, configurationSection.getString("type", "INVENTORY")). + Enums.getIfPresent(Panel.Type.class, configurationSection.getString(TYPE, "INVENTORY")). or(Panel.Type.INVENTORY); PanelTemplateRecord.TemplateItem borderItem = null; @@ -194,7 +200,7 @@ public class TemplateReader PanelTemplateRecord template = new PanelTemplateRecord(type, title, borderItem, backgroundItem, forcedRows); // Read content - ConfigurationSection content = configurationSection.getConfigurationSection("content"); + ConfigurationSection content = configurationSection.getConfigurationSection(CONTENT); if (content == null) { @@ -219,7 +225,7 @@ public class TemplateReader // If it contains a section, then build a new button template from it. template.addButtonTemplate(rowIndex, columnIndex, - readPanelItemTemplate(line.getConfigurationSection(String.valueOf(columnIndex + 1)))); + readPanelItemTemplate(line.getConfigurationSection(String.valueOf(columnIndex + 1)), null, panelItemDataMap)); } else if (line.isString(String.valueOf(columnIndex + 1))) { @@ -337,9 +343,9 @@ public class TemplateReader } // Read Click data - if (section.isConfigurationSection("actions")) + if (section.isConfigurationSection(ACTIONS)) { - ConfigurationSection actionSection = section.getConfigurationSection("actions"); + ConfigurationSection actionSection = section.getConfigurationSection(ACTIONS); if (actionSection != null) { @@ -354,9 +360,9 @@ public class TemplateReader { ItemTemplateRecord.ActionRecords actionData = new ItemTemplateRecord.ActionRecords(clickType, - actionDataSection.getString("type"), - actionDataSection.getString("content"), - actionDataSection.getString("tooltip")); + actionDataSection.getString(TYPE), + actionDataSection.getString(CONTENT), + actionDataSection.getString(TOOLTIP)); itemRecord.addAction(actionData); } } @@ -364,42 +370,42 @@ public class TemplateReader { ConfigurationSection actionDataSection = actionSection.getConfigurationSection(actionKey); - if (actionDataSection != null && actionDataSection.contains("click-type")) + if (actionDataSection != null && actionDataSection.contains(CLICK_TYPE)) { clickType = Enums.getIfPresent(ClickType.class, - actionDataSection.getString("click-type", "UNKNOWN").toUpperCase()). - or(ClickType.UNKNOWN); - + actionDataSection.getString(CLICK_TYPE, "UNKNOWN").toUpperCase()). + or(ClickType.UNKNOWN); + ItemTemplateRecord.ActionRecords actionData = - new ItemTemplateRecord.ActionRecords(clickType, - actionKey, - actionDataSection.getString("content"), - actionDataSection.getString("tooltip")); + new ItemTemplateRecord.ActionRecords(clickType, + actionKey, + actionDataSection.getString(CONTENT), + actionDataSection.getString(TOOLTIP)); itemRecord.addAction(actionData); } } }); } } - else if (section.isList("actions")) + else if (section.isList(ACTIONS)) { // Read Click data as list which allows to have duplicate click types. - List> actionList = section.getMapList("actions"); + List> actionList = section.getMapList(ACTIONS); if (!actionList.isEmpty()) { actionList.forEach(valueMap -> { ClickType clickType = Enums.getIfPresent(ClickType.class, - String.valueOf(valueMap.get("click-type")).toUpperCase()).orNull(); + String.valueOf(valueMap.get(CLICK_TYPE)).toUpperCase()).orNull(); if (clickType != null) { ItemTemplateRecord.ActionRecords actionData = - new ItemTemplateRecord.ActionRecords(clickType, - valueMap.containsKey("type") ? String.valueOf(valueMap.get("type")) : null, - valueMap.containsKey("content") ? String.valueOf(valueMap.get("content")) : null, - valueMap.containsKey("tooltip") ? String.valueOf(valueMap.get("tooltip")) : null); + new ItemTemplateRecord.ActionRecords(clickType, + valueMap.containsKey(TYPE) ? String.valueOf(valueMap.get(TYPE)) : null, + valueMap.containsKey(CONTENT) ? String.valueOf(valueMap.get(CONTENT)) : null, + valueMap.containsKey(TOOLTIP) ? String.valueOf(valueMap.get(TOOLTIP)) : null); itemRecord.addAction(actionData); } }); diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index 7d0c01b82..a3daa230c 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -257,8 +257,8 @@ public class BlueprintClipboard { } // Banners - if (blockState instanceof Banner) { - b.setBannerPatterns(((Banner) blockState).getPatterns()); + if (blockState instanceof Banner banner) { + b.setBannerPatterns(banner.getPatterns()); } return b; @@ -282,8 +282,8 @@ public class BlueprintClipboard { BlueprintEntity bpe = new BlueprintEntity(); bpe.setType(entity.getType()); bpe.setCustomName(entity.getCustomName()); - if (entity instanceof Villager) { - setVillager(entity, bpe); + if (entity instanceof Villager villager) { + setVillager(villager, bpe); } if (entity instanceof Colorable c) { if (c.getColor() != null) { @@ -321,11 +321,10 @@ public class BlueprintClipboard { /** * Set the villager stats - * @param entity - villager + * @param v - villager * @param bpe - Blueprint Entity */ - private void setVillager(LivingEntity entity, BlueprintEntity bpe) { - Villager v = (Villager)entity; + private void setVillager(Villager v, BlueprintEntity bpe) { bpe.setExperience(v.getVillagerExperience()); bpe.setLevel(v.getVillagerLevel()); bpe.setProfession(v.getProfession()); @@ -371,11 +370,17 @@ public class BlueprintClipboard { public void setPos1(@Nullable Location pos1) { origin = null; if (pos1 != null) { - if (pos1.getBlockY() < 0) { - pos1.setY(0); + final int minHeight = pos1.getWorld() == null ? 0 : pos1.getWorld().getMinHeight(); + final int maxHeight = pos1.getWorld() == null ? 255 : pos1.getWorld().getMaxHeight(); + + if (pos1.getBlockY() < minHeight) + { + pos1.setY(minHeight); } - if (pos1.getBlockY() > 255) { - pos1.setY(255); + + if (pos1.getBlockY() > maxHeight) + { + pos1.setY(maxHeight); } } this.pos1 = pos1; @@ -387,11 +392,17 @@ public class BlueprintClipboard { public void setPos2(@Nullable Location pos2) { origin = null; if (pos2 != null) { - if (pos2.getBlockY() < 0) { - pos2.setY(0); + final int minHeight = pos2.getWorld() == null ? 0 : pos2.getWorld().getMinHeight(); + final int maxHeight = pos2.getWorld() == null ? 255 : pos2.getWorld().getMaxHeight(); + + if (pos2.getBlockY() < minHeight) + { + pos2.setY(minHeight); } - if (pos2.getBlockY() > 255) { - pos2.setY(255); + + if (pos2.getBlockY() > maxHeight) + { + pos2.setY(maxHeight); } } this.pos2 = pos2; diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java index 6528f58bd..a3be6906c 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java @@ -134,99 +134,110 @@ public class BlueprintPaster { this.location = island.getProtectionCenter().toVector().subtract(off).toLocation(world); } + private record Bits(Map blocks, + Map attached, + Map> entities, + Iterator> it, + Iterator> it2, + Iterator>> it3, + int pasteSpeed) {} /** * The main pasting method */ public CompletableFuture paste() { CompletableFuture result = new CompletableFuture<>(); // Iterators for the various maps to paste - Map blocks = blueprint.getBlocks() == null ? Collections.emptyMap() : blueprint.getBlocks(); - Map attached = blueprint.getAttached() == null ? Collections.emptyMap() : blueprint.getAttached(); - Map> entities = blueprint.getEntities() == null ? Collections.emptyMap() : blueprint.getEntities(); - Iterator> it = blocks.entrySet().iterator(); - Iterator> it2 = attached.entrySet().iterator(); - Iterator>> it3 = entities.entrySet().iterator(); + final Map blocks = blueprint.getBlocks() == null ? Collections.emptyMap() : blueprint.getBlocks(); + final Map attached = blueprint.getAttached() == null ? Collections.emptyMap() : blueprint.getAttached(); + final Map> entities = blueprint.getEntities() == null ? Collections.emptyMap() : blueprint.getEntities(); // Initial state & speed pasteState = PasteState.CHUNK_LOAD; - final int pasteSpeed = plugin.getSettings().getPasteSpeed(); // If this is an island OVERWORLD paste, get the island owner. final Optional owner = Optional.ofNullable(island) .filter(i -> location.getWorld().getEnvironment().equals(World.Environment.NORMAL)) .map(i -> User.getInstance(i.getOwner())); // Tell the owner we're pasting blocks and how much time it might take - owner.ifPresent(user -> { - // Estimated time: - double total = (double) blocks.size() + attached.size() + entities.size(); - BigDecimal time = BigDecimal.valueOf(total / (pasteSpeed * 20.0D) + (chunkLoadTime / 1000.0D)).setScale(1, RoundingMode.UP); - user.sendMessage("commands.island.create.pasting.estimated-time", TextVariables.NUMBER, String.valueOf(time.doubleValue())); - // We're pasting blocks! - user.sendMessage("commands.island.create.pasting.blocks", TextVariables.NUMBER, String.valueOf(blocks.size() + attached.size())); - }); - - pastingTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { - long timer = System.currentTimeMillis(); - int count = 0; - if (pasteState.equals(PasteState.CHUNK_LOAD)) { - pasteState = PasteState.CHUNK_LOADING; - // Load chunk - Util.getChunkAtAsync(location).thenRun(() -> { - pasteState = PasteState.BLOCKS; - long duration = System.currentTimeMillis() - timer; - if (duration > chunkLoadTime) { - chunkLoadTime = duration; - } - }); - } - while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && it.hasNext()) { - pasteBlock(location, it.next()); - count++; - } - while (pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && it2.hasNext()) { - pasteBlock(location, it2.next()); - count++; - } - while (pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && it3.hasNext()) { - pasteEntity(location, it3.next()); - count++; - } - // STATE SHIFT - if (pasteState.equals(PasteState.BLOCKS) && !it.hasNext()) { - // Blocks done - // Next paste attachments - pasteState = PasteState.ATTACHMENTS; - } - else if (pasteState.equals(PasteState.ATTACHMENTS) && !it2.hasNext()) { - // Attachments done. Next paste entities - pasteState = PasteState.ENTITIES; - if (entities.size() != 0) { - owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(entities.size()))); - } - } - else if (pasteState.equals(PasteState.ENTITIES) && !it3.hasNext()) { - pasteState = PasteState.DONE; - owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done")); - } - else if (pasteState.equals(PasteState.DONE)) { - // All done. Cancel task - // Set pos1 and 2 if this was a clipboard paste - if (island == null && clipboard != null) { - clipboard.setPos1(pos1); - clipboard.setPos2(pos2); - } - result.complete(true); - pasteState = PasteState.CANCEL; - } else if (pasteState.equals(PasteState.CANCEL)) { - // This state makes sure the follow-on task only ever runs once - pastingTask.cancel(); - result.complete(true); - } - }, 0L, 1L); + owner.ifPresent(user -> tellOwner(user, blocks.size(), attached.size(), entities.size(), plugin.getSettings().getPasteSpeed())); + Bits bits = new Bits(blocks, attached, entities, + blocks.entrySet().iterator(), attached.entrySet().iterator(), entities.entrySet().iterator(), + plugin.getSettings().getPasteSpeed()); + pastingTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> pasterTask(result, owner, bits), 0L, 1L); return result; } + private void pasterTask(CompletableFuture result, Optional owner, Bits bits) { + final int pasteSpeed = plugin.getSettings().getPasteSpeed(); + + long timer = System.currentTimeMillis(); + int count = 0; + if (pasteState.equals(PasteState.CHUNK_LOAD)) { + pasteState = PasteState.CHUNK_LOADING; + // Load chunk + Util.getChunkAtAsync(location).thenRun(() -> { + pasteState = PasteState.BLOCKS; + long duration = System.currentTimeMillis() - timer; + if (duration > chunkLoadTime) { + chunkLoadTime = duration; + } + }); + } + while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && bits.it.hasNext()) { + pasteBlock(location, bits.it.next()); + count++; + } + while (pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && bits.it2.hasNext()) { + pasteBlock(location, bits.it2.next()); + count++; + } + while (pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && bits.it3.hasNext()) { + pasteEntity(location, bits.it3.next()); + count++; + } + // STATE SHIFT + if (pasteState.equals(PasteState.BLOCKS) && !bits.it.hasNext()) { + // Blocks done + // Next paste attachments + pasteState = PasteState.ATTACHMENTS; + } + else if (pasteState.equals(PasteState.ATTACHMENTS) && !bits.it2.hasNext()) { + // Attachments done. Next paste entities + pasteState = PasteState.ENTITIES; + if (bits.entities.size() != 0) { + owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(bits.entities.size()))); + } + } + else if (pasteState.equals(PasteState.ENTITIES) && !bits.it3.hasNext()) { + pasteState = PasteState.DONE; + owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done")); + } + else if (pasteState.equals(PasteState.DONE)) { + // All done. Cancel task + // Set pos1 and 2 if this was a clipboard paste + if (island == null && clipboard != null) { + clipboard.setPos1(pos1); + clipboard.setPos2(pos2); + } + pasteState = PasteState.CANCEL; + result.complete(true); + } else if (pasteState.equals(PasteState.CANCEL)) { + // This state makes sure the follow-on task only ever runs once + pastingTask.cancel(); + result.complete(true); + } + } + + private void tellOwner(User user, int blocksSize, int attachedSize, int entitiesSize, int pasteSpeed) { + // Estimated time: + double total = (double) blocksSize + attachedSize + entitiesSize; + BigDecimal time = BigDecimal.valueOf(total / (pasteSpeed * 20.0D) + (chunkLoadTime / 1000.0D)).setScale(1, RoundingMode.UP); + user.sendMessage("commands.island.create.pasting.estimated-time", TextVariables.NUMBER, String.valueOf(time.doubleValue())); + // We're pasting blocks! + user.sendMessage("commands.island.create.pasting.blocks", TextVariables.NUMBER, String.valueOf(blocksSize + attachedSize)); + } + private void pasteBlock(Location location, Entry entry) { World world = location.getWorld(); Location pasteTo = location.clone().add(entry.getKey()); @@ -294,8 +305,8 @@ public class BlueprintPaster { writeSign(block, bpBlock.getSignLines(), bpBlock.isGlowingText()); } // Chests, in general - if (bs instanceof InventoryHolder) { - Inventory ih = ((InventoryHolder)bs).getInventory(); + if (bs instanceof InventoryHolder holder) { + Inventory ih = holder.getInventory(); // Double chests are pasted as two blocks so inventory is filled twice. // This code stops over-filling for the first block. bpBlock.getInventory().forEach(ih::setItem); diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java index e173f6b86..6a5927ddb 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java @@ -56,23 +56,23 @@ public class BlueprintEntity { * @since 1.8.0 */ public void configureEntity(Entity e) { - if (e instanceof Villager) { - setVillager(e); + if (e instanceof Villager villager) { + setVillager(villager); } - if (e instanceof Colorable) { - ((Colorable) e).setColor(color); + if (e instanceof Colorable c) { + c.setColor(color); } - if (tamed != null && e instanceof Tameable) { - ((Tameable)e).setTamed(tamed); + if (tamed != null && e instanceof Tameable tameable) { + tameable.setTamed(tamed); } - if (chest != null && e instanceof ChestedHorse) { - ((ChestedHorse)e).setCarryingChest(chest); + if (chest != null && e instanceof ChestedHorse chestedHorse) { + chestedHorse.setCarryingChest(chest); } - if (adult != null && e instanceof Ageable) { + if (adult != null && e instanceof Ageable ageable) { if (adult) { - ((Ageable)e).setAdult(); + ageable.setAdult(); } else { - ((Ageable)e).setBaby(); + ageable.setBaby(); } } if (e instanceof AbstractHorse horse) { @@ -81,18 +81,17 @@ public class BlueprintEntity { inventory.forEach(horse.getInventory()::setItem); } } - if (style != null && e instanceof Horse) { - ((Horse)e).setStyle(style); + if (style != null && e instanceof Horse horse) { + horse.setStyle(style); } } /** - * @param e - villager + * @param v - villager * @since 1.16.0 */ - private void setVillager(Entity e) { - Villager v = (Villager)e; + private void setVillager(Villager v) { v.setProfession(profession == null ? Profession.NONE : profession); v.setVillagerExperience(experience == null ? 0 : experience); v.setVillagerLevel(level == null ? 0 : level); 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 76e97a97e..4b463fe33 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -698,7 +698,7 @@ public class Island implements DataObject, MetaDataAble { * @since 1.5.2 */ public BoundingBox getBoundingBox() { - return new BoundingBox(getMinX(), 0.0D, getMinZ(), getMaxX(), world.getMaxHeight(), getMaxZ()); + return new BoundingBox(getMinX(), world.getMinHeight(), getMinZ(), getMaxX(), world.getMaxHeight(), getMaxZ()); } /** diff --git a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java index 05b42d537..008ff23a9 100644 --- a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java @@ -289,7 +289,7 @@ public class YamlDatabaseHandler extends AbstractDatabaseHandler { // Convert any serialized dots back to dots // In YAML dots . cause a lot of problems, so I serialize them as :dot: // There may be a better way to do this. - key = key.replaceAll(":dot:", "."); + key = key.replace(":dot:", "."); Object mapKey = deserialize(key,Class.forName(keyType.getTypeName())); if (mapKey == null) { continue; @@ -452,7 +452,7 @@ public class YamlDatabaseHandler extends AbstractDatabaseHandler { for (Entry object : value.entrySet()) { // Serialize all key and values String key = (String)serialize(object.getKey()); - key = key.replaceAll("\\.", ":dot:"); + key = key.replace("\\.", ":dot:"); result.put(key, serialize(object.getValue())); } // Save the list in the config file diff --git a/src/main/java/world/bentobox/bentobox/hooks/VaultHook.java b/src/main/java/world/bentobox/bentobox/hooks/VaultHook.java index b4e6002c0..acece9e21 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/VaultHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/VaultHook.java @@ -17,6 +17,7 @@ import world.bentobox.bentobox.api.user.User; public class VaultHook extends Hook { private static final String AMOUNT_MUST_BE_POSITIVE = "Amount must be positive."; + private static final String PLAYER_OR_OFFLINEPLAYER_REQUIRED = "User must be a Player or an OfflinePlayer"; private Economy economy; public VaultHook() { @@ -109,7 +110,7 @@ public class VaultHook extends Hook { */ public EconomyResponse withdraw(User user, double amount, World world) { if (!user.isOfflinePlayer()) { - throw new IllegalArgumentException("User must be a Player or an OfflinePlayer"); + throw new IllegalArgumentException(PLAYER_OR_OFFLINEPLAYER_REQUIRED); } if (amount < 0.0D) { throw new IllegalArgumentException(AMOUNT_MUST_BE_POSITIVE); @@ -149,7 +150,7 @@ public class VaultHook extends Hook { */ public EconomyResponse deposit(User user, double amount, World world) { if (!user.isOfflinePlayer()) { - throw new IllegalArgumentException("User must be a Player or an OfflinePlayer"); + throw new IllegalArgumentException(PLAYER_OR_OFFLINEPLAYER_REQUIRED); } if (amount < 0.0D) { throw new IllegalArgumentException(AMOUNT_MUST_BE_POSITIVE); @@ -198,7 +199,7 @@ public class VaultHook extends Hook { } if (!user.isOfflinePlayer()) { - throw new IllegalArgumentException("User must be a Player or an OfflinePlayer"); + throw new IllegalArgumentException(PLAYER_OR_OFFLINEPLAYER_REQUIRED); } if (world == null) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java index 95cf5ee01..c0c46b964 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java @@ -30,6 +30,7 @@ import world.bentobox.bentobox.api.events.flags.FlagSettingChangeEvent; import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.api.flags.FlagListener; import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.RanksManager; @@ -239,10 +240,20 @@ public class PVPListener extends FlagListener { if (e.getTo() == null) { return; } + + // Get previous island to skip reporting if island is not changed. + Island previousIsland = this.getIslands().getIslandAt(e.getFrom()).orElse(null); + getIslands().getIslandAt(e.getTo()).ifPresent(island -> { if (island.getMemberSet(RanksManager.COOP_RANK).contains(e.getPlayer().getUniqueId())) { return; } + + if (e.getFrom().getWorld() == e.getTo().getWorld() && island == previousIsland) { + // do not report as it is the same world and same island. + return; + } + if (island.isAllowed(Flags.PVP_OVERWORLD)) { alertUser(e.getPlayer(), Flags.PVP_OVERWORLD); } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListener.java index df67b0a5c..0900d396a 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListener.java @@ -15,6 +15,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import world.bentobox.bentobox.BentoBox; @@ -54,6 +55,16 @@ public class ObsidianScoopingListener extends FlagListener { || e.getClickedBlock().getRelative(e.getBlockFace()).getType().equals(Material.WATER)) { return false; } + + if (Material.BUCKET.equals(e.getPlayer().getInventory().getItemInOffHand().getType()) && + Material.BUCKET.equals(e.getPlayer().getInventory().getItemInMainHand().getType()) && + EquipmentSlot.OFF_HAND.equals(e.getHand())) + { + // If player is holding bucket in both hands, then allow to interact only with main hand. + // Prevents lava duplication glitch. + return false; + } + return lookForLava(e); } diff --git a/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java b/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java index 0026d94c3..0d47ed93e 100644 --- a/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java @@ -666,7 +666,28 @@ public class AddonsManager { * @since 1.8.0 */ public void allLoaded() { - addons.forEach(Addon::allLoaded); + this.getEnabledAddons().forEach(this::allLoaded); } + + /** + * This method calls Addon#allLoaded in safe manner. If for some reason addon crashes on Addon#allLoaded, then + * it will disable itself without harming other addons. + * @param addon Addon that should trigger Addon#allLoaded method. + */ + private void allLoaded(@NonNull Addon addon) { + try { + addon.allLoaded(); + } catch (NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) { + // Looks like the addon is incompatible, because it tries to refer to missing classes... + this.handleAddonIncompatibility(addon, e); + // Disable addon. + this.disable(addon); + } catch (Exception e) { + // Unhandled exception. We'll give a bit of debug here. + this.handleAddonError(addon, e); + // Disable addon. + this.disable(addon); + } + } } diff --git a/src/main/java/world/bentobox/bentobox/managers/LocalesManager.java b/src/main/java/world/bentobox/bentobox/managers/LocalesManager.java index 934148c78..46f67d3bd 100644 --- a/src/main/java/world/bentobox/bentobox/managers/LocalesManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/LocalesManager.java @@ -179,7 +179,7 @@ public class LocalesManager { } catch (InvalidConfigurationException e) { plugin.logError("Could not update locale file '" + lf + "' due to it being malformed: " + e.getMessage()); } catch (Exception e) { - plugin.logError("Error updating locale file '" + lf + "': " + e.getMessage()); + plugin.logError("Error updating locale file for " + addon.getDescription().getName() + "'s '" + lf + "': " + e.getMessage()); plugin.logStacktrace(e); } } diff --git a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java index fc1c7e44c..1ab0b7d86 100644 --- a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java @@ -577,17 +577,17 @@ public class PlayersManager { plugin.getVault().ifPresent(vault -> vault.withdraw(target, vault.getBalance(target), world)); } // Reset the health - if (plugin.getIWM().isOnLeaveResetHealth(world)) { + if (plugin.getIWM().isOnLeaveResetHealth(world) && target.isPlayer()) { Util.resetHealth(target.getPlayer()); } // Reset the hunger - if (plugin.getIWM().isOnLeaveResetHunger(world)) { + if (plugin.getIWM().isOnLeaveResetHunger(world) && target.isPlayer()) { target.getPlayer().setFoodLevel(20); } // Reset the XP - if (plugin.getIWM().isOnLeaveResetXP(world)) { + if (plugin.getIWM().isOnLeaveResetXP(world) && target.isPlayer()) { target.getPlayer().setTotalExperience(0); } // Save player diff --git a/src/main/java/world/bentobox/bentobox/managers/WebManager.java b/src/main/java/world/bentobox/bentobox/managers/WebManager.java index dbd5ef1d5..c04d1e9c5 100644 --- a/src/main/java/world/bentobox/bentobox/managers/WebManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/WebManager.java @@ -196,7 +196,7 @@ public class WebManager { @NonNull private String getContent(@NonNull GitHubRepository repo, String fileName) { try { - String content = repo.getContent(fileName).getContent().replaceAll("\\n", ""); + String content = repo.getContent(fileName).getContent().replaceAll("\\n", ""); // replaceAll is required here return new String(Base64.getDecoder().decode(content), StandardCharsets.UTF_8); } catch (IllegalAccessException e) { // Fail silently diff --git a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java index 5e3e76414..e772b6ba5 100644 --- a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java +++ b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java @@ -21,7 +21,9 @@ import world.bentobox.bentobox.managers.RanksManager; */ public class IslandInfo { - private final BentoBox plugin; + private static final String XZ1 = "[xz1]"; + private static final String RANGE = "[range]"; + private final BentoBox plugin; private final Island island; private final @Nullable UUID owner; private final World world; @@ -57,23 +59,23 @@ public class IslandInfo { try { String dateTimeFormat = plugin.getLocalesManager().get("commands.admin.info.last-login-date-time-format"); formattedDate = new SimpleDateFormat(dateTimeFormat).format(new Date(lastPlayed)); - } catch (NullPointerException | IllegalArgumentException ignored) { + } catch (Exception ignored) { formattedDate = new Date(lastPlayed).toString(); } user.sendMessage("commands.admin.info.last-login","[date]", formattedDate); - user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner))); + user.sendMessage("commands.admin.info.deaths", TextVariables.NUMBER, String.valueOf(plugin.getPlayers().getDeaths(world, owner))); String resets = String.valueOf(plugin.getPlayers().getResets(world, owner)); String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world)); - user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total); + user.sendMessage("commands.admin.info.resets-left", TextVariables.NUMBER, resets, "[total]", total); // Show team members showMembers(user); } Vector location = island.getProtectionCenter().toVector(); user.sendMessage("commands.admin.info.island-protection-center", TextVariables.XYZ, Util.xyz(location)); user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(island.getCenter().toVector())); - user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(new Vector(island.getMinX(), 0, island.getMinZ())), "[xz2]", Util.xyz(new Vector(island.getMaxX(), 0, island.getMaxZ()))); - user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(island.getProtectionRange())); + user.sendMessage("commands.admin.info.island-coords", XZ1, Util.xyz(new Vector(island.getMinX(), 0, island.getMinZ())), "[xz2]", Util.xyz(new Vector(island.getMaxX(), 0, island.getMaxZ()))); + user.sendMessage("commands.admin.info.protection-range", RANGE, String.valueOf(island.getProtectionRange())); if (!island.getBonusRanges().isEmpty()) { user.sendMessage("commands.admin.info.protection-range-bonus-title"); } @@ -84,8 +86,8 @@ public class IslandInfo { user.sendMessage(brb.getMessage(), TextVariables.NUMBER, String.valueOf(brb.getRange())); } }); - user.sendMessage("commands.admin.info.max-protection-range", "[range]", String.valueOf(island.getMaxEverProtectionRange())); - user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX() - 1, 0, island.getMaxProtectedZ() - 1))); + user.sendMessage("commands.admin.info.max-protection-range", RANGE, String.valueOf(island.getMaxEverProtectionRange())); + user.sendMessage("commands.admin.info.protection-coords", XZ1, Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX() - 1, 0, island.getMaxProtectedZ() - 1))); if (island.isSpawn()) { user.sendMessage("commands.admin.info.is-spawn"); } @@ -110,17 +112,17 @@ public class IslandInfo { user.sendMessage("commands.admin.info.unowned"); } else { user.sendMessage("commands.admin.info.owner", "[owner]", plugin.getPlayers().getName(owner), TextVariables.UUID, owner.toString()); - user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner))); + user.sendMessage("commands.admin.info.deaths", TextVariables.NUMBER, String.valueOf(plugin.getPlayers().getDeaths(world, owner))); String resets = String.valueOf(plugin.getPlayers().getResets(world, owner)); String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world)); - user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total); + user.sendMessage("commands.admin.info.resets-left", TextVariables.NUMBER, resets, "[total]", total); // Show team members showMembers(user); } Vector location = island.getProtectionCenter().toVector(); user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(location)); - user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(island.getProtectionRange())); - user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX() - 1, 0, island.getMaxProtectedZ() - 1))); + user.sendMessage("commands.admin.info.protection-range", RANGE, String.valueOf(island.getProtectionRange())); + user.sendMessage("commands.admin.info.protection-coords", XZ1, Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX() - 1, 0, island.getMaxProtectedZ() - 1))); if (island.isSpawn()) { user.sendMessage("commands.admin.info.is-spawn"); } diff --git a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java index a413010e5..3e427a0a2 100644 --- a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java +++ b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java @@ -51,6 +51,7 @@ public class SafeSpotTeleport { private final AtomicBoolean checking = new AtomicBoolean(); private BukkitTask task; private boolean portal; + private boolean cancelIfFail; // Locations private Location bestSpot; private Iterator> chunksToScanIterator; @@ -73,6 +74,7 @@ public class SafeSpotTeleport { this.result = builder.getResult(); this.world = Objects.requireNonNull(location.getWorld()); this.maxHeight = world.getMaxHeight() - 20; + this.cancelIfFail = builder.isCancelIfFail(); // Try to go Util.getChunkAtAsync(location).thenRun(() -> tryToGo(builder.getFailureMessage())); } @@ -150,7 +152,7 @@ public class SafeSpotTeleport { if (!plugin.getIWM().inWorld(entity.getLocation())) { // Last resort player.performCommand("spawn"); - } else { + } else if (!this.cancelIfFail) { // Create a spot for the player to be if (world.getEnvironment().equals(Environment.NETHER)) { makeAndTeleport(Material.NETHERRACK); @@ -226,7 +228,7 @@ public class SafeSpotTeleport { // Check the safe spot at the current height for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { - if (minY >= startY && checkBlock(chunk, x, startY, z)) { + if (minY <= startY && checkBlock(chunk, x, startY, z)) { return true; } maxY = Math.max(chunk.getHighestBlockYAt(x, z), maxY); @@ -336,6 +338,7 @@ public class SafeSpotTeleport { private Location location; private Runnable runnable; private Runnable failRunnable; + private boolean cancelIfFail; public Builder(BentoBox plugin) { this.plugin = plugin; @@ -461,6 +464,20 @@ public class SafeSpotTeleport { return new SafeSpotTeleport(this); } + + /** + * This method allows stopping "safe" block generation if teleportation fails. + * @param cancelIfFail - value for canceling + * @return Builder + * @since 1.20.1 + */ + public Builder cancelIfFail(boolean cancelIfFail) + { + this.cancelIfFail = cancelIfFail; + return this; + } + + /** * The task to run after the player is safely teleported. * @@ -557,5 +574,13 @@ public class SafeSpotTeleport { } + /** + * @return the cancelIfFail + * @since 1.20.1 + */ + public boolean isCancelIfFail() + { + return this.cancelIfFail; + } } } diff --git a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java index cc2ff5bc8..6fd5d1011 100644 --- a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java +++ b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java @@ -194,6 +194,10 @@ public class ServerCompatibility { * @since 1.19.0 */ V1_18_1(Compatibility.COMPATIBLE), + /** + * @since 1.20.1 + */ + V1_18_2(Compatibility.COMPATIBLE), ; private final Compatibility compatibility; diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 2da92e94e..51c28f0c9 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -652,8 +652,9 @@ commands: kick: description: "remove a member from your island" parameters: "" - owner-kicked: "&c The owner kicked you from the island in [gamemode]!" + player-kicked: "&c The [name] kicked you from the island in [gamemode]!" cannot-kick: "&c You cannot kick yourself!" + cannot-kick-rank: "&c Your rank does not allow to kick [name]!" success: "&b [name] &a has been kicked from your island." demote: description: "demote a player on your island down a rank" diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java index 3e1c3ac28..b51cec80c 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java @@ -184,6 +184,10 @@ public class IslandTeamKickCommandTest { RanksManager rm = new RanksManager(); when(plugin.getRanksManager()).thenReturn(rm); + // Ranks + when(island.getRank(uuid)).thenReturn(RanksManager.OWNER_RANK); + when(island.getRank(user)).thenReturn(RanksManager.OWNER_RANK); + when(island.getRank(notUUID)).thenReturn(RanksManager.MEMBER_RANK); } @After @@ -206,11 +210,75 @@ public class IslandTeamKickCommandTest { * Test method for {@link IslandTeamKickCommand#execute(User, String, java.util.List)} */ @Test - public void testExecuteNotTeamOwner() { - when(im.getOwner(any(), any())).thenReturn(notUUID); + public void testExecuteLowerTeamRank() { + when(island.getRank(user)).thenReturn(RanksManager.MEMBER_RANK); + when(island.getRank(notUUID)).thenReturn(RanksManager.SUB_OWNER_RANK); + + when(pm.getUUID(any())).thenReturn(notUUID); + when(pm.getName(notUUID)).thenReturn("poslovitch"); + + Set members = new HashSet<>(); + members.add(notUUID); + when(im.getMembers(any(), any())).thenReturn(members); + + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch"))); + verify(user).sendMessage(eq("commands.island.team.kick.cannot-kick-rank"), eq(TextVariables.NAME), eq("poslovitch")); + } + + + /** + * Test method for {@link IslandTeamKickCommand#execute(User, String, java.util.List)} + */ + @Test + public void testExecuteEqualTeamRank() { + when(island.getRank(user)).thenReturn(RanksManager.SUB_OWNER_RANK); + when(island.getRank(notUUID)).thenReturn(RanksManager.SUB_OWNER_RANK); + + when(pm.getUUID(any())).thenReturn(notUUID); + when(pm.getName(notUUID)).thenReturn("poslovitch"); + + Set members = new HashSet<>(); + members.add(notUUID); + when(im.getMembers(any(), any())).thenReturn(members); + + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + assertFalse(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch"))); + verify(user).sendMessage(eq("commands.island.team.kick.cannot-kick-rank"), eq(TextVariables.NAME), eq("poslovitch")); + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(User, String, java.util.List)} + */ + @Test + public void testExecuteLargerTeamRank() { + when(island.getRank(user)).thenReturn(RanksManager.SUB_OWNER_RANK); + when(island.getRank(notUUID)).thenReturn(RanksManager.MEMBER_RANK); + + when(pm.getUUID(any())).thenReturn(notUUID); + when(pm.getName(notUUID)).thenReturn("poslovitch"); + + Set members = new HashSet<>(); + members.add(notUUID); + when(im.getMembers(any(), any())).thenReturn(members); + + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); + assertTrue(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch"))); + verify(im).removePlayer(any(), eq(notUUID)); + verify(user).sendMessage("commands.island.team.kick.success", TextVariables.NAME, "poslovitch"); + } + + /** + * Test method for {@link IslandTeamKickCommand#execute(User, String, java.util.List)} + */ + @Test + public void testExecuteNoCommandRank() { + when(island.getRankCommand(anyString())).thenReturn(RanksManager.SUB_OWNER_RANK); + when(island.getRank(user)).thenReturn(RanksManager.MEMBER_RANK); + IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); assertFalse(itl.execute(user, itl.getLabel(), Collections.emptyList())); - verify(user).sendMessage(eq("general.errors.not-owner")); + verify(user).sendMessage(eq("general.errors.insufficient-rank"), eq(TextVariables.RANK), eq("ranks.member")); } /** diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java b/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java index ba9131e91..3140a989a 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java @@ -21,10 +21,12 @@ import org.bukkit.metadata.MetadataValue; import org.bukkit.plugin.PluginManager; import org.eclipse.jdt.annotation.Nullable; import org.junit.After; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; +import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; import com.google.common.collect.ImmutableSet; @@ -57,6 +59,7 @@ import world.bentobox.bentobox.util.Util; * @author tastybento * */ +@RunWith(PowerMockRunner.class) public abstract class AbstractCommonSetup { protected UUID uuid = UUID.randomUUID(); @@ -136,7 +139,7 @@ public abstract class AbstractCommonSetup { // Island - nothing is allowed by default when(island.isAllowed(any())).thenReturn(false); - when(island.isAllowed(any(), any())).thenReturn(false); + when(island.isAllowed(any(User.class), any())).thenReturn(false); when(island.getOwner()).thenReturn(uuid); when(island.getMemberSet()).thenReturn(ImmutableSet.of(uuid)); diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java index 7cde56a00..0f258b06e 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java @@ -69,6 +69,10 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { position = new Vector(10,10,10); when(inv.getItemInMainHand()).thenReturn(new ItemStack(Material.NAME_TAG)); + // Initialize the Flags class. This is a workaround to prevent weird errors when mocking + // I think it's because the flag class needs to be initialized before use in argument matchers + Flags.TRADING.setDefaultSetting(false); + // Class under test eil = new EntityInteractListener(); } @@ -91,7 +95,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { */ @Test public void testOnPlayerInteractAtEntityArmorStandAllowed() { - when(island.isAllowed(any(), any())).thenReturn(true); + when(island.isAllowed(any(User.class), any())).thenReturn(true); clickedEntity = mock(ArmorStand.class); when(clickedEntity.getLocation()).thenReturn(location); PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position, hand); @@ -118,7 +122,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { */ @Test public void testOnPlayerInteractEntityHorseAllowed() { - when(island.isAllowed(any(), any())).thenReturn(true); + when(island.isAllowed(any(User.class), any())).thenReturn(true); clickedEntity = mock(Horse.class); when(clickedEntity.getLocation()).thenReturn(location); PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); @@ -145,7 +149,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { */ @Test public void testOnPlayerInteractEntityMinecartAllowed() { - when(island.isAllowed(any(), any())).thenReturn(true); + when(island.isAllowed(any(User.class), any())).thenReturn(true); clickedEntity = mock(RideableMinecart.class); when(clickedEntity.getLocation()).thenReturn(location); PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); @@ -172,7 +176,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { */ @Test public void testOnPlayerInteractEntityBoatAllowed() { - when(island.isAllowed(any(), any())).thenReturn(true); + when(island.isAllowed(any(User.class), any())).thenReturn(true); clickedEntity = mock(Boat.class); when(clickedEntity.getLocation()).thenReturn(location); PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); @@ -199,7 +203,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { */ @Test public void testOnPlayerInteractAtEntityVillagerAllowed() { - when(island.isAllowed(any(), any())).thenReturn(true); + when(island.isAllowed(any(User.class), any())).thenReturn(true); clickedEntity = mock(Villager.class); when(clickedEntity.getLocation()).thenReturn(location); PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); @@ -212,9 +216,10 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent)}. */ @Test + public void testOnPlayerInteractEntityNamingVillagerAllowedNoTrading() { - when(island.isAllowed(any(), eq(Flags.TRADING))).thenReturn(false); - when(island.isAllowed(any(), eq(Flags.NAME_TAG))).thenReturn(true); + when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(false); + when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(true); clickedEntity = mock(Villager.class); when(clickedEntity.getLocation()).thenReturn(location); PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); @@ -228,8 +233,8 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { */ @Test public void testOnPlayerInteractEntityNamingVillagerAllowedTradingNoNaming() { - when(island.isAllowed(any(), eq(Flags.TRADING))).thenReturn(true); - when(island.isAllowed(any(), eq(Flags.NAME_TAG))).thenReturn(false); + when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(true); + when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(false); clickedEntity = mock(Villager.class); when(clickedEntity.getLocation()).thenReturn(location); PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand); @@ -257,7 +262,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { */ @Test public void testOnPlayerInteractAtEntityWanderingTraderAllowed() { - when(island.isAllowed(any(), any())).thenReturn(true); + when(island.isAllowed(any(User.class), any())).thenReturn(true); clickedEntity = mock(WanderingTrader.class); when(clickedEntity.getType()).thenReturn(EntityType.WANDERING_TRADER); when(clickedEntity.getLocation()).thenReturn(location); @@ -271,9 +276,12 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent)}. */ @Test + public void testOnPlayerInteractEntityNamingWanderingTraderAllowedNoTrading() { - when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(false); - when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(true); + when(island.isAllowed(any(), + eq(Flags.TRADING))).thenReturn(false); + when(island.isAllowed(any(User.class), + eq(Flags.NAME_TAG))).thenReturn(true); clickedEntity = mock(WanderingTrader.class); when(clickedEntity.getType()).thenReturn(EntityType.WANDERING_TRADER); when(clickedEntity.getLocation()).thenReturn(location); @@ -287,6 +295,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup { * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent)}. */ @Test + public void testOnPlayerInteractEntityNamingWanderingTraderAllowedTradingNoNaming() { when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(true); when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(false); diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListenerTest.java index 73f134462..8eb31b5b0 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/ObsidianScoopingListenerTest.java @@ -153,6 +153,11 @@ public class ObsidianScoopingListenerTest { map.put("OBSIDIAN_SCOOPING", true); when(ws.getWorldFlags()).thenReturn(map); + PlayerInventory playerInventory = mock(PlayerInventory.class); + when(playerInventory.getItemInMainHand()).thenReturn(item); + when(playerInventory.getItemInOffHand()).thenReturn(new ItemStack(Material.AIR)); + when(p.getInventory()).thenReturn(playerInventory); + // Addon when(iwm.getAddon(Mockito.any())).thenReturn(Optional.empty()); } diff --git a/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java index c99d1b0da..033e154ac 100644 --- a/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/PlayersManagerTest.java @@ -169,6 +169,7 @@ public class PlayersManagerTest { when(user.getPlayer()).thenReturn(p); when(user.getName()).thenReturn("tastybento"); when(user.isOnline()).thenReturn(true); + when(user.isPlayer()).thenReturn(true); User.setPlugin(plugin);