Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop

This commit is contained in:
tastybento 2021-07-06 13:54:54 -07:00
commit ed0084f2c4
16 changed files with 353 additions and 22 deletions

View File

@ -1,4 +1,5 @@
The following is a list of all addons currently made for BentoBox: 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. * [**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. * [**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! * [**Cauldron Witchery**](https://github.com/BentoBoxWorld/CauldronWitchery/): Allows summoning mobs using some magic!

View File

@ -25,6 +25,7 @@ Start now to create the server you've dreamed of!
These are some popular Gamemodes: These are some popular Gamemodes:
* [**AcidIsland**](https://github.com/BentoBoxWorld/AcidIsland): You are marooned in a sea of acid! * [**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. * [**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. * [**BSkyBlock**](https://github.com/BentoBoxWorld/BSkyBlock): The successor to the popular ASkyBlock.
* [**CaveBlock**](https://github.com/BentoBoxWorld/CaveBlock): Try to live underground! * [**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! * [**SkyGrid**](https://github.com/BentoBoxWorld/SkyGrid): Survive in world made up of scattered blocks - what an adventure!

View File

@ -1,10 +1,14 @@
package world.bentobox.bentobox; package world.bentobox.bentobox;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bstats.bukkit.Metrics; import org.bstats.bukkit.Metrics;
import org.bstats.charts.AdvancedPie; import org.bstats.charts.AdvancedPie;
import org.bstats.charts.SimpleBarChart;
import org.bstats.charts.SimplePie; import org.bstats.charts.SimplePie;
import org.bstats.charts.SingleLineChart; import org.bstats.charts.SingleLineChart;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -29,6 +33,13 @@ public class BStats {
*/ */
private int islandsCreatedCount = 0; private int islandsCreatedCount = 0;
/**
* Contains the amount of connected players since last data send.
* @since 1.17.1
*/
private final Set<UUID> connectedPlayerSet = new HashSet<>();
BStats(BentoBox plugin) { BStats(BentoBox plugin) {
this.plugin = plugin; this.plugin = plugin;
} }
@ -53,6 +64,11 @@ public class BStats {
// Single Line charts // Single Line charts
registerIslandsCountChart(); registerIslandsCountChart();
registerIslandsCreatedChart(); registerIslandsCreatedChart();
// Bar Charts
registerAddonsBarChart();
registerGameModeAddonsBarChart();
registerHooksBarChart();
} }
private void registerDefaultLanguageChart() { private void registerDefaultLanguageChart() {
@ -86,6 +102,15 @@ public class BStats {
islandsCreatedCount++; 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. * Sends the enabled addons (except GameModeAddons) of this server.
* @since 1.1 * @since 1.1
@ -132,7 +157,9 @@ public class BStats {
*/ */
private void registerPlayersPerServerChart() { private void registerPlayersPerServerChart() {
metrics.addCustomChart(new SimplePie("playersPerServer", () -> { metrics.addCustomChart(new SimplePie("playersPerServer", () -> {
int players = Bukkit.getOnlinePlayers().size(); int players = this.connectedPlayerSet.size();
this.connectedPlayerSet.clear();
if (players <= 0) return "0"; if (players <= 0) return "0";
else if (players <= 10) return "1-10"; else if (players <= 10) return "1-10";
else if (players <= 30) return "11-30"; else if (players <= 30) return "11-30";
@ -164,4 +191,44 @@ public class BStats {
return values; 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<String, Integer> 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<String, Integer> 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<String, Integer> values = new HashMap<>();
plugin.getHooks().getHooks().forEach(hook -> values.put(hook.getPluginName(), 1));
return values;
}));
}
} }

View File

@ -1,5 +1,6 @@
package world.bentobox.bentobox.api.flags; package world.bentobox.bentobox.api.flags;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -131,6 +132,7 @@ public class Flag implements Comparable<Flag> {
private final Addon addon; private final Addon addon;
private final int cooldown; private final int cooldown;
private final Mode mode; private final Mode mode;
private final Set<Flag> subflags;
private Flag(Builder builder) { private Flag(Builder builder) {
this.id = builder.id; this.id = builder.id;
@ -147,6 +149,7 @@ public class Flag implements Comparable<Flag> {
this.cooldown = builder.cooldown; this.cooldown = builder.cooldown;
this.addon = builder.addon; this.addon = builder.addon;
this.mode = builder.mode; this.mode = builder.mode;
this.subflags = builder.subflags;
} }
public String getID() { public String getID() {
@ -200,6 +203,18 @@ public class Flag implements Comparable<Flag> {
.getWorldSettings(world) .getWorldSettings(world)
.getWorldFlags() .getWorldFlags()
.put(getID(), setting); .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 // Save config file
BentoBox.getInstance().getIWM().getAddon(world).ifPresent(GameModeAddon::saveWorldSettings); BentoBox.getInstance().getIWM().getAddon(world).ifPresent(GameModeAddon::saveWorldSettings);
} }
@ -208,6 +223,7 @@ public class Flag implements Comparable<Flag> {
/** /**
* Set the original status of this flag for locations outside of island spaces. * Set the original status of this flag for locations outside of island spaces.
* May be overriden by the the setting for this world. * 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 * @param defaultSetting - true means it is allowed. false means it is not allowed
*/ */
public void setDefaultSetting(boolean defaultSetting) { public void setDefaultSetting(boolean defaultSetting) {
@ -217,6 +233,7 @@ public class Flag implements Comparable<Flag> {
/** /**
* Set the status of this flag for locations outside of island spaces for a specific world. * 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. * 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 * @param defaultSetting - true means it is allowed. false means it is not allowed
*/ */
public void setDefaultSetting(World world, boolean defaultSetting) { public void setDefaultSetting(World world, boolean defaultSetting) {
@ -435,6 +452,22 @@ public class Flag implements Comparable<Flag> {
return mode; 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<Flag> getSubflags() {
return subflags;
}
@Override @Override
public String toString() { public String toString() {
return "Flag [id=" + id + "]"; return "Flag [id=" + id + "]";
@ -480,6 +513,9 @@ public class Flag implements Comparable<Flag> {
// Mode // Mode
private Mode mode = Mode.EXPERT; private Mode mode = Mode.EXPERT;
// Subflags
private Set<Flag> subflags;
/** /**
* Builder for making flags * Builder for making flags
* @param id - a unique id that MUST be the same as the enum of the flag * @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<Flag> {
public Builder(String id, Material icon) { public Builder(String id, Material icon) {
this.id = id; this.id = id;
this.icon = icon; this.icon = icon;
this.subflags = new HashSet<>();
} }
/** /**
@ -595,6 +632,19 @@ public class Flag implements Comparable<Flag> {
return this; 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 * Build the flag
* @return Flag * @return Flag

View File

@ -90,6 +90,12 @@ public class CycleClick implements PanelItem.ClickHandler {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_OFF, 1F, 1F); user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_OFF, 1F, 1F);
// Fire event // Fire event
Bukkit.getPluginManager().callEvent(new FlagProtectionChangeEvent(island, user.getUniqueId(), flag, island.getFlag(flag))); 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)) { } else if (click.equals(ClickType.RIGHT)) {
if (currentRank <= minRank) { if (currentRank <= minRank) {
island.setFlag(flag, maxRank); 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); user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
// Fire event // Fire event
Bukkit.getPluginManager().callEvent(new FlagProtectionChangeEvent(island, user.getUniqueId(), flag, island.getFlag(flag))); 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()) { } else if (click.equals(ClickType.SHIFT_LEFT) && user.isOp()) {
if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID())) { if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID())) {
plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID()); plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID());

View File

@ -77,6 +77,11 @@ public class IslandToggleClick implements ClickHandler {
island.setCooldown(flag); island.setCooldown(flag);
// Fire event // Fire event
Bukkit.getPluginManager().callEvent(new FlagSettingChangeEvent(island, user.getUniqueId(), flag, island.isAllowed(flag))); 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 { } else {

View File

@ -61,6 +61,12 @@ public class WorldToggleClick implements ClickHandler {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F); user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
// Fire event // Fire event
Bukkit.getPluginManager().callEvent(new FlagWorldSettingChangeEvent(user.getWorld(), user.getUniqueId(), flag, flag.isSetForWorld(user.getWorld()))); 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 // Save world settings

View File

@ -1,6 +1,7 @@
package world.bentobox.bentobox.database.objects; package world.bentobox.bentobox.database.objects;
import java.io.IOException; import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
@ -823,11 +824,28 @@ public class Island implements DataObject, MetaDataAble {
/** /**
* Set the Island Guard flag rank * Set the Island Guard flag rank
* This method affects subflags (if the given flag is a parent flag)
* @param flag - flag * @param flag - flag
* @param value - Use RanksManager settings, e.g. RanksManager.MEMBER * @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); 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(); setChanged();
} }
@ -1075,7 +1093,14 @@ public class Island implements DataObject, MetaDataAble {
// Fixes #getLastPlayed() returning 0 when it is the owner's first connection. // Fixes #getLastPlayed() returning 0 when it is the owner's first connection.
long lastPlayed = (Bukkit.getServer().getOfflinePlayer(getOwner()).getLastPlayed() != 0) ? long lastPlayed = (Bukkit.getServer().getOfflinePlayer(getOwner()).getLastPlayed() != 0) ?
Bukkit.getServer().getOfflinePlayer(getOwner()).getLastPlayed() : Bukkit.getServer().getOfflinePlayer(getOwner()).getFirstPlayed(); 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()))); user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(getWorld(), getOwner())));
String resets = String.valueOf(plugin.getPlayers().getResets(getWorld(), getOwner())); String resets = String.valueOf(plugin.getPlayers().getResets(getWorld(), getOwner()));
@ -1124,23 +1149,50 @@ public class Island implements DataObject, MetaDataAble {
/** /**
* Toggles a settings flag * Toggles a settings flag
* This method affects subflags (if the given flag is a parent flag)
* @param flag - flag * @param flag - flag
*/ */
public void toggleFlag(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)) { if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) {
setSettingsFlag(flag, !isAllowed(flag)); setSettingsFlag(flag, newToggleValue, doSubflags);
} }
setChanged(); setChanged();
} }
/** /**
* Sets the state of a settings flag * Sets the state of a settings flag
* This method affects subflags (if the given flag is a parent flag)
* @param flag - flag * @param flag - flag
* @param state - true or false * @param state - true or false
*/ */
public void setSettingsFlag(Flag flag, boolean state) { 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)) { 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(); setChanged();
} }

View File

@ -102,6 +102,9 @@ public class JoinLeaveListener implements Listener {
plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK); plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK);
plugin.getIslands().getMaxHomes(i); plugin.getIslands().getMaxHomes(i);
}); });
// Add a player to the bStats cache.
plugin.getMetrics().ifPresent(bStats -> bStats.addPlayer(playerUUID));
} }

View File

@ -95,7 +95,7 @@ public class BlockInteractionListener extends FlagListener {
private void checkClickedBlock(Event e, Player player, Location loc, Material type) { private void checkClickedBlock(Event e, Player player, Location loc, Material type) {
// Handle pots // Handle pots
if (type.name().startsWith("POTTED")) { if (type.name().startsWith("POTTED")) {
checkIsland(e, player, loc, Flags.CONTAINER); checkIsland(e, player, loc, Flags.FLOWER_POT);
return; return;
} }
if (Tag.ANVIL.isTagged(type)) { if (Tag.ANVIL.isTagged(type)) {
@ -115,7 +115,7 @@ public class BlockInteractionListener extends FlagListener {
return; return;
} }
if (Tag.SHULKER_BOXES.isTagged(type)) { if (Tag.SHULKER_BOXES.isTagged(type)) {
checkIsland(e, player, loc, Flags.CONTAINER); checkIsland(e, player, loc, Flags.SHULKER_BOX);
return; return;
} }
if (Tag.TRAPDOORS.isTagged(type)) { if (Tag.TRAPDOORS.isTagged(type)) {
@ -136,12 +136,20 @@ public class BlockInteractionListener extends FlagListener {
checkIsland(e, player, loc, Flags.HIVE); checkIsland(e, player, loc, Flags.HIVE);
break; break;
case BARREL: case BARREL:
checkIsland(e, player, loc, Flags.BARREL);
break;
case CHEST: case CHEST:
case CHEST_MINECART: case CHEST_MINECART:
checkIsland(e, player, loc, Flags.CHEST);
break;
case TRAPPED_CHEST: case TRAPPED_CHEST:
checkIsland(e, player, loc, Flags.TRAPPED_CHEST);
break;
case FLOWER_POT: case FLOWER_POT:
checkIsland(e, player, loc, Flags.FLOWER_POT);
break;
case COMPOSTER: case COMPOSTER:
checkIsland(e, player, loc, Flags.CONTAINER); checkIsland(e, player, loc, Flags.COMPOSTER);
break; break;
case DISPENSER: case DISPENSER:
checkIsland(e, player, loc, Flags.DISPENSER); checkIsland(e, player, loc, Flags.DISPENSER);

View File

@ -1,15 +1,20 @@
package world.bentobox.bentobox.listeners.flags.protection; 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.Beacon;
import org.bukkit.block.BrewingStand; import org.bukkit.block.BrewingStand;
import org.bukkit.block.Chest;
import org.bukkit.block.Dispenser; import org.bukkit.block.Dispenser;
import org.bukkit.block.Dropper; import org.bukkit.block.Dropper;
import org.bukkit.block.Furnace; import org.bukkit.block.Furnace;
import org.bukkit.block.Hopper; import org.bukkit.block.Hopper;
import org.bukkit.block.ShulkerBox;
import org.bukkit.entity.Animals; import org.bukkit.entity.Animals;
import org.bukkit.entity.NPC; import org.bukkit.entity.NPC;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.minecart.HopperMinecart; import org.bukkit.entity.minecart.HopperMinecart;
import org.bukkit.entity.minecart.StorageMinecart;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
@ -61,6 +66,29 @@ public class InventoryListener extends FlagListener {
else if (inventoryHolder instanceof NPC) { else if (inventoryHolder instanceof NPC) {
checkIsland(e, player, e.getInventory().getLocation(), Flags.TRADING); 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)) { else if (!(inventoryHolder instanceof Player)) {
// All other containers // All other containers
checkIsland(e, player, e.getInventory().getLocation(), Flags.CONTAINER); checkIsland(e, player, e.getInventory().getLocation(), Flags.CONTAINER);

View File

@ -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> 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);
}
}
}

View File

@ -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.RemoveMobsListener;
import world.bentobox.bentobox.listeners.flags.worldsettings.SpawnerSpawnEggsListener; import world.bentobox.bentobox.listeners.flags.worldsettings.SpawnerSpawnEggsListener;
import world.bentobox.bentobox.listeners.flags.worldsettings.TreesGrowingOutsideRangeListener; 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.listeners.flags.worldsettings.WitherListener;
import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
@ -110,7 +111,14 @@ public final class Flags {
public static final Flag BEACON = new Flag.Builder("BEACON", Material.BEACON).build(); 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 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 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 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 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(); public static final Flag HOPPER = new Flag.Builder("HOPPER", Material.HOPPER).mode(Flag.Mode.ADVANCED).build();
@ -129,6 +137,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 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 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 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. * Prevents players from interacting with the Dragon Egg.
@ -526,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(); 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. * Provides a list of all the Flag instances contained in this class using reflection.
* Deprecated Flags are ignored. * Deprecated Flags are ignored.

View File

@ -188,6 +188,7 @@ commands:
island-uuid: "UUID: [uuid]" island-uuid: "UUID: [uuid]"
owner: "Owner: [owner] ([uuid])" owner: "Owner: [owner] ([uuid])"
last-login: "Last login: [date]" last-login: "Last login: [date]"
last-login-date-time-format: "EEE MMM dd HH:mm:ss zzz yyyy"
deaths: "Deaths: [number]" deaths: "Deaths: [number]"
resets-left: "Resets: [number] (Max: [total])" resets-left: "Resets: [number] (Max: [total])"
team-members-title: "Team members:" team-members-title: "Team members:"
@ -787,15 +788,45 @@ protection:
name: "Cakes" name: "Cakes"
hint: "Cake eating disabled" hint: "Cake eating disabled"
CONTAINER: CONTAINER:
name: "Containers" name: "All containers"
description: |- description: |-
&a Toggle interaction with chests, &a Toggle interaction with all containers.
&a shulker boxes and flower pots, &a Includes: Barrel, bee hive, brewing stand,
&a composters and barrels. &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 Changing individual settings overrides
&7 by dedicated flags. &7 this flag.
hint: "Container access disabled" 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: DISPENSER:
name: "Dispensers" name: "Dispensers"
description: "Toggle dispenser interaction" description: "Toggle dispenser interaction"
@ -1271,6 +1302,15 @@ protection:
&a back to their island using commands &a back to their island using commands
&a if they are falling. &a if they are falling.
hint: "&c You cannot do that while 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: WITHER_DAMAGE:
name: "Toggle wither damage" name: "Toggle wither damage"
description: |- description: |-

View File

@ -24,6 +24,7 @@ softdepend:
- LangUtils - LangUtils
- WildStacker - WildStacker
- LuckPerms - LuckPerms
- HolographicDisplays
permissions: permissions:
bentobox.admin: bentobox.admin:

View File

@ -70,14 +70,14 @@ public class BlockInteractionListenerTest extends AbstractCommonSetup {
when(Tag.BEDS.isTagged(Material.WHITE_BED)).thenReturn(true); when(Tag.BEDS.isTagged(Material.WHITE_BED)).thenReturn(true);
clickedBlocks.put(Material.BREWING_STAND, Flags.BREWING); clickedBlocks.put(Material.BREWING_STAND, Flags.BREWING);
clickedBlocks.put(Material.CAULDRON, Flags.BREWING); clickedBlocks.put(Material.CAULDRON, Flags.BREWING);
clickedBlocks.put(Material.BARREL, Flags.CONTAINER); clickedBlocks.put(Material.BARREL, Flags.BARREL);
clickedBlocks.put(Material.CHEST, Flags.CONTAINER); clickedBlocks.put(Material.CHEST, Flags.CHEST);
clickedBlocks.put(Material.CHEST_MINECART, Flags.CONTAINER); clickedBlocks.put(Material.CHEST_MINECART, Flags.CHEST);
clickedBlocks.put(Material.TRAPPED_CHEST, Flags.CONTAINER); clickedBlocks.put(Material.TRAPPED_CHEST, Flags.TRAPPED_CHEST);
clickedBlocks.put(Material.SHULKER_BOX, Flags.CONTAINER); clickedBlocks.put(Material.SHULKER_BOX, Flags.SHULKER_BOX);
when(Tag.SHULKER_BOXES.isTagged(Material.SHULKER_BOX)).thenReturn(true); when(Tag.SHULKER_BOXES.isTagged(Material.SHULKER_BOX)).thenReturn(true);
clickedBlocks.put(Material.FLOWER_POT, Flags.CONTAINER); clickedBlocks.put(Material.FLOWER_POT, Flags.FLOWER_POT);
clickedBlocks.put(Material.COMPOSTER, Flags.CONTAINER); clickedBlocks.put(Material.COMPOSTER, Flags.COMPOSTER);
clickedBlocks.put(Material.DISPENSER, Flags.DISPENSER); clickedBlocks.put(Material.DISPENSER, Flags.DISPENSER);
clickedBlocks.put(Material.DROPPER, Flags.DROPPER); clickedBlocks.put(Material.DROPPER, Flags.DROPPER);
clickedBlocks.put(Material.HOPPER, Flags.HOPPER); clickedBlocks.put(Material.HOPPER, Flags.HOPPER);