diff --git a/pom.xml b/pom.xml
index da107d0bd..89e22cc51 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,7 +88,7 @@
-LOCAL
- 2.4.1
+ 2.4.2
bentobox-world
https://sonarcloud.io
${project.basedir}/lib
diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteGUI.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteGUI.java
index 4fba9e558..c84727767 100644
--- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteGUI.java
+++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteGUI.java
@@ -219,7 +219,8 @@ public class IslandTeamInviteGUI {
return true;
}
if (clickType.equals(ClickType.LEFT)) {
- user.closeInventory();
+ // Close inventory after one tick to allow the no pickup click return to occur
+ Bukkit.getScheduler().runTask(plugin, () -> user.closeInventory());
if (itic.canExecute(user, itic.getLabel(), List.of(player.getName()))) {
plugin.log("Invite sent to: " + player.getName() + " by " + user.getName() + " to join island in "
+ itc.getWorld().getName());
@@ -229,7 +230,8 @@ public class IslandTeamInviteGUI {
+ itc.getWorld().getName());
}
} else if (clickType.equals(ClickType.RIGHT)) {
- user.closeInventory();
+ // Close inventory after one tick to allow the no pickup click return to occur
+ Bukkit.getScheduler().runTask(plugin, () -> user.closeInventory());
if (this.itc.getCoopCommand().canExecute(user, itic.getLabel(), List.of(player.getName()))) {
plugin.log("Coop: " + player.getName() + " cooped " + user.getName() + " to island in "
+ itc.getWorld().getName());
@@ -240,15 +242,15 @@ public class IslandTeamInviteGUI {
+ itc.getWorld().getName());
}
} else if (clickType.equals(ClickType.SHIFT_LEFT)) {
- user.closeInventory();
+ // Close inventory after one tick to allow the no pickup click return to occur
+ Bukkit.getScheduler().runTask(plugin, () -> user.closeInventory());
if (this.itc.getTrustCommand().canExecute(user, itic.getLabel(), List.of(player.getName()))) {
plugin.log("Trust: " + player.getName() + " trusted " + user.getName() + " to island in "
+ itc.getWorld().getName());
this.itc.getTrustCommand().execute(user, itic.getLabel(), List.of(player.getName()));
} else {
plugin.log("Trust failed: " + player.getName() + "'s trust failed for " + user.getName()
- + " for island in "
- + itc.getWorld().getName());
+ + " for island in " + itc.getWorld().getName());
}
}
return true;
@@ -272,8 +274,8 @@ public class IslandTeamInviteGUI {
public Prompt acceptInput(@NonNull ConversationContext context, String input) {
if (itic.canExecute(user, itic.getLabel(), List.of(input))
&& itic.execute(user, itic.getLabel(), List.of(input))) {
- return Prompt.END_OF_CONVERSATION;
- }
+ return Prompt.END_OF_CONVERSATION;
+ }
// Set the search item to what was entered
searchName = input;
// Return to the GUI but give a second for the error to show
diff --git a/src/main/java/world/bentobox/bentobox/database/json/BentoboxTypeAdapterFactory.java b/src/main/java/world/bentobox/bentobox/database/json/BentoboxTypeAdapterFactory.java
index e7aca2f8b..6615ced50 100644
--- a/src/main/java/world/bentobox/bentobox/database/json/BentoboxTypeAdapterFactory.java
+++ b/src/main/java/world/bentobox/bentobox/database/json/BentoboxTypeAdapterFactory.java
@@ -9,6 +9,8 @@ import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.entity.Villager;
+import org.bukkit.entity.Villager.Profession;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
@@ -29,7 +31,9 @@ import world.bentobox.bentobox.database.json.adapters.LocationTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.MaterialTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.PairTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.PotionEffectTypeAdapter;
+import world.bentobox.bentobox.database.json.adapters.ProfessionTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.VectorTypeAdapter;
+import world.bentobox.bentobox.database.json.adapters.VillagerTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.WorldTypeAdapter;
import world.bentobox.bentobox.util.Pair;
@@ -78,6 +82,10 @@ public class BentoboxTypeAdapterFactory implements TypeAdapterFactory {
return (TypeAdapter) new WorldTypeAdapter();
} else if (Vector.class.isAssignableFrom(rawType)) {
return (TypeAdapter) new VectorTypeAdapter();
+ } else if (Profession.class.isAssignableFrom(rawType)) {
+ return (TypeAdapter) new ProfessionTypeAdapter();
+ } else if (Villager.Type.class.isAssignableFrom(rawType)) {
+ return (TypeAdapter) new VillagerTypeAdapter();
} else if (Pair.class.isAssignableFrom(rawType)) {
// Add Pair handling here with type safety
Type pairType = type.getType();
diff --git a/src/main/java/world/bentobox/bentobox/database/json/adapters/ProfessionTypeAdapter.java b/src/main/java/world/bentobox/bentobox/database/json/adapters/ProfessionTypeAdapter.java
new file mode 100644
index 000000000..bd40494ec
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/database/json/adapters/ProfessionTypeAdapter.java
@@ -0,0 +1,34 @@
+package world.bentobox.bentobox.database.json.adapters;
+
+import java.io.IOException;
+
+import org.bukkit.entity.Villager.Profession;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+public class ProfessionTypeAdapter extends TypeAdapter {
+
+ @Override
+ public void write(JsonWriter out, Profession profession) throws IOException {
+ out.value(profession.name());
+ }
+
+ @Override
+ public Profession read(JsonReader reader) throws IOException {
+ if (reader.peek() == JsonToken.NULL) {
+ reader.nextNull();
+ return null;
+ }
+ String id = reader.nextString();
+ try {
+ return Profession.valueOf(id);
+ } catch (Exception e) {
+ // Do nothing
+ }
+ return Profession.NONE;
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/world/bentobox/bentobox/database/json/adapters/VillagerTypeAdapter.java b/src/main/java/world/bentobox/bentobox/database/json/adapters/VillagerTypeAdapter.java
new file mode 100644
index 000000000..050142ad6
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/database/json/adapters/VillagerTypeAdapter.java
@@ -0,0 +1,34 @@
+package world.bentobox.bentobox.database.json.adapters;
+
+import java.io.IOException;
+
+import org.bukkit.entity.Villager;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+public class VillagerTypeAdapter extends TypeAdapter {
+
+ @Override
+ public void write(JsonWriter out, Villager.Type type) throws IOException {
+ out.value(type.name());
+ }
+
+ @Override
+ public Villager.Type read(JsonReader reader) throws IOException {
+ if (reader.peek() == JsonToken.NULL) {
+ reader.nextNull();
+ return null;
+ }
+ String id = reader.nextString();
+ try {
+ return Villager.Type.valueOf(id);
+ } catch (Exception e) {
+ // Do nothing
+ }
+ return Villager.Type.PLAINS;
+
+ }
+}
\ No newline at end of file
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 7f26e3180..848b98863 100644
--- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java
+++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java
@@ -501,7 +501,7 @@ public class Island implements DataObject, MetaDataAble {
/**
* Get the maximum protected Z block coordinate based on the island location. It
- * will never be more than {@link #getMinZ()}
+ * will never be more than {@link #getMaxZ()}
*
* @return the maxProtectedZ
* @since 1.5.2
diff --git a/src/main/java/world/bentobox/bentobox/hooks/LangUtilsHook.java b/src/main/java/world/bentobox/bentobox/hooks/LangUtilsHook.java
index 5a6b9ae70..d1ded8cea 100644
--- a/src/main/java/world/bentobox/bentobox/hooks/LangUtilsHook.java
+++ b/src/main/java/world/bentobox/bentobox/hooks/LangUtilsHook.java
@@ -363,16 +363,23 @@ public class LangUtilsHook extends Hook {
* @param user The user's language will be used for translation.
* @return Return the translation result.
*/
+ @SuppressWarnings("deprecation")
public static String getPotionBaseEffectName(PotionType potionType, User user) {
if (hooked) {
return LanguageHelper.getPotionBaseEffectName(potionType, getUserLocale(user));
}
- List effects = potionType.getPotionEffects();
- if (effects.isEmpty()) {
- return "No Effects";
+ try {
+ List effects = potionType.getPotionEffects();
+
+ if (effects.isEmpty()) {
+ return "No Effects";
+ }
+ return effects.stream().map(effect -> Util.prettifyText(effect.getType().getKey().getKey()))
+ .collect(Collectors.joining(", "));
+ } catch (Exception e) {
+ // Older versions of Spigot pre-1.20.4 don't have getPotionEffects()
+ return Util.prettifyText(potionType.getEffectType().getKey().getKey());
}
- return effects.stream().map(effect -> Util.prettifyText(effect.getType().getKey().getKey()))
- .collect(Collectors.joining(", "));
}
/**
diff --git a/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java b/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java
index 6cb57fd45..ff5149f52 100644
--- a/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java
+++ b/src/main/java/world/bentobox/bentobox/listeners/PanelListenerManager.java
@@ -26,7 +26,7 @@ public class PanelListenerManager implements Listener {
private static final HashMap openPanels = new HashMap<>();
- @EventHandler(priority = EventPriority.HIGHEST)
+ @EventHandler(priority = EventPriority.LOW)
public void onInventoryClick(InventoryClickEvent event) {
User user = User.getInstance(event.getWhoClicked()); // The player that clicked the item
InventoryView view = event.getView();
diff --git a/src/main/java/world/bentobox/bentobox/listeners/SeedWorldMakerListener.java b/src/main/java/world/bentobox/bentobox/listeners/SeedWorldMakerListener.java
index 72e42adae..56dc79cb8 100644
--- a/src/main/java/world/bentobox/bentobox/listeners/SeedWorldMakerListener.java
+++ b/src/main/java/world/bentobox/bentobox/listeners/SeedWorldMakerListener.java
@@ -39,7 +39,7 @@ public class SeedWorldMakerListener implements Listener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onChunkLoad(ChunkLoadEvent e) {
- if (!ready || !e.getChunk().isGenerated()) {
+ if (!ready) {
return;
}
World world = e.getWorld();
@@ -47,7 +47,7 @@ public class SeedWorldMakerListener implements Listener {
World seed = Bukkit.getWorld(world.getName() + "/bentobox");
int x = e.getChunk().getX();
int z = e.getChunk().getZ();
- if (seed != null && !seed.getChunkAt(x, z, false).isGenerated()) {
+ if (seed != null) {
Util.getChunkAtAsync(seed, x, z, true);
}
});
diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java
index aecaf82c0..496ae5238 100644
--- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java
+++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java
@@ -100,9 +100,11 @@ public class BlockInteractionListener extends FlagListener
switch (type)
{
case BEACON -> this.checkIsland(e, player, loc, Flags.BEACON);
+ case BELL -> this.checkIsland(e, player, loc, Flags.BELL_RINGING);
case BREWING_STAND -> this.checkIsland(e, player, loc, Flags.BREWING);
case BEEHIVE, BEE_NEST -> this.checkIsland(e, player, loc, Flags.HIVE);
case BARREL -> this.checkIsland(e, player, loc, Flags.BARREL);
+ case CANDLE -> this.checkIsland(e, player, loc, Flags.CANDLES);
case CHEST, CHEST_MINECART -> this.checkIsland(e, player, loc, Flags.CHEST);
case TRAPPED_CHEST -> this.checkIsland(e, player, loc, Flags.TRAPPED_CHEST);
case FLOWER_POT -> this.checkIsland(e, player, loc, Flags.FLOWER_POT);
diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreakBlocksListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreakBlocksListener.java
index b40c99b05..bf8ec1cab 100644
--- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreakBlocksListener.java
+++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreakBlocksListener.java
@@ -21,6 +21,8 @@ import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.vehicle.VehicleDamageEvent;
+import com.google.common.base.Enums;
+
import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.lists.Flags;
@@ -101,6 +103,10 @@ public class BreakBlocksListener extends FlagListener {
{
return;
}
+ if (Enums.getIfPresent(Material.class, "TRIAL_SPAWNER").isPresent() && m.equals(Material.TRIAL_SPAWNER)) {
+ this.checkIsland(e, p, l, Flags.BREAK_SPAWNERS);
+ return;
+ }
switch (m)
{
case CAKE -> this.checkIsland(e, p, l, Flags.BREAK_BLOCKS);
@@ -182,7 +188,7 @@ public class BreakBlocksListener extends FlagListener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onProjectileHitBreakBlock(ProjectileHitEvent e) {
// We want to make sure this is an actual projectile (arrow or trident)
- if (!(e.getEntity() instanceof AbstractArrow)) {
+ if (!(e.getEntity() instanceof Projectile)) {
return;
}
diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/CandleListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/CandleListener.java
new file mode 100644
index 000000000..bd8f92c0b
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/CandleListener.java
@@ -0,0 +1,33 @@
+package world.bentobox.bentobox.listeners.flags.protection;
+
+import org.bukkit.Tag;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.player.PlayerInteractEvent;
+
+import world.bentobox.bentobox.api.flags.FlagListener;
+import world.bentobox.bentobox.lists.Flags;
+
+/**
+ * Protects candles
+ * @author tastybento
+ * @since 2.4.2
+ */
+public class CandleListener extends FlagListener {
+
+ /**
+ * Prevent dying signs.
+ * @param e - event
+ */
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onCandleInteract(final PlayerInteractEvent e) {
+ if (e.getClickedBlock() == null) {
+ return;
+ }
+
+ if (Tag.CANDLES.isTagged(e.getClickedBlock().getType())
+ || Tag.CANDLE_CAKES.isTagged(e.getClickedBlock().getType())) {
+ this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.CANDLES);
+ }
+ }
+}
diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/TNTListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/ExplosionListener.java
similarity index 99%
rename from src/main/java/world/bentobox/bentobox/listeners/flags/protection/TNTListener.java
rename to src/main/java/world/bentobox/bentobox/listeners/flags/protection/ExplosionListener.java
index f0878c8ce..d000eea7f 100644
--- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/TNTListener.java
+++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/ExplosionListener.java
@@ -27,7 +27,7 @@ import world.bentobox.bentobox.lists.Flags;
* Protects islands from visitors blowing things up
* @author tastybento
*/
-public class TNTListener extends FlagListener {
+public class ExplosionListener extends FlagListener {
/**
* Contains {@link EntityType}s that generates an explosion.
* @since 1.5.0
diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListener.java
index bef997cd8..0580802be 100644
--- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListener.java
+++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListener.java
@@ -1,15 +1,23 @@
package world.bentobox.bentobox.listeners.flags.protection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
import org.bukkit.Material;
import org.bukkit.Tag;
+import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
+import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
+import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityInteractEvent;
import org.bukkit.event.player.PlayerInteractEvent;
+import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.lists.Flags;
@@ -55,24 +63,47 @@ public class PhysicalInteractionListener extends FlagListener
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onProjectileHit(EntityInteractEvent e)
{
- if (!(e.getEntity() instanceof Projectile p))
+ if (e.getEntity() instanceof Projectile p && p.getShooter() instanceof Player player)
{
- return;
- }
-
- if (p.getShooter() instanceof Player player)
- {
- if (Tag.WOODEN_BUTTONS.isTagged(e.getBlock().getType()))
- {
- this.checkIsland(e, player, e.getBlock().getLocation(), Flags.BUTTON);
- return;
- }
-
- if (Tag.PRESSURE_PLATES.isTagged(e.getBlock().getType()))
- {
- // Pressure plates
- this.checkIsland(e, player, e.getBlock().getLocation(), Flags.PRESSURE_PLATE);
- }
+ checkBlocks(e, player, e.getBlock());
}
}
+
+ private boolean checkBlocks(Event e, Player player, Block block) {
+ Map, Flag> TAG_TO_FLAG = Map.of(Tag.WOODEN_BUTTONS, Flags.BUTTON, Tag.PRESSURE_PLATES,
+ Flags.PRESSURE_PLATE, Tag.FENCE_GATES, Flags.GATE, Tag.DOORS, Flags.DOOR, Tag.CANDLE_CAKES,
+ Flags.CANDLES, Tag.CANDLES, Flags.CANDLES);
+ Map MAT_TO_FLAG = Map.of(Material.LEVER, Flags.LEVER, Material.TRIPWIRE, Flags.REDSTONE,
+ Material.TARGET, Flags.REDSTONE, Material.DECORATED_POT, Flags.BREAK_BLOCKS);
+ boolean result = TAG_TO_FLAG.entrySet().stream().filter(entry -> entry.getKey().isTagged(block.getType()))
+ .findFirst().map(entry -> this.checkIsland(e, player, block.getLocation(), entry.getValue()))
+ .orElse(true);
+ if (result && MAT_TO_FLAG.containsKey(block.getType())) {
+ result = this.checkIsland(e, player, block.getLocation(), MAT_TO_FLAG.get(block.getType()));
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Protects buttons and plates, etc. from being activated by projectiles that explode
+ * @param e - event
+ */
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onProjectileExplode(EntityExplodeEvent e) {
+ if (e.getEntity() instanceof Projectile p && p.getShooter() instanceof Player player) {
+ List blocksToRemove = new ArrayList<>();
+
+ for (Block b : e.blockList()) {
+ if (!this.checkBlocks(e, player, b)) {
+ blocksToRemove.add(b);
+ }
+ }
+
+ e.blockList().removeAll(blocksToRemove);
+ }
+ }
+
+
}
diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/SpawnerSpawnEggsListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/SpawnerSpawnEggsListener.java
index 09c5d2e5c..34a7baac6 100644
--- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/SpawnerSpawnEggsListener.java
+++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/SpawnerSpawnEggsListener.java
@@ -1,6 +1,5 @@
package world.bentobox.bentobox.listeners.flags.worldsettings;
-import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerInteractEvent;
@@ -21,8 +20,8 @@ public class SpawnerSpawnEggsListener extends FlagListener {
public void onSpawnerChange(final PlayerInteractEvent e) {
User user = User.getInstance(e.getPlayer());
// Checking if the clicked block is a spawner and the item in hand is a mob egg
- if (e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.SPAWNER)
- && e.getItem() != null && e.getItem().getType().toString().endsWith("_SPAWN_EGG")
+ if (e.getClickedBlock() != null && e.getClickedBlock().getType().name().endsWith("_SPAWNER")
+ && e.getItem() != null && e.getItem().getType().name().endsWith("_SPAWN_EGG")
&& getIWM().inWorld(e.getClickedBlock().getWorld())
&& !(user.hasPermission(getIWM().getPermissionPrefix(e.getClickedBlock().getWorld()) + "mod.bypass." + Flags.SPAWNER_SPAWN_EGGS.getID() + ".everywhere")
|| user.hasPermission(getIWM().getPermissionPrefix(e.getClickedBlock().getWorld()) + "mod.bypassprotect"))
diff --git a/src/main/java/world/bentobox/bentobox/lists/Flags.java b/src/main/java/world/bentobox/bentobox/lists/Flags.java
index d0f5c75b5..15eb623d7 100644
--- a/src/main/java/world/bentobox/bentobox/lists/Flags.java
+++ b/src/main/java/world/bentobox/bentobox/lists/Flags.java
@@ -19,6 +19,7 @@ import world.bentobox.bentobox.listeners.flags.protection.BlockInteractionListen
import world.bentobox.bentobox.listeners.flags.protection.BreakBlocksListener;
import world.bentobox.bentobox.listeners.flags.protection.BreedingListener;
import world.bentobox.bentobox.listeners.flags.protection.BucketListener;
+import world.bentobox.bentobox.listeners.flags.protection.CandleListener;
import world.bentobox.bentobox.listeners.flags.protection.DyeListener;
import world.bentobox.bentobox.listeners.flags.protection.EggListener;
import world.bentobox.bentobox.listeners.flags.protection.ElytraListener;
@@ -38,7 +39,7 @@ import world.bentobox.bentobox.listeners.flags.protection.PortalListener;
import world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener;
import world.bentobox.bentobox.listeners.flags.protection.SculkShriekerListener;
import world.bentobox.bentobox.listeners.flags.protection.ShearingListener;
-import world.bentobox.bentobox.listeners.flags.protection.TNTListener;
+import world.bentobox.bentobox.listeners.flags.protection.ExplosionListener;
import world.bentobox.bentobox.listeners.flags.protection.TeleportationListener;
import world.bentobox.bentobox.listeners.flags.protection.ThrowingListener;
import world.bentobox.bentobox.listeners.flags.settings.DecayListener;
@@ -266,9 +267,9 @@ public final class Flags {
* Prevents players from priming TNT.
* @since 1.5.0
*
- * @see TNTListener
+ * @see ExplosionListener
*/
- public static final Flag TNT_PRIMING = new Flag.Builder("TNT_PRIMING", Material.TNT).listener(new TNTListener()).build();
+ public static final Flag TNT_PRIMING = new Flag.Builder("TNT_PRIMING", Material.TNT).listener(new ExplosionListener()).build();
/**
* Prevents players from extinguishing fires.
@@ -461,7 +462,7 @@ public final class Flags {
/**
* If {@code false}, prevents TNT from breaking blocks and damaging nearby entities.
* @since 1.5.0
- * @see TNTListener
+ * @see ExplosionListener
*/
public static final Flag TNT_DAMAGE = new Flag.Builder("TNT_DAMAGE", Material.TNT).type(Type.SETTING)
.mode(Flag.Mode.ADVANCED).build();
@@ -469,7 +470,7 @@ public final class Flags {
/**
* If {@code false}, prevents Block Explode from breaking blocks and damaging nearby entities.
* @since 1.19.1
- * @see TNTListener
+ * @see ExplosionListener
*/
public static final Flag BLOCK_EXPLODE_DAMAGE = new Flag.Builder("BLOCK_EXPLODE_DAMAGE", Material.TNT_MINECART).type(Type.SETTING)
.mode(Flag.Mode.ADVANCED).build();
@@ -477,7 +478,7 @@ public final class Flags {
/**
* If {@code false}, prevents TNT from breaking blocks and damaging nearby entities outside of island boundaries.
* @since 1.15.3
- * @see TNTListener
+ * @see ExplosionListener
*/
public static final Flag WORLD_TNT_DAMAGE = new Flag.Builder("WORLD_TNT_DAMAGE", Material.TNT)
.type(Type.WORLD_SETTING)
@@ -486,7 +487,7 @@ public final class Flags {
/**
* If {@code false}, prevents Block Explode from breaking blocks and damaging nearby entities outside of island boundaries.
* @since 1.19.1
- * @see TNTListener
+ * @see ExplosionListener
*/
public static final Flag WORLD_BLOCK_EXPLODE_DAMAGE = new Flag.Builder("WORLD_BLOCK_EXPLODE_DAMAGE", Material.TNT_MINECART)
.type(Type.WORLD_SETTING)
@@ -687,6 +688,23 @@ public final class Flags {
*/
public static final Flag SIGN_EDITING = new Flag.Builder("SIGN_EDITING", Material.DARK_OAK_SIGN).mode(Flag.Mode.BASIC).type(Type.PROTECTION).build();
+ /**
+ * Bell ringing protection
+ * Listeners are {@link BlockInteractionListener} and {@link PhysicalInteractionListener}
+ * @since 2.4.2
+ */
+ public static final Flag BELL_RINGING = new Flag.Builder("BELL_RINGING", Material.BELL).mode(Flag.Mode.EXPERT)
+ .type(Type.PROTECTION).build();
+
+ /**
+ * Candle protection
+ * Listener is {@link CandleListener}
+ * @since 2.4.2
+ */
+ public static final Flag CANDLES = new Flag.Builder("CANDLES", Material.CANDLE).mode(Flag.Mode.EXPERT)
+ .listener(new CandleListener())
+ .type(Type.PROTECTION).build();
+
/**
* Provides a list of all the Flag instances contained in this class using reflection.
* Deprecated Flags are ignored.
diff --git a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java
index d472ba78d..5677b3c89 100644
--- a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java
+++ b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java
@@ -125,6 +125,22 @@ public enum GameModePlaceholder {
*/
ISLAND_MEMBERS_LIST("island_members_list", (addon, user, island) -> island == null ? "" : island.getMemberSet(RanksManager.MEMBER_RANK).stream()
.map(addon.getPlayers()::getName).collect(Collectors.joining(","))),
+ /**
+ * Returns a comma separated list of player names that are at least TRUSTED on this island.
+ * @since 2.4.2
+ */
+ ISLAND_TRUSTED_LIST("island_trusted_list",
+ (addon, user, island) -> island == null ? ""
+ : island.getMemberSet(RanksManager.TRUSTED_RANK, false).stream().map(addon.getPlayers()::getName)
+ .collect(Collectors.joining(","))),
+ /**
+ * Returns a comma separated list of player names that are at least COOP on this island.
+ * @since 2.4.2
+ */
+ ISLAND_COOP_LIST("island_coop_list",
+ (addon, user, island) -> island == null ? ""
+ : island.getMemberSet(RanksManager.COOP_RANK, false).stream().map(addon.getPlayers()::getName)
+ .collect(Collectors.joining(","))),
/**
* Returns the amount of players that are TRUSTED on this island.
* @since 1.5.0
@@ -244,6 +260,20 @@ public enum GameModePlaceholder {
VISITED_ISLAND_MEMBERS_LIST("visited_island_members_list", (addon, user, island) ->
getVisitedIsland(addon, user).map(value -> value.getMemberSet(RanksManager.MEMBER_RANK).stream()
.map(addon.getPlayers()::getName).collect(Collectors.joining(","))).orElse("")),
+ /**
+ * Returns a comma separated list of player names that are at TRUSTED on the island the player is standing on.
+ * @since 2.4.2
+ */
+ VISITED_ISLAND_TRUSTED_LIST("visited_island_trusted_list", (addon, user,
+ island) -> getVisitedIsland(addon, user).map(value -> value.getMemberSet(RanksManager.TRUSTED_RANK, false)
+ .stream().map(addon.getPlayers()::getName).collect(Collectors.joining(","))).orElse("")),
+ /**
+ * Returns a comma separated list of player names that are COOP on the island the player is standing on.
+ * @since 2.4.2
+ */
+ VISITED_ISLAND_COOP_LIST("visited_island_coop_list", (addon, user,
+ island) -> getVisitedIsland(addon, user).map(value -> value.getMemberSet(RanksManager.COOP_RANK, false)
+ .stream().map(addon.getPlayers()::getName).collect(Collectors.joining(","))).orElse("")),
/**
* Returns the amount of players that are at least MEMBER on the island the player is standing on.
* @since 1.5.2
diff --git a/src/main/java/world/bentobox/bentobox/managers/BlueprintClipboardManager.java b/src/main/java/world/bentobox/bentobox/managers/BlueprintClipboardManager.java
index 4a5e66c33..fdcbddee3 100644
--- a/src/main/java/world/bentobox/bentobox/managers/BlueprintClipboardManager.java
+++ b/src/main/java/world/bentobox/bentobox/managers/BlueprintClipboardManager.java
@@ -108,7 +108,9 @@ public class BlueprintClipboardManager {
bp = gson.fromJson(fr, Blueprint.class);
} catch (Exception e) {
plugin.logError("Blueprint has JSON error: " + zipFile.getName());
+ plugin.logStacktrace(e);
throw new IOException("Blueprint has JSON error: " + zipFile.getName());
+
}
Files.delete(file.toPath());
// Bedrock check and set
diff --git a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java
index 47c27bea9..dd5dd9bb0 100644
--- a/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java
+++ b/src/main/java/world/bentobox/bentobox/managers/PlayersManager.java
@@ -84,9 +84,11 @@ public class PlayersManager {
Objects.requireNonNull(playerUUID, "Player UUID must not be null");
// If the player exists in the database, load it; otherwise, create and save a new player
- Players player = loadPlayer(playerUUID);
- if (player != null) {
- return player;
+ if (handler.objectExists(playerUUID.toString())) {
+ Players p = loadPlayer(playerUUID);
+ if (p != null) {
+ return p;
+ }
}
Players newPlayer = new Players(plugin, playerUUID);
handler.saveObjectAsync(newPlayer);
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 f5edc3c8f..e6f91066d 100644
--- a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java
+++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java
@@ -38,7 +38,7 @@ public class IslandCache {
* Map of all islands with island uniqueId as key
*/
@NonNull
- private final Map<@NonNull String, @NonNull Island> islandsById;
+ private final Map<@NonNull String, Island> islandsById;
/**
* Every player who is associated with an island is in this map. Key is player
* UUID, value is a set of islands
diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml
index 7989f41ed..47c5f27bc 100644
--- a/src/main/resources/locales/en-US.yml
+++ b/src/main/resources/locales/en-US.yml
@@ -912,6 +912,10 @@ protection:
description: Toggle interaction
name: Beacons
hint: Beacon use disabled
+ BELL_RINGING:
+ description: Toggle interaction
+ name: Allow bell ringing
+ hint: Bell ringing disabled
BED:
description: Toggle interaction
name: Beds
@@ -960,6 +964,10 @@ protection:
description: Toggle button use
name: Buttons
hint: Button use disabled
+ CANDLES:
+ description: Toggle candle interaction
+ name: Candles
+ hint: Candle interaction disabled
CAKE:
description: Toggle cake interaction
name: Cakes
diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/CandleListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/CandleListenerTest.java
new file mode 100644
index 000000000..6d709b421
--- /dev/null
+++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/CandleListenerTest.java
@@ -0,0 +1,110 @@
+package world.bentobox.bentobox.listeners.flags.protection;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.Tag;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.event.Event.Result;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import world.bentobox.bentobox.BentoBox;
+import world.bentobox.bentobox.listeners.flags.AbstractCommonSetup;
+import world.bentobox.bentobox.lists.Flags;
+import world.bentobox.bentobox.util.Util;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ BentoBox.class, Flags.class, Util.class, Bukkit.class })
+public class CandleListenerTest extends AbstractCommonSetup {
+
+ private CandleListener l;
+ @Mock
+ private Block block;
+
+ /**
+ */
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // Island manager
+ // Default is that everything is allowed
+ when(island.isAllowed(any(), any())).thenReturn(true);
+
+ when(block.getLocation()).thenReturn(location);
+
+ // Tags
+ when(Tag.CANDLES.isTagged(any(Material.class))).thenReturn(true);
+ when(Tag.CANDLE_CAKES.isTagged(any(Material.class))).thenReturn(true);
+
+ // Listener
+ l = new CandleListener();
+ }
+
+ /**
+ * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.CandleListener#onCandleInteract(org.bukkit.event.player.PlayerInteractEvent)}.
+ */
+ @Test
+ public void testOnCandleInteract() {
+ // Block
+ when(block.getType()).thenReturn(Material.CANDLE);
+ PlayerInteractEvent e = new PlayerInteractEvent(mockPlayer, Action.LEFT_CLICK_BLOCK, null, block, BlockFace.UP);
+ l.onCandleInteract(e);
+ assertEquals(Result.ALLOW, e.useInteractedBlock());
+ }
+
+ /**
+ * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.CandleListener#onCandleInteract(org.bukkit.event.player.PlayerInteractEvent)}.
+ */
+ @Test
+ public void testOnCandleCakeInteract() {
+ // Block
+ when(block.getType()).thenReturn(Material.CANDLE_CAKE);
+ PlayerInteractEvent e = new PlayerInteractEvent(mockPlayer, Action.LEFT_CLICK_BLOCK, null, block, BlockFace.UP);
+ l.onCandleInteract(e);
+ assertEquals(Result.ALLOW, e.useInteractedBlock());
+ }
+
+ /**
+ * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.CandleListener#onCandleInteract(org.bukkit.event.player.PlayerInteractEvent)}.
+ */
+ @Test
+ public void testOnCandleInteractFail() {
+ when(island.isAllowed(any(), any())).thenReturn(false);
+ // Block
+ when(block.getType()).thenReturn(Material.CANDLE);
+ PlayerInteractEvent e = new PlayerInteractEvent(mockPlayer, Action.LEFT_CLICK_BLOCK, null, block, BlockFace.UP);
+ l.onCandleInteract(e);
+ assertEquals(Result.DENY, e.useInteractedBlock());
+ verify(notifier).notify(any(), eq("protection.protected"));
+ }
+
+ /**
+ * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.CandleListener#onCandleInteract(org.bukkit.event.player.PlayerInteractEvent)}.
+ */
+ @Test
+ public void testOnCandleCakeInteractFail() {
+ when(island.isAllowed(any(), any())).thenReturn(false);
+ // Block
+ when(block.getType()).thenReturn(Material.CANDLE_CAKE);
+ PlayerInteractEvent e = new PlayerInteractEvent(mockPlayer, Action.LEFT_CLICK_BLOCK, null, block, BlockFace.UP);
+ l.onCandleInteract(e);
+ assertEquals(Result.DENY, e.useInteractedBlock());
+ verify(notifier).notify(any(), eq("protection.protected"));
+ }
+
+}
diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListenerTest.java
index 0db36a177..883d07019 100644
--- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListenerTest.java
+++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PhysicalInteractionListenerTest.java
@@ -7,10 +7,13 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@@ -25,6 +28,7 @@ import org.bukkit.entity.Slime;
import org.bukkit.entity.Zombie;
import org.bukkit.event.Event.Result;
import org.bukkit.event.block.Action;
+import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityInteractEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
@@ -228,4 +232,61 @@ public class PhysicalInteractionListenerTest extends AbstractCommonSetup {
assertTrue(p.name() +" failed", e.isCancelled());
});
}
+
+ /**
+ * Test method for {@link PhysicalInteractionListener#onProjectileExplode(org.bukkit.event.entity.EntityExplodeEvent)}.
+ */
+ @Test
+ public void testOnProjectileExplodeNotProjectile() {
+ Entity entity = mock(Entity.class);
+ List blocks = new ArrayList<>();
+ EntityExplodeEvent e = new EntityExplodeEvent(entity, location, blocks, 0);
+ PhysicalInteractionListener i = new PhysicalInteractionListener();
+ i.onProjectileExplode(e);
+ assertFalse(e.isCancelled());
+ }
+
+ /**
+ * Test method for {@link PhysicalInteractionListener#onProjectileExplode(org.bukkit.event.entity.EntityExplodeEvent)}.
+ */
+ @Test
+ public void testOnProjectileExplodeProjectileNoPlayer() {
+ Projectile entity = mock(Projectile.class);
+ ProjectileSource source = mock(Creeper.class);
+ when(entity.getShooter()).thenReturn(source);
+ List blocks = new ArrayList<>();
+ EntityExplodeEvent e = new EntityExplodeEvent(entity, location, blocks, 0);
+ PhysicalInteractionListener i = new PhysicalInteractionListener();
+ i.onProjectileExplode(e);
+ assertFalse(e.isCancelled());
+ }
+
+ /**
+ * Test method for {@link PhysicalInteractionListener#onProjectileExplode(org.bukkit.event.entity.EntityExplodeEvent)}.
+ */
+ @Test
+ public void testOnProjectileExplodeProjectilePlayer() {
+ Projectile entity = mock(Projectile.class);
+ when(entity.getShooter()).thenReturn(mockPlayer);
+ List blocks = new ArrayList<>();
+ Block block1 = mock(Block.class);
+ Block block2 = mock(Block.class);
+ when(block1.getLocation()).thenReturn(location);
+ when(block2.getLocation()).thenReturn(location);
+ blocks.add(block1);
+ blocks.add(block2);
+
+ EntityExplodeEvent e = new EntityExplodeEvent(entity, location, blocks, 0);
+ PhysicalInteractionListener i = new PhysicalInteractionListener();
+
+ // Test with wooden button
+ when(block1.getType()).thenReturn(Material.OAK_BUTTON);
+ when(Tag.WOODEN_BUTTONS.isTagged(Material.OAK_BUTTON)).thenReturn(true);
+ // Test with pressure plate
+ when(block2.getType()).thenReturn(Material.STONE_PRESSURE_PLATE);
+ when(Tag.PRESSURE_PLATES.isTagged(Material.STONE_PRESSURE_PLATE)).thenReturn(true);
+
+ i.onProjectileExplode(e);
+ verify(notifier, times(2)).notify(any(), eq("protection.protected"));
+ }
}
diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/TNTListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/TNTListenerTest.java
index 1aff866eb..846ddc34c 100644
--- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/TNTListenerTest.java
+++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/TNTListenerTest.java
@@ -58,7 +58,7 @@ public class TNTListenerTest extends AbstractCommonSetup {
private Entity entity;
// Class under test
- private TNTListener listener;
+ private ExplosionListener listener;
@Override
@Before
@@ -85,7 +85,7 @@ public class TNTListenerTest extends AbstractCommonSetup {
// Util
when(Util.findFirstMatchingEnum(any(), anyString())).thenCallRealMethod();
- listener = new TNTListener();
+ listener = new ExplosionListener();
listener.setPlugin(plugin);
}
diff --git a/src/test/java/world/bentobox/bentobox/managers/FlagsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/FlagsManagerTest.java
index 2cc674d87..c9f53635d 100644
--- a/src/test/java/world/bentobox/bentobox/managers/FlagsManagerTest.java
+++ b/src/test/java/world/bentobox/bentobox/managers/FlagsManagerTest.java
@@ -46,7 +46,7 @@ public class FlagsManagerTest {
/**
* Update this value if the number of registered listeners changes
*/
- private static final int NUMBER_OF_LISTENERS = 54;
+ private static final int NUMBER_OF_LISTENERS = 55;
@Mock
private BentoBox plugin;
@Mock