From 3a1ec0a570a00d42bc8b4e927d3b683359e57565 Mon Sep 17 00:00:00 2001 From: BONNe Date: Tue, 6 Jul 2021 16:22:44 +0300 Subject: [PATCH 1/8] Implements better online player counter. (#1791) Current one is based on online player count when bStats sends data to the server. This one will send data about amount of players who logged in the server. --- .../java/world/bentobox/bentobox/BStats.java | 23 ++++++++++++++++++- .../bentobox/listeners/JoinLeaveListener.java | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/java/world/bentobox/bentobox/BStats.java b/src/main/java/world/bentobox/bentobox/BStats.java index dee4a6a86..869cc0fd2 100644 --- a/src/main/java/world/bentobox/bentobox/BStats.java +++ b/src/main/java/world/bentobox/bentobox/BStats.java @@ -1,7 +1,10 @@ package world.bentobox.bentobox; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import java.util.UUID; import org.bstats.bukkit.Metrics; import org.bstats.charts.AdvancedPie; @@ -29,6 +32,13 @@ public class BStats { */ private int islandsCreatedCount = 0; + /** + * Contains the amount of connected players since last data send. + * @since 1.17.1 + */ + private final Set connectedPlayerSet = new HashSet<>(); + + BStats(BentoBox plugin) { this.plugin = plugin; } @@ -86,6 +96,15 @@ public class BStats { islandsCreatedCount++; } + /** + * Adds given UUID to the connected player set. + * @param uuid UUID of a player who logins. + * @since 1.17.1 + */ + public void addPlayer(UUID uuid) { + this.connectedPlayerSet.add(uuid); + } + /** * Sends the enabled addons (except GameModeAddons) of this server. * @since 1.1 @@ -132,7 +151,9 @@ public class BStats { */ private void registerPlayersPerServerChart() { metrics.addCustomChart(new SimplePie("playersPerServer", () -> { - int players = Bukkit.getOnlinePlayers().size(); + int players = this.connectedPlayerSet.size(); + this.connectedPlayerSet.clear(); + if (players <= 0) return "0"; else if (players <= 10) return "1-10"; else if (players <= 30) return "11-30"; diff --git a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index 29882ce21..bac2aa6b1 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java @@ -102,6 +102,9 @@ public class JoinLeaveListener implements Listener { plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK); plugin.getIslands().getMaxHomes(i); }); + + // Add a player to the bStats cache. + plugin.getMetrics().ifPresent(bStats -> bStats.addPlayer(playerUUID)); } From 4139c5f2523075ad2e1f98492e584fdd1d66162f Mon Sep 17 00:00:00 2001 From: BONNe Date: Tue, 6 Jul 2021 16:23:09 +0300 Subject: [PATCH 2/8] Add Boxed Gamemode to the list. (#1793) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1806f8064..ea309b227 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Start now to create the server you've dreamed of! These are some popular Gamemodes: * [**AcidIsland**](https://github.com/BentoBoxWorld/AcidIsland): You are marooned in a sea of acid! * [**AOneBlock**](https://github.com/BentoBoxWorld/AOneBlock): Start to play with only 1 magical block. +* [**Boxed**](https://github.com/BentoBoxWorld/Boxed): A game mode where you are boxed into a tiny space that only expands by completing advancements. * [**BSkyBlock**](https://github.com/BentoBoxWorld/BSkyBlock): The successor to the popular ASkyBlock. * [**CaveBlock**](https://github.com/BentoBoxWorld/CaveBlock): Try to live underground! * [**SkyGrid**](https://github.com/BentoBoxWorld/SkyGrid): Survive in world made up of scattered blocks - what an adventure! From 89625a78b8d7c0473134909804e2be8f344999d1 Mon Sep 17 00:00:00 2001 From: BONNe Date: Tue, 6 Jul 2021 16:23:28 +0300 Subject: [PATCH 3/8] Add Bank addon (#1792) --- ADDON.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADDON.md b/ADDON.md index ae5386c67..01f9fc49c 100644 --- a/ADDON.md +++ b/ADDON.md @@ -1,4 +1,5 @@ The following is a list of all addons currently made for BentoBox: +* [**Bank**](https://github.com/BentoBoxWorld/Bank/): Provides an island bank to enable island members to share money. * [**Biomes**](https://github.com/BentoBoxWorld/Biomes/): Enables players to change biomes on islands. * [**Border**](https://github.com/BentoBoxWorld/Border/): Adds a world border around islands. * [**Cauldron Witchery**](https://github.com/BentoBoxWorld/CauldronWitchery/): Allows summoning mobs using some magic! From 7de6563ece2cf3ee14bcae9dbed80758a79ba6a1 Mon Sep 17 00:00:00 2001 From: Fredthedoggy <45927799+Fredthedoggy@users.noreply.github.com> Date: Tue, 6 Jul 2021 16:30:54 -0400 Subject: [PATCH 4/8] Add Holographic Displays as SoftDepend for AOneBlock (#1794) --- src/main/resources/plugin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a07ee8626..f080ab77f 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -24,6 +24,7 @@ softdepend: - LangUtils - WildStacker - LuckPerms + - HolographicDisplays permissions: bentobox.admin: From f88b8d4d6dc99495f434c090b1b144b416265483 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 6 Jul 2021 13:33:10 -0700 Subject: [PATCH 5/8] Custom date time format support for / info (#1783) Fixes #1720 --- .../bentobox/bentobox/database/objects/Island.java | 10 +++++++++- src/main/resources/locales/en-US.yml | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) 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 57554ada4..40927e278 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -1,6 +1,7 @@ package world.bentobox.bentobox.database.objects; import java.io.IOException; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.EnumMap; import java.util.HashMap; @@ -1075,7 +1076,14 @@ public class Island implements DataObject, MetaDataAble { // Fixes #getLastPlayed() returning 0 when it is the owner's first connection. long lastPlayed = (Bukkit.getServer().getOfflinePlayer(getOwner()).getLastPlayed() != 0) ? Bukkit.getServer().getOfflinePlayer(getOwner()).getLastPlayed() : Bukkit.getServer().getOfflinePlayer(getOwner()).getFirstPlayed(); - user.sendMessage("commands.admin.info.last-login","[date]", new Date(lastPlayed).toString()); + String formattedDate; + 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) { + 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(getWorld(), getOwner()))); String resets = String.valueOf(plugin.getPlayers().getResets(getWorld(), getOwner())); diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 10578d84e..ed207d26d 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -188,6 +188,7 @@ commands: island-uuid: "UUID: [uuid]" owner: "Owner: [owner] ([uuid])" last-login: "Last login: [date]" + last-login-date-time-format: "EEE MMM dd HH:mm:ss zzz yyyy" deaths: "Deaths: [number]" resets-left: "Resets: [number] (Max: [total])" team-members-title: "Team members:" From 0f7866a00b84ab8778b75916c53087fb5a3a1181 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 6 Jul 2021 13:41:23 -0700 Subject: [PATCH 6/8] Parent/sub-flag support, split up and designate CONTAINER flag as parent flag (#1784) * Split CONTAINER flag into multiple flags CONTAINER split into - CONTAINER (Chest/Minecart Chest) - BARREL (Barrel) - COMPOSTER (Composter) - FLOWER_POT (Flower Pot) - SHULKER_BOX (Shulker Box) - TRAPPED_CHEST (Trapped Chest) Fixes #1777 * Add subflag support * Create container parent flag, chest subflag * Remove extra string from when CHEST was CONTAINER * Fix incorrect flag specified on fired event in IslandToggleClick * Add missing world subflag event firing * Remove extra import --- .../bentobox/bentobox/api/flags/Flag.java | 50 +++++++++++++++++++ .../api/flags/clicklisteners/CycleClick.java | 12 +++++ .../clicklisteners/IslandToggleClick.java | 5 ++ .../clicklisteners/WorldToggleClick.java | 6 +++ .../bentobox/database/objects/Island.java | 50 +++++++++++++++++-- .../protection/BlockInteractionListener.java | 14 ++++-- .../flags/protection/InventoryListener.java | 28 +++++++++++ .../world/bentobox/bentobox/lists/Flags.java | 12 ++++- src/main/resources/locales/en-US.yml | 42 +++++++++++++--- .../BlockInteractionListenerTest.java | 14 +++--- 10 files changed, 213 insertions(+), 20 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/flags/Flag.java b/src/main/java/world/bentobox/bentobox/api/flags/Flag.java index c56474630..754cdf325 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/Flag.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/Flag.java @@ -1,5 +1,6 @@ package world.bentobox.bentobox.api.flags; +import java.util.Arrays; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -131,6 +132,7 @@ public class Flag implements Comparable { private final Addon addon; private final int cooldown; private final Mode mode; + private final Set subflags; private Flag(Builder builder) { this.id = builder.id; @@ -147,6 +149,7 @@ public class Flag implements Comparable { this.cooldown = builder.cooldown; this.addon = builder.addon; this.mode = builder.mode; + this.subflags = builder.subflags; } public String getID() { @@ -200,6 +203,18 @@ public class Flag implements Comparable { .getWorldSettings(world) .getWorldFlags() .put(getID(), setting); + + // Subflag support + if (hasSubflags()) { + subflags.stream() + .filter(subflag -> subflag.getType().equals(Type.WORLD_SETTING) || subflag.getType().equals(Type.PROTECTION)) + .forEach(subflag -> BentoBox.getInstance() + .getIWM() + .getWorldSettings(world) + .getWorldFlags() + .put(subflag.getID(), setting)); + } + // Save config file BentoBox.getInstance().getIWM().getAddon(world).ifPresent(GameModeAddon::saveWorldSettings); } @@ -208,6 +223,7 @@ public class Flag implements Comparable { /** * Set the original status of this flag for locations outside of island spaces. * May be overriden by the the setting for this world. + * Does not affect subflags. * @param defaultSetting - true means it is allowed. false means it is not allowed */ public void setDefaultSetting(boolean defaultSetting) { @@ -217,6 +233,7 @@ public class Flag implements Comparable { /** * Set the status of this flag for locations outside of island spaces for a specific world. * World must exist and be registered before this method can be called. + * Does not affect subflags. * @param defaultSetting - true means it is allowed. false means it is not allowed */ public void setDefaultSetting(World world, boolean defaultSetting) { @@ -435,6 +452,22 @@ public class Flag implements Comparable { return mode; } + /** + * @return whether the flag has subflags (and therefore is a parent flag) + * @since 1.17.0 + */ + public boolean hasSubflags() { + return !subflags.isEmpty(); + } + + /** + * @return the subflags, an empty Set if none + * @since 1.17.0 + */ + public Set getSubflags() { + return subflags; + } + @Override public String toString() { return "Flag [id=" + id + "]"; @@ -480,6 +513,9 @@ public class Flag implements Comparable { // Mode private Mode mode = Mode.EXPERT; + // Subflags + private Set subflags; + /** * Builder for making flags * @param id - a unique id that MUST be the same as the enum of the flag @@ -488,6 +524,7 @@ public class Flag implements Comparable { public Builder(String id, Material icon) { this.id = id; this.icon = icon; + this.subflags = new HashSet<>(); } /** @@ -595,6 +632,19 @@ public class Flag implements Comparable { return this; } + /** + * Add subflags and designate this flag as a parent flag. + * Subflags have their state simultaneously changed with the parent flag. + * Take extra care to ensure that subflags have the same number of possible values as the parent flag. + * @param flags all Flags that are subflags + * @return Builder - flag builder + * @since 1.17.0 + */ + public Builder subflags(Flag... flags) { + this.subflags.addAll(Arrays.asList(flags)); + return this; + } + /** * Build the flag * @return Flag diff --git a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClick.java b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClick.java index 401e760de..922337133 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClick.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClick.java @@ -90,6 +90,12 @@ public class CycleClick implements PanelItem.ClickHandler { user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_OFF, 1F, 1F); // Fire event Bukkit.getPluginManager().callEvent(new FlagProtectionChangeEvent(island, user.getUniqueId(), flag, island.getFlag(flag))); + + // Subflag support + if (flag.hasSubflags()) { + // Fire events for all subflags as well + flag.getSubflags().forEach(subflag -> Bukkit.getPluginManager().callEvent(new FlagProtectionChangeEvent(island, user.getUniqueId(), subflag, island.getFlag(subflag)))); + } } else if (click.equals(ClickType.RIGHT)) { if (currentRank <= minRank) { island.setFlag(flag, maxRank); @@ -99,6 +105,12 @@ public class CycleClick implements PanelItem.ClickHandler { user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F); // Fire event Bukkit.getPluginManager().callEvent(new FlagProtectionChangeEvent(island, user.getUniqueId(), flag, island.getFlag(flag))); + + // Subflag support + if (flag.hasSubflags()) { + // Fire events for all subflags as well + flag.getSubflags().forEach(subflag -> Bukkit.getPluginManager().callEvent(new FlagProtectionChangeEvent(island, user.getUniqueId(), subflag, island.getFlag(subflag)))); + } } else if (click.equals(ClickType.SHIFT_LEFT) && user.isOp()) { if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID())) { plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID()); diff --git a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/IslandToggleClick.java b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/IslandToggleClick.java index 08dbba227..2afcd6b96 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/IslandToggleClick.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/IslandToggleClick.java @@ -77,6 +77,11 @@ public class IslandToggleClick implements ClickHandler { island.setCooldown(flag); // Fire event Bukkit.getPluginManager().callEvent(new FlagSettingChangeEvent(island, user.getUniqueId(), flag, island.isAllowed(flag))); + + if (flag.hasSubflags()) { + // Fire events for all subflags as well + flag.getSubflags().forEach(subflag -> Bukkit.getPluginManager().callEvent(new FlagSettingChangeEvent(island, user.getUniqueId(), subflag, island.isAllowed(subflag)))); + } } }); } else { diff --git a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/WorldToggleClick.java b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/WorldToggleClick.java index e33bbf623..cb8c0a0e1 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/WorldToggleClick.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/WorldToggleClick.java @@ -61,6 +61,12 @@ public class WorldToggleClick implements ClickHandler { user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F); // Fire event Bukkit.getPluginManager().callEvent(new FlagWorldSettingChangeEvent(user.getWorld(), user.getUniqueId(), flag, flag.isSetForWorld(user.getWorld()))); + + // Subflag support + if (flag.hasSubflags()) { + // Fire events for all subflags as well + flag.getSubflags().forEach(subflag -> Bukkit.getPluginManager().callEvent(new FlagWorldSettingChangeEvent(user.getWorld(), user.getUniqueId(), subflag, subflag.isSetForWorld(user.getWorld())))); + } } // Save world settings 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 40927e278..a2273e16f 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -824,11 +824,28 @@ public class Island implements DataObject, MetaDataAble { /** * Set the Island Guard flag rank + * This method affects subflags (if the given flag is a parent flag) * @param flag - flag * @param value - Use RanksManager settings, e.g. RanksManager.MEMBER */ - public void setFlag(Flag flag, int value){ + public void setFlag(Flag flag, int value) { + setFlag(flag, value, true); + } + + /** + * Set the Island Guard flag rank + * Also specify whether subflags are affected by this method call + * @param flag - flag + * @param value - Use RanksManager settings, e.g. RanksManager.MEMBER + * @param doSubflags - whether to set subflags + */ + public void setFlag(Flag flag, int value, boolean doSubflags) { flags.put(flag, value); + // Subflag support + if (doSubflags && flag.hasSubflags()) { + // Ensure that a subflag isn't a subflag of itself or else we're in trouble! + flag.getSubflags().forEach(subflag -> setFlag(subflag, value, true)); + } setChanged(); } @@ -1132,23 +1149,50 @@ public class Island implements DataObject, MetaDataAble { /** * Toggles a settings flag + * This method affects subflags (if the given flag is a parent flag) * @param flag - flag */ public void toggleFlag(Flag flag) { + toggleFlag(flag, true); + } + + /** + * Toggles a settings flag + * Also specify whether subflags are affected by this method call + * @param flag - flag + */ + public void toggleFlag(Flag flag, boolean doSubflags) { + boolean newToggleValue = !isAllowed(flag); // Use for subflags if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) { - setSettingsFlag(flag, !isAllowed(flag)); + setSettingsFlag(flag, newToggleValue, doSubflags); } setChanged(); } /** * Sets the state of a settings flag + * This method affects subflags (if the given flag is a parent flag) * @param flag - flag * @param state - true or false */ public void setSettingsFlag(Flag flag, boolean state) { + setSettingsFlag(flag, state, true); + } + + /** + * Sets the state of a settings flag + * Also specify whether subflags are affected by this method call + * @param flag - flag + * @param state - true or false + */ + public void setSettingsFlag(Flag flag, boolean state, boolean doSubflags) { + int newState = state ? 1 : -1; if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) { - flags.put(flag, state ? 1 : -1); + flags.put(flag, newState); + if (doSubflags && flag.hasSubflags()) { + // If we have circular subflags or a flag is a subflag of itself we are in trouble! + flag.getSubflags().forEach(subflag -> setSettingsFlag(subflag, state, true)); + } } setChanged(); } 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 1fbd2e2af..faf4408ad 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 @@ -95,7 +95,7 @@ public class BlockInteractionListener extends FlagListener { private void checkClickedBlock(Event e, Player player, Location loc, Material type) { // Handle pots if (type.name().startsWith("POTTED")) { - checkIsland(e, player, loc, Flags.CONTAINER); + checkIsland(e, player, loc, Flags.FLOWER_POT); return; } if (Tag.ANVIL.isTagged(type)) { @@ -115,7 +115,7 @@ public class BlockInteractionListener extends FlagListener { return; } if (Tag.SHULKER_BOXES.isTagged(type)) { - checkIsland(e, player, loc, Flags.CONTAINER); + checkIsland(e, player, loc, Flags.SHULKER_BOX); return; } if (Tag.TRAPDOORS.isTagged(type)) { @@ -136,12 +136,20 @@ public class BlockInteractionListener extends FlagListener { checkIsland(e, player, loc, Flags.HIVE); break; case BARREL: + checkIsland(e, player, loc, Flags.BARREL); + break; case CHEST: case CHEST_MINECART: + checkIsland(e, player, loc, Flags.CHEST); + break; case TRAPPED_CHEST: + checkIsland(e, player, loc, Flags.TRAPPED_CHEST); + break; case FLOWER_POT: + checkIsland(e, player, loc, Flags.FLOWER_POT); + break; case COMPOSTER: - checkIsland(e, player, loc, Flags.CONTAINER); + checkIsland(e, player, loc, Flags.COMPOSTER); break; case DISPENSER: checkIsland(e, player, loc, Flags.DISPENSER); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListener.java index 2cd31f7a2..ef7e103c2 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/InventoryListener.java @@ -1,15 +1,20 @@ package world.bentobox.bentobox.listeners.flags.protection; +import org.bukkit.Material; +import org.bukkit.block.Barrel; import org.bukkit.block.Beacon; import org.bukkit.block.BrewingStand; +import org.bukkit.block.Chest; import org.bukkit.block.Dispenser; import org.bukkit.block.Dropper; import org.bukkit.block.Furnace; import org.bukkit.block.Hopper; +import org.bukkit.block.ShulkerBox; import org.bukkit.entity.Animals; import org.bukkit.entity.NPC; import org.bukkit.entity.Player; import org.bukkit.entity.minecart.HopperMinecart; +import org.bukkit.entity.minecart.StorageMinecart; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.inventory.InventoryClickEvent; @@ -61,6 +66,29 @@ public class InventoryListener extends FlagListener { else if (inventoryHolder instanceof NPC) { checkIsland(e, player, e.getInventory().getLocation(), Flags.TRADING); } + else if (inventoryHolder instanceof Barrel) { + checkIsland(e, player, e.getInventory().getLocation(), Flags.BARREL); + } + else if (inventoryHolder instanceof ShulkerBox) { + checkIsland(e, player, e.getInventory().getLocation(), Flags.SHULKER_BOX); + } + else if (inventoryHolder instanceof Chest) { + // To differentiate between a Chest and a Trapped Chest we need to get the Block corresponding to the inventory + Chest chestInventoryHolder = (Chest) inventoryHolder; + try { + if (chestInventoryHolder.getBlock().getType() == Material.TRAPPED_CHEST) { + checkIsland(e, player, e.getInventory().getLocation(), Flags.TRAPPED_CHEST); + } else { + checkIsland(e, player, e.getInventory().getLocation(), Flags.CHEST); + } + } catch (IllegalStateException ignored) { + // Thrown if the Chest corresponds to a block that isn't placed (how did we get here?) + checkIsland(e, player, e.getInventory().getLocation(), Flags.CHEST); + } + } + else if (inventoryHolder instanceof StorageMinecart) { + checkIsland(e, player, e.getInventory().getLocation(), Flags.CHEST); + } else if (!(inventoryHolder instanceof Player)) { // All other containers checkIsland(e, player, e.getInventory().getLocation(), Flags.CONTAINER); diff --git a/src/main/java/world/bentobox/bentobox/lists/Flags.java b/src/main/java/world/bentobox/bentobox/lists/Flags.java index a9d07fbce..32fce07e7 100644 --- a/src/main/java/world/bentobox/bentobox/lists/Flags.java +++ b/src/main/java/world/bentobox/bentobox/lists/Flags.java @@ -110,7 +110,14 @@ public final class Flags { public static final Flag BEACON = new Flag.Builder("BEACON", Material.BEACON).build(); public static final Flag BED = new Flag.Builder("BED", Material.RED_BED).build(); public static final Flag BREWING = new Flag.Builder("BREWING", Material.BREWING_STAND).mode(Flag.Mode.ADVANCED).build(); - public static final Flag CONTAINER = new Flag.Builder("CONTAINER", Material.CHEST).mode(Flag.Mode.BASIC).build(); + // START CONTAINER split + public static final Flag CHEST = new Flag.Builder("CHEST", Material.CHEST).mode(Flag.Mode.ADVANCED).build(); + public static final Flag BARREL = new Flag.Builder("BARREL", Material.BARREL).mode(Flag.Mode.ADVANCED).build(); + public static final Flag COMPOSTER = new Flag.Builder("COMPOSTER", Material.COMPOSTER).mode(Flag.Mode.ADVANCED).build(); + public static final Flag FLOWER_POT = new Flag.Builder("FLOWER_POT", Material.FLOWER_POT).mode(Flag.Mode.ADVANCED).build(); + public static final Flag SHULKER_BOX = new Flag.Builder("SHULKER_BOX", Material.SHULKER_BOX).mode(Flag.Mode.ADVANCED).build(); + public static final Flag TRAPPED_CHEST = new Flag.Builder("TRAPPED_CHEST", Material.TRAPPED_CHEST).mode(Flag.Mode.ADVANCED).build(); + // END CONTAINER split public static final Flag DISPENSER = new Flag.Builder("DISPENSER", Material.DISPENSER).mode(Flag.Mode.ADVANCED).build(); public static final Flag DROPPER = new Flag.Builder("DROPPER", Material.DROPPER).mode(Flag.Mode.ADVANCED).build(); public static final Flag HOPPER = new Flag.Builder("HOPPER", Material.HOPPER).mode(Flag.Mode.ADVANCED).build(); @@ -129,6 +136,9 @@ public final class Flags { public static final Flag ITEM_FRAME = new Flag.Builder("ITEM_FRAME", Material.ITEM_FRAME).mode(Flag.Mode.ADVANCED).build(); public static final Flag CAKE = new Flag.Builder("CAKE", Material.CAKE).build(); public static final Flag HIVE = new Flag.Builder("HIVE", Material.HONEY_BOTTLE).type(Type.PROTECTION).build(); + public static final Flag CONTAINER = new Flag.Builder("CONTAINER", Material.CHEST).mode(Flag.Mode.BASIC) + .subflags(BREWING, BARREL, CHEST, COMPOSTER, FLOWER_POT, SHULKER_BOX, TRAPPED_CHEST, FURNACE, JUKEBOX, DISPENSER, DROPPER, HOPPER, ITEM_FRAME, HIVE) + .build(); /** * Prevents players from interacting with the Dragon Egg. diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index ed207d26d..6d9d7cc50 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -788,15 +788,45 @@ protection: name: "Cakes" hint: "Cake eating disabled" CONTAINER: - name: "Containers" + name: "All containers" description: |- - &a Toggle interaction with chests, - &a shulker boxes and flower pots, - &a composters and barrels. + &a Toggle interaction with all containers. + &a Includes: Barrel, bee hive, brewing stand, + &a chest, composter, dispenser, dropper, + &a flower pot, furnace, hopper, item frame, + &a jukebox, minecart chest, shulker box, + &a trapped chest. - &7 Other containers are handled - &7 by dedicated flags. + &7 Changing individual settings overrides + &7 this flag. hint: "Container access disabled" + CHEST: + name: "Chests and minecart chests" + description: |- + &a Toggle interaction with chests + &a and chest minecarts. + &a (does not include trapped chests) + hint: "Chest access disabled" + BARREL: + name: "Barrels" + description: "Toggle barrel interaction" + hint: "Barrel access disabled" + COMPOSTER: + name: "Composters" + description: "Toggle composter interaction" + hint: "Composter interaction disabled" + FLOWER_POT: + name: "Flower pots" + description: "Toggle flower pot interaction" + hint: "Flower pot interaction disabled" + SHULKER_BOX: + name: "Shulker boxes" + description: "Toggle shulker box interaction" + hint: "Shulker box access disabled" + TRAPPED_CHEST: + name: "Trapped chests" + description: "Toggle trapped chest interaction" + hint: "Trapped chest access disabled" DISPENSER: name: "Dispensers" description: "Toggle dispenser interaction" diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListenerTest.java index c86dae48a..ad3cac559 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListenerTest.java @@ -70,14 +70,14 @@ public class BlockInteractionListenerTest extends AbstractCommonSetup { when(Tag.BEDS.isTagged(Material.WHITE_BED)).thenReturn(true); clickedBlocks.put(Material.BREWING_STAND, Flags.BREWING); clickedBlocks.put(Material.CAULDRON, Flags.BREWING); - clickedBlocks.put(Material.BARREL, Flags.CONTAINER); - clickedBlocks.put(Material.CHEST, Flags.CONTAINER); - clickedBlocks.put(Material.CHEST_MINECART, Flags.CONTAINER); - clickedBlocks.put(Material.TRAPPED_CHEST, Flags.CONTAINER); - clickedBlocks.put(Material.SHULKER_BOX, Flags.CONTAINER); + clickedBlocks.put(Material.BARREL, Flags.BARREL); + clickedBlocks.put(Material.CHEST, Flags.CHEST); + clickedBlocks.put(Material.CHEST_MINECART, Flags.CHEST); + clickedBlocks.put(Material.TRAPPED_CHEST, Flags.TRAPPED_CHEST); + clickedBlocks.put(Material.SHULKER_BOX, Flags.SHULKER_BOX); when(Tag.SHULKER_BOXES.isTagged(Material.SHULKER_BOX)).thenReturn(true); - clickedBlocks.put(Material.FLOWER_POT, Flags.CONTAINER); - clickedBlocks.put(Material.COMPOSTER, Flags.CONTAINER); + clickedBlocks.put(Material.FLOWER_POT, Flags.FLOWER_POT); + clickedBlocks.put(Material.COMPOSTER, Flags.COMPOSTER); clickedBlocks.put(Material.DISPENSER, Flags.DISPENSER); clickedBlocks.put(Material.DROPPER, Flags.DROPPER); clickedBlocks.put(Material.HOPPER, Flags.HOPPER); From 759ba522f413e834c50834a527f64737a94092c2 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 6 Jul 2021 13:47:35 -0700 Subject: [PATCH 7/8] Add world setting flag for island visitors keep inventory (#1785) --- .../VisitorKeepInventoryListener.java | 39 +++++++++++++++++++ .../world/bentobox/bentobox/lists/Flags.java | 8 ++++ src/main/resources/locales/en-US.yml | 9 +++++ 3 files changed, 56 insertions(+) create mode 100644 src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/VisitorKeepInventoryListener.java diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/VisitorKeepInventoryListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/VisitorKeepInventoryListener.java new file mode 100644 index 000000000..e6ef8b142 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/VisitorKeepInventoryListener.java @@ -0,0 +1,39 @@ +package world.bentobox.bentobox.listeners.flags.worldsettings; + +import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.PlayerDeathEvent; +import world.bentobox.bentobox.api.flags.FlagListener; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.lists.Flags; +import world.bentobox.bentobox.util.Util; + +import java.util.Optional; + +/** + * Prevents visitors from losing their items if they + * die on an island in which they are a visitor. + * Handles {@link world.bentobox.bentobox.lists.Flags#VISITOR_KEEP_INVENTORY}. + * @author jstnf + * @since 1.17.0 + */ +public class VisitorKeepInventoryListener extends FlagListener { + + @EventHandler (priority = EventPriority.LOW, ignoreCancelled = true) + public void onVisitorDeath(PlayerDeathEvent e) { + World world = Util.getWorld(e.getEntity().getWorld()); + if (!getIWM().inWorld(world) || !Flags.VISITOR_KEEP_INVENTORY.isSetForWorld(world)) { + // If the player dies outside of the island world, don't do anything + return; + } + + Optional island = getIslands().getProtectedIslandAt(e.getEntity().getLocation()); + if (island.isPresent() && !island.get().getMemberSet().contains(e.getEntity().getUniqueId())) { + e.setKeepInventory(true); + e.setKeepLevel(true); + e.getDrops().clear(); + e.setDroppedExp(0); + } + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/lists/Flags.java b/src/main/java/world/bentobox/bentobox/lists/Flags.java index 32fce07e7..057082afe 100644 --- a/src/main/java/world/bentobox/bentobox/lists/Flags.java +++ b/src/main/java/world/bentobox/bentobox/lists/Flags.java @@ -62,6 +62,7 @@ import world.bentobox.bentobox.listeners.flags.worldsettings.PistonPushListener; import world.bentobox.bentobox.listeners.flags.worldsettings.RemoveMobsListener; import world.bentobox.bentobox.listeners.flags.worldsettings.SpawnerSpawnEggsListener; import world.bentobox.bentobox.listeners.flags.worldsettings.TreesGrowingOutsideRangeListener; +import world.bentobox.bentobox.listeners.flags.worldsettings.VisitorKeepInventoryListener; import world.bentobox.bentobox.listeners.flags.worldsettings.WitherListener; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; @@ -536,6 +537,13 @@ public final class Flags { */ public static final Flag PETS_STAY_AT_HOME = new Flag.Builder("PETS_STAY_AT_HOME", Material.TROPICAL_FISH).listener(new PetTeleportListener()).type(Type.WORLD_SETTING).defaultSetting(true).build(); + /** + * Toggles whether island visitors keep their items if they die on another player's island. + * @since 1.17.0 + * @see VisitorKeepInventoryListener + */ + public static final Flag VISITOR_KEEP_INVENTORY = new Flag.Builder("VISITOR_KEEP_INVENTORY", Material.TOTEM_OF_UNDYING).listener(new VisitorKeepInventoryListener()).type(Type.WORLD_SETTING).defaultSetting(false).build(); + /** * Provides a list of all the Flag instances contained in this class using reflection. * Deprecated Flags are ignored. diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 6d9d7cc50..9dc3d7cee 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -1302,6 +1302,15 @@ protection: &a back to their island using commands &a if they are falling. hint: "&c You cannot do that while falling." + VISITOR_KEEP_INVENTORY: + name: "Visitors keep inventory on death" + description: |- + &a Prevent players from losing their + &a items and experience if they die on + &a an island in which they are a visitor. + &a + &a Island members still lose their items + &a if they die on their own island! WITHER_DAMAGE: name: "Toggle wither damage" description: |- From 69f7b4946966fc84c4d4be2e65ab47418446e72c Mon Sep 17 00:00:00 2001 From: BONNe Date: Tue, 6 Jul 2021 23:50:58 +0300 Subject: [PATCH 8/8] Implement 3 bar charts: addons, gamemodes, hooks (#1790) BStats supports sending Bar chart data, however, it does not display it via their site directly. It can be called manually, to view. PieChart does not work very well for addons and hooks. BarChart however allows viewing each addon separately. This change allows sending data to the server about bar charts. --- .../java/world/bentobox/bentobox/BStats.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/main/java/world/bentobox/bentobox/BStats.java b/src/main/java/world/bentobox/bentobox/BStats.java index 869cc0fd2..aedf7c83a 100644 --- a/src/main/java/world/bentobox/bentobox/BStats.java +++ b/src/main/java/world/bentobox/bentobox/BStats.java @@ -8,6 +8,7 @@ import java.util.UUID; import org.bstats.bukkit.Metrics; import org.bstats.charts.AdvancedPie; +import org.bstats.charts.SimpleBarChart; import org.bstats.charts.SimplePie; import org.bstats.charts.SingleLineChart; import org.bukkit.Bukkit; @@ -63,6 +64,11 @@ public class BStats { // Single Line charts registerIslandsCountChart(); registerIslandsCreatedChart(); + + // Bar Charts + registerAddonsBarChart(); + registerGameModeAddonsBarChart(); + registerHooksBarChart(); } private void registerDefaultLanguageChart() { @@ -185,4 +191,44 @@ public class BStats { return values; })); } + + /** + * Sends the enabled addons (except GameModeAddons) of this server as bar chart. + * @since 1.17.1 + */ + private void registerAddonsBarChart() { + metrics.addCustomChart(new SimpleBarChart("addonsBar", () -> { + Map values = new HashMap<>(); + plugin.getAddonsManager().getEnabledAddons().stream() + .filter(addon -> !(addon instanceof GameModeAddon) && addon.getDescription().isMetrics()) + .forEach(addon -> values.put(addon.getDescription().getName(), 1)); + return values; + })); + } + + /** + * Sends the enabled GameModeAddons of this server as a bar chart. + * @since 1.17.1 + */ + private void registerGameModeAddonsBarChart() { + metrics.addCustomChart(new SimpleBarChart("gameModeAddonsBar", () -> { + Map values = new HashMap<>(); + plugin.getAddonsManager().getGameModeAddons().stream() + .filter(gameModeAddon -> gameModeAddon.getDescription().isMetrics()) + .forEach(gameModeAddon -> values.put(gameModeAddon.getDescription().getName(), 1)); + return values; + })); + } + + /** + * Sends the enabled Hooks of this server as a bar chart. + * @since 1.17.1 + */ + private void registerHooksBarChart() { + metrics.addCustomChart(new SimpleBarChart("hooksBar", () -> { + Map values = new HashMap<>(); + plugin.getHooks().getHooks().forEach(hook -> values.put(hook.getPluginName(), 1)); + return values; + })); + } }