Merge branch 'develop' into 1.16.5_lts

This commit is contained in:
tastybento 2022-01-08 11:58:50 -08:00
parent 4683f7f9af
commit 260635e31e
63 changed files with 3000 additions and 1676 deletions

View File

@ -30,19 +30,7 @@ import world.bentobox.bentobox.listeners.JoinLeaveListener;
import world.bentobox.bentobox.listeners.PanelListenerManager;
import world.bentobox.bentobox.listeners.PortalTeleportationListener;
import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener;
import world.bentobox.bentobox.managers.AddonsManager;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.managers.CommandsManager;
import world.bentobox.bentobox.managers.FlagsManager;
import world.bentobox.bentobox.managers.HooksManager;
import world.bentobox.bentobox.managers.IslandDeletionManager;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.managers.LocalesManager;
import world.bentobox.bentobox.managers.PlaceholdersManager;
import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.managers.WebManager;
import world.bentobox.bentobox.managers.*;
import world.bentobox.bentobox.util.heads.HeadGetter;
import world.bentobox.bentobox.versions.ServerCompatibility;
@ -69,6 +57,7 @@ public class BentoBox extends JavaPlugin {
private HooksManager hooksManager;
private PlaceholdersManager placeholdersManager;
private IslandDeletionManager islandDeletionManager;
private IslandChunkDeletionManager islandChunkDeletionManager;
private WebManager webManager;
// Settings
@ -157,7 +146,6 @@ public class BentoBox extends JavaPlugin {
// Load hooks
hooksManager = new HooksManager(this);
hooksManager.registerHook(new VaultHook());
// Load addons. Addons may load worlds, so they must go before islands are loaded.
addonsManager = new AddonsManager(this);
@ -177,6 +165,8 @@ public class BentoBox extends JavaPlugin {
private void completeSetup(long loadTime) {
final long enableStart = System.currentTimeMillis();
hooksManager.registerHook(new VaultHook());
hooksManager.registerHook(new PlaceholderAPIHook());
// Setup the Placeholders manager
placeholdersManager = new PlaceholdersManager(this);
@ -294,6 +284,7 @@ public class BentoBox extends JavaPlugin {
// Death counter
manager.registerEvents(new DeathListener(this), this);
// Island Delete Manager
islandChunkDeletionManager = new IslandChunkDeletionManager(this);
islandDeletionManager = new IslandDeletionManager(this);
manager.registerEvents(islandDeletionManager, this);
}
@ -523,6 +514,13 @@ public class BentoBox extends JavaPlugin {
return islandDeletionManager;
}
/**
* @return the islandChunkDeletionManager
*/
public IslandChunkDeletionManager getIslandChunkDeletionManager() {
return islandChunkDeletionManager;
}
/**
* @return an optional of the Bstats instance
* @since 1.1

View File

@ -298,6 +298,22 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "island.deletion.keep-previous-island-on-reset", since = "1.13.0")
private boolean keepPreviousIslandOnReset = false;
@ConfigComment("Toggles how the islands are deleted.")
@ConfigComment("* If set to 'false', all islands will be deleted at once.")
@ConfigComment(" This is fast but may cause an impact on the performance")
@ConfigComment(" as it'll load all the chunks of the in-deletion islands.")
@ConfigComment("* If set to 'true', the islands will be deleted one by one.")
@ConfigComment(" This is slower but will not cause any impact on the performance.")
@ConfigEntry(path = "island.deletion.slow-deletion", since = "1.19.1")
private boolean slowDeletion = false;
@ConfigComment("By default, If the destination is not safe, the plugin will try to search for a safe spot around the destination,")
@ConfigComment("then it will try to expand the y-coordinate up and down from the destination.")
@ConfigComment("This setting limits how far the y-coordinate will be expanded.")
@ConfigComment("If set to 0 or lower, the plugin will not expand the y-coordinate.")
@ConfigEntry(path = "island.safe-spot-search-vertical-range", since = "1.19.1")
private int safeSpotSearchVerticalRange = 400;
/* WEB */
@ConfigComment("Toggle whether BentoBox can connect to GitHub to get data about updates and addons.")
@ConfigComment("Disabling this will result in the deactivation of the update checker and of some other")
@ -890,4 +906,20 @@ public class Settings implements ConfigObject {
public void setMinPortalSearchRadius(int minPortalSearchRadius) {
this.minPortalSearchRadius = minPortalSearchRadius;
}
public int getSafeSpotSearchVerticalRange() {
return safeSpotSearchVerticalRange;
}
public void setSafeSpotSearchVerticalRange(int safeSpotSearchVerticalRange) {
this.safeSpotSearchVerticalRange = safeSpotSearchVerticalRange;
}
public boolean isSlowDeletion() {
return slowDeletion;
}
public void setSlowDeletion(boolean slowDeletion) {
this.slowDeletion = slowDeletion;
}
}

View File

@ -0,0 +1,78 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
/**
* Deletes all named homes from an island
* @author tastybento
*
*/
public class AdminDeleteHomesCommand extends ConfirmableCommand {
public AdminDeleteHomesCommand(CompositeCommand parent) {
super(parent, "deletehomes");
}
@Override
public void setup() {
setPermission("mod.deletehomes");
setOnlyPlayer(false);
setParametersHelp("commands.admin.deletehomes.parameters");
setDescription("commands.admin.deletehomes.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.size() != 1) {
showHelp(this, user);
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// Get target player
UUID targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
// Get island
Island island = getIslands().getIsland(getWorld(), targetUUID);
if (island == null) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
// Confirm
askConfirmation(user, user.getTranslation("commands.admin.deletehomes.warning"), () -> deleteHomes(user, targetUUID, island));
return true;
}
private boolean deleteHomes(User user, UUID targetUUID, Island island) {
island.removeHomes();
user.sendMessage("general.success");
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
if (args.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
}
}

View File

@ -92,6 +92,8 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
new AdminSettingsCommand(this);
// Location
new AdminSetProtectionCenterCommand(this);
// Delete homes
new AdminDeleteHomesCommand(this);
}
/**

View File

@ -9,6 +9,8 @@ import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Particle;
import com.google.common.base.Enums;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
@ -22,6 +24,8 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
private static final String DISPLAY = "display";
private static final String SHOW = "show";
private static final String HIDE = "hide";
// Since 1.18, the Particle.BARRIER was replaced by BLOCK_MARKER
private static final Particle BARRIER = Enums.getIfPresent(Particle.class, "BARRIER").or(Enums.getIfPresent(Particle.class, "BLOCK_MARKER").or(Particle.LAVA));
// Map of users to which ranges must be displayed
private final Map<User, Integer> displayRanges = new HashMap<>();
@ -46,15 +50,15 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
if (!displayRanges.containsKey(user)) {
switch (label) {
case DISPLAY, SHOW -> showZones(user);
case HIDE -> user.sendMessage("commands.admin.range.display.already-off");
default -> showHelp(this, user);
case DISPLAY, SHOW -> showZones(user);
case HIDE -> user.sendMessage("commands.admin.range.display.already-off");
default -> showHelp(this, user);
}
} else {
switch (label) {
case DISPLAY, HIDE -> hideZones(user);
case SHOW -> user.sendMessage("commands.admin.range.display.already-on");
default -> showHelp(this, user);
case DISPLAY, HIDE -> hideZones(user);
case SHOW -> user.sendMessage("commands.admin.range.display.already-on");
default -> showHelp(this, user);
}
}
@ -71,7 +75,7 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
getIslands().getIslandAt(user.getLocation()).ifPresent(island -> {
// Draw the island protected area
drawZone(user, Particle.BARRIER, null, island, island.getProtectionRange());
drawZone(user, BARRIER, null, island, island.getProtectionRange());
// Draw the default protected area if island protected zone is different
if (island.getProtectionRange() != getPlugin().getIWM().getIslandProtectionRange(getWorld())) {

View File

@ -34,7 +34,7 @@ public abstract class FlagListener implements Listener {
* Reason for why flag was allowed or disallowed
* Used by admins for debugging player actions
*/
enum Why {
protected enum Why {
UNPROTECTED_WORLD,
OP,
BYPASS_EVERYWHERE,
@ -206,7 +206,15 @@ public abstract class FlagListener implements Listener {
}
}
private void report(@Nullable User user, @NonNull Event e, @NonNull Location loc, @NonNull Flag flag, @NonNull Why why) {
/**
* Report why something did or did not happen for the admin why command
* @param user user involved
* @param e associated event
* @param loc location
* @param flag flag
* @param why reason enum
*/
protected void report(@Nullable User user, @NonNull Event e, @NonNull Location loc, @NonNull Flag flag, @NonNull Why why) {
// A quick way to debug flag listener unit tests is to add this line here: System.out.println(why.name()); NOSONAR
if (user != null && user.isPlayer() && user.getPlayer().getMetadata(loc.getWorld().getName() + "_why_debug").stream()
.filter(p -> p.getOwningPlugin().equals(getPlugin())).findFirst().map(MetadataValue::asBoolean).orElse(false)) {

View File

@ -97,7 +97,7 @@ public class TemplatedPanel extends Panel
{
String type = String.valueOf(record.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 1);
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
this.typeSlotMap.put(type, counter + 1);
}
}
@ -227,7 +227,7 @@ public class TemplatedPanel extends Panel
{
String type = String.valueOf(record.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 1);
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
this.typeSlotMap.put(type, counter + 1);
}
}
@ -290,7 +290,7 @@ public class TemplatedPanel extends Panel
{
String type = String.valueOf(record.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 1);
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
this.typeSlotMap.put(type, counter + 1);
}
}

View File

@ -7,6 +7,9 @@
package world.bentobox.bentobox.api.panels.reader;
import java.util.Arrays;
import java.util.Objects;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
@ -28,11 +31,11 @@ import world.bentobox.bentobox.api.panels.Panel;
* @since 1.17.3
*/
public record PanelTemplateRecord(Panel.Type type,
@Nullable String title,
@Nullable TemplateItem border,
@Nullable TemplateItem background,
boolean[] forcedRows,
@NonNull ItemTemplateRecord[][] content)
@Nullable String title,
@Nullable TemplateItem border,
@Nullable TemplateItem background,
boolean[] forcedRows,
@NonNull ItemTemplateRecord[][] content)
{
/**
* Instantiates a new Panel template record with empty content.
@ -76,4 +79,43 @@ public record PanelTemplateRecord(Panel.Type type,
this(icon, null, null);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.deepHashCode(content);
result = prime * result + Arrays.hashCode(forcedRows);
result = prime * result + Objects.hash(background, border, title, type);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof PanelTemplateRecord)) {
return false;
}
PanelTemplateRecord other = (PanelTemplateRecord) obj;
return Objects.equals(background, other.background) && Objects.equals(border, other.border)
&& Arrays.deepEquals(content, other.content) && Arrays.equals(forcedRows, other.forcedRows)
&& Objects.equals(title, other.title) && type == other.type;
}
@Override
public String toString() {
return "PanelTemplateRecord {type=" + type +
", title=" + title +
", border=" + border +
", background=" + background +
", forcedRows=" + Arrays.toString(forcedRows) +
", content=" + Arrays.toString(content) + "}";
}
}

View File

@ -4,6 +4,7 @@ import java.util.Map;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
@ -16,14 +17,9 @@ import com.google.gson.reflect.TypeToken;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.database.json.adapters.BukkitObjectTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.EnumTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.FlagTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.ItemStackTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.LocationTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.PotionEffectTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.VectorTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.WorldTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.*;
import world.bentobox.bentobox.versions.ServerCompatibility;
/**
* Allocates type adapters based on class type.
@ -52,6 +48,8 @@ public class BentoboxTypeAdapterFactory implements TypeAdapterFactory {
if (Location.class.isAssignableFrom(rawType)) {
// Use our current location adapter for backward compatibility
return (TypeAdapter<T>) new LocationTypeAdapter();
} else if (Biome.class.isAssignableFrom(rawType) && !ServerCompatibility.getInstance().isVersion(ServerCompatibility.ServerVersion.V1_17_1)) { // TODO: Any better way ?
return (TypeAdapter<T>) new BiomeTypeAdapter();
} else if (Enum.class.isAssignableFrom(rawType)) {
return new EnumTypeAdapter(rawType);
} else if (ItemStack.class.isAssignableFrom(rawType)) {

View File

@ -0,0 +1,114 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.bentobox.database.json.adapters;
import com.google.common.base.Enums;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.bukkit.block.Biome;
import org.eclipse.jdt.annotation.Nullable;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* Minecraft 1.18 reworked their biomes, and a lot of things were renamed or removed.
* This adapter will address these changes in each database object, instead of manually fining it
* per implementation.
*/
public final class BiomeTypeAdapter extends TypeAdapter<Biome>
{
/**
* Map that contains string value to the actual Biome enum object.
*/
static final Map<String, Biome> BIOMEMAP;
static {
Map<String, Biome> biomeMap = new HashMap<>();
// Put in current values.
Arrays.stream(Biome.values()).forEach(biome -> biomeMap.put(biome.name(), biome));
// Put in renamed biomes values.
biomeMap.put("TALL_BIRCH_FOREST", getBiome("OLD_GROWTH_BIRCH_FOREST", "TALL_BIRCH_FOREST"));
biomeMap.put("GIANT_TREE_TAIGA", getBiome("OLD_GROWTH_PINE_TAIGA", "GIANT_TREE_TAIGA"));
biomeMap.put("GIANT_SPRUCE_TAIGA", getBiome("OLD_GROWTH_SPRUCE_TAIGA", "GIANT_SPRUCE_TAIGA"));
biomeMap.put("SNOWY_TUNDRA", getBiome("SNOWY_PLAINS", "SNOWY_TUNDRA"));
biomeMap.put("JUNGLE_EDGE", getBiome("SPARSE_JUNGLE", "JUNGLE_EDGE"));
biomeMap.put("STONE_SHORE", getBiome("STONY_SHORE", "STONE_SHORE"));
biomeMap.put("MOUNTAINS", getBiome("WINDSWEPT_HILLS", "MOUNTAINS"));
biomeMap.put("WOODED_MOUNTAINS", getBiome("WINDSWEPT_FOREST", "WOODED_MOUNTAINS"));
biomeMap.put("GRAVELLY_MOUNTAINS", getBiome("WINDSWEPT_GRAVELLY_HILLS", "GRAVELLY_MOUNTAINS"));
biomeMap.put("SHATTERED_SAVANNA", getBiome("WINDSWEPT_SAVANNA", "SHATTERED_SAVANNA"));
biomeMap.put("WOODED_BADLANDS_PLATEAU", getBiome("WOODED_BADLANDS", "WOODED_BADLANDS_PLATEAU"));
// Put in removed biomes values. BONNe chose some close enough values.
biomeMap.put("SNOWY_MOUNTAINS", getBiome("WINDSWEPT_HILLS", "SNOWY_MOUNTAINS"));
biomeMap.put("DESERT_HILLS", getBiome("WINDSWEPT_HILLS", "DESERT_HILLS"));
biomeMap.put("MOUNTAIN_EDGE", getBiome("WINDSWEPT_HILLS", "MOUNTAIN_EDGE"));
biomeMap.put("SNOWY_TAIGA_HILLS", getBiome("WINDSWEPT_HILLS", "SNOWY_TAIGA_HILLS"));
biomeMap.put("TAIGA_HILLS", getBiome("WINDSWEPT_HILLS", "TAIGA_HILLS"));
biomeMap.put("TAIGA_MOUNTAINS", getBiome("WINDSWEPT_HILLS", "TAIGA_MOUNTAINS"));
biomeMap.put("SNOWY_TAIGA_MOUNTAINS", getBiome("WINDSWEPT_HILLS", "SNOWY_TAIGA_MOUNTAINS"));
biomeMap.put("WOODED_HILLS", getBiome("WINDSWEPT_FOREST", "WOODED_HILLS"));
biomeMap.put("SWAMP_HILLS", getBiome("WINDSWEPT_FOREST", "SWAMP_HILLS"));
biomeMap.put("DARK_FOREST_HILLS", getBiome("WINDSWEPT_FOREST", "DARK_FOREST_HILLS"));
biomeMap.put("JUNGLE_HILLS", getBiome("SPARSE_JUNGLE", "JUNGLE_HILLS"));
biomeMap.put("MODIFIED_JUNGLE", getBiome("SPARSE_JUNGLE", "MODIFIED_JUNGLE"));
biomeMap.put("MODIFIED_JUNGLE_EDGE", getBiome("SPARSE_JUNGLE", "MODIFIED_JUNGLE_EDGE"));
biomeMap.put("BAMBOO_JUNGLE_HILLS", getBiome("SPARSE_JUNGLE", "BAMBOO_JUNGLE_HILLS"));
biomeMap.put("BIRCH_FOREST_HILLS", getBiome("OLD_GROWTH_BIRCH_FOREST", "BIRCH_FOREST_HILLS"));
biomeMap.put("TALL_BIRCH_HILLS", getBiome("OLD_GROWTH_BIRCH_FOREST", "TALL_BIRCH_HILLS"));
biomeMap.put("GIANT_TREE_TAIGA_HILLS", getBiome("OLD_GROWTH_PINE_TAIGA", "GIANT_TREE_TAIGA_HILLS"));
biomeMap.put("GIANT_SPRUCE_TAIGA_HILLS", getBiome("OLD_GROWTH_SPRUCE_TAIGA", "GIANT_SPRUCE_TAIGA_HILLS"));
biomeMap.put("MUSHROOM_FIELD_SHORE", getBiome("MUSHROOM_FIELDS", "MUSHROOM_FIELD_SHORE"));
biomeMap.put("BADLANDS_PLATEAU", getBiome("BADLANDS", "BADLANDS_PLATEAU"));
biomeMap.put("MODIFIED_WOODED_BADLANDS_PLATEAU", getBiome("BADLANDS", "MODIFIED_WOODED_BADLANDS_PLATEAU"));
biomeMap.put("MODIFIED_BADLANDS_PLATEAU", getBiome("BADLANDS", "MODIFIED_BADLANDS_PLATEAU"));
biomeMap.put("SHATTERED_SAVANNA_PLATEAU", getBiome("SAVANNA_PLATEAU", "SHATTERED_SAVANNA_PLATEAU"));
biomeMap.put("DESERT_LAKES", getBiome("DESERT", "DESERT_LAKES"));
biomeMap.put("MODIFIED_GRAVELLY_MOUNTAINS", getBiome("WINDSWEPT_GRAVELLY_HILLS", "MODIFIED_GRAVELLY_MOUNTAINS"));
biomeMap.put("DEEP_WARM_OCEAN", getBiome("DEEP_LUKEWARM_OCEAN", "DEEP_WARM_OCEAN"));
BIOMEMAP = Collections.unmodifiableMap(biomeMap);
}
/**
* Safely gets a biome based on string values
* @param primary - primary biome
* @param secondary - secondary biome
* @return Biome or null
*/
@Nullable
private static Biome getBiome(String primary, String secondary) {
return Enums.getIfPresent(Biome.class, primary).or(Enums.getIfPresent(Biome.class, secondary).orNull());
}
@Override
public Biome read(JsonReader input) throws IOException
{
if (JsonToken.NULL.equals(input.peek())) {
input.nextNull();
return null;
}
return BIOMEMAP.get(input.nextString().toUpperCase(Locale.ENGLISH));
}
@Override
public void write(JsonWriter output, Biome enumValue) throws IOException {
output.value(enumValue != null ? enumValue.name() : null);
}
}

View File

@ -1444,6 +1444,16 @@ public class Island implements DataObject, MetaDataAble {
return getHomes().remove(name.toLowerCase()) != null;
}
/**
* Remove all homes from this island except the default home
* @return true if any non-default homes removed
* @since 1.20.0
*/
public boolean removeHomes() {
setChanged();
return getHomes().keySet().removeIf(k -> !k.isEmpty());
}
/**
* Rename a home
* @param oldName - old name of home
@ -1542,4 +1552,6 @@ public class Island implements DataObject, MetaDataAble {
}

View File

@ -237,7 +237,7 @@ public class Players implements DataObject, MetaDataAble {
/**
* Clears all home Locations in world
* @param world - world
* @deprecated Home locations are no longer stored for players. Use {@link IslandManager}
* @deprecated Home locations are no longer stored for players. Use {@link world.bentobox.bentobox.managers.IslandsManager}
*/
@Deprecated(since="1.18.0", forRemoval=true)
public void clearHomeLocations(World world) {

View File

@ -39,12 +39,12 @@ public class BlockEndDragon implements Listener {
World w = location.getWorld();
if (w == null || !plugin.getIWM().isIslandEnd(w)
|| !Flags.REMOVE_END_EXIT_ISLAND.isSetForWorld(w)
|| w.getBlockAt(0, 255, 0).getType().equals(Material.END_PORTAL)) {
|| w.getBlockAt(0, w.getMaxHeight() - 1, 0).getType().equals(Material.END_PORTAL)) {
return;
}
// Setting a End Portal at the top will trick dragon legacy check.
w.getBlockAt(0, 255, 0).setType(Material.END_PORTAL, false);
w.getBlockAt(0, w.getMaxHeight() - 1, 0).setType(Material.END_PORTAL, false);
}
/**
@ -68,9 +68,9 @@ public class BlockEndDragon implements Listener {
}
private boolean testBlock(Block block) {
return block.getY() == 255
&& block.getX() == 0
return block.getX() == 0
&& block.getZ() == 0
&& block.getY() == block.getWorld().getMaxHeight() - 1
&& block.getWorld().getEnvironment().equals(Environment.THE_END)
&& Flags.REMOVE_END_EXIT_ISLAND.isSetForWorld(block.getWorld())
&& plugin.getIWM().inWorld(block.getWorld())

View File

@ -57,7 +57,7 @@ public class CommandRankClickListener implements ClickHandler {
// Get the user's island
Island island = plugin.getIslands().getIsland(panel.getWorld().orElse(user.getWorld()), user.getUniqueId());
if (island == null || !island.getOwner().equals(user.getUniqueId())) {
if (island == null || island.getOwner() == null || !island.getOwner().equals(user.getUniqueId())) {
user.sendMessage("general.errors.not-owner");
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
return true;

View File

@ -10,6 +10,7 @@ import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
@ -50,8 +51,8 @@ public class TNTListener extends FlagListener {
// Stop TNT from being damaged if it is being caused by a visitor with a flaming arrow
if (e.getEntity() instanceof Projectile projectile) {
// Find out who fired it
if (projectile.getShooter() instanceof Player && projectile.getFireTicks() > 0
&& !checkIsland(e, (Player)projectile.getShooter(), e.getBlock().getLocation(), Flags.TNT_PRIMING)) {
if (projectile.getShooter() instanceof Player shooter && projectile.getFireTicks() > 0
&& !checkIsland(e, shooter, e.getBlock().getLocation(), Flags.TNT_PRIMING)) {
// Remove the arrow
projectile.remove();
e.setCancelled(true);
@ -82,12 +83,17 @@ public class TNTListener extends FlagListener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onExplosion(final EntityExplodeEvent e) {
// Check world and types
if (getIWM().inWorld(e.getLocation()) && TNT_TYPES.contains(e.getEntityType())) {
// Remove any blocks from the explosion list if required
e.blockList().removeIf(b -> protect(b.getLocation()));
e.setCancelled(protect(e.getLocation()));
if (!getIWM().inWorld(e.getLocation()) || !TNT_TYPES.contains(e.getEntityType())) {
return;
}
if (protect(e.getLocation())) {
// This is protected as a whole, so just cancel the event
e.setCancelled(true);
} else {
// Remove any blocks from the explosion list if required
e.blockList().removeIf(b -> protect(b.getLocation()));
}
}
protected boolean protect(Location location) {
@ -109,4 +115,43 @@ public class TNTListener extends FlagListener {
e.setCancelled(protect(e.getEntity().getLocation()));
}
}
protected boolean protectBlockExplode(Location location) {
return getIslands().getProtectedIslandAt(location).map(i -> !i.isAllowed(Flags.BLOCK_EXPLODE_DAMAGE))
.orElseGet(() -> !Flags.WORLD_BLOCK_EXPLODE_DAMAGE.isSetForWorld(location.getWorld()));
}
/**
* Prevents block explosion from breaking blocks
* @param e - event
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onExplosion(final BlockExplodeEvent e) {
// Check world
if (!getIWM().inWorld(e.getBlock().getLocation())) {
return;
}
if (protectBlockExplode(e.getBlock().getLocation())) {
// This is protected as a whole, so just cancel the event
e.setCancelled(true);
} else {
// Remove any blocks from the explosion list if required
e.blockList().removeIf(b -> protectBlockExplode(b.getLocation()));
}
}
/**
* Prevents block explosion from damaging entities.
* @param e event
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onExplosion(final EntityDamageEvent e) {
// Check if this in world and a block explosion
if (getIWM().inWorld(e.getEntity().getLocation())
&& e.getCause().equals(EntityDamageEvent.DamageCause.BLOCK_EXPLOSION)) {
// Check if it is disallowed, then cancel it.
e.setCancelled(protectBlockExplode(e.getEntity().getLocation()));
}
}
}

View File

@ -225,8 +225,8 @@ public class PVPListener extends FlagListener {
String message = "protection.flags." + flag.getID() + "." + (e.isSetTo() ? "enabled" : "disabled");
// Send the message to visitors
e.getIsland().getVisitors().forEach(visitor -> User.getInstance(visitor).sendMessage(message));
// Send the message to island members (and coops and trusted)
e.getIsland().getMemberSet(RanksManager.COOP_RANK).forEach(member -> User.getInstance(member).sendMessage(message));
// Send the message to players on the island
e.getIsland().getPlayersOnIsland().forEach(player -> User.getInstance(player).sendMessage(message));
}
}

View File

@ -60,10 +60,12 @@ public class IslandRespawnListener extends FlagListener {
if (world == null) {
return; // world no longer available
}
final Location respawnLocation = getIslands().getSafeHomeLocation(Util.getWorld(world), User.getInstance(e.getPlayer().getUniqueId()), "");
if (respawnLocation != null) {
e.setRespawnLocation(respawnLocation);
World w = Util.getWorld(world);
if (w != null) {
final Location respawnLocation = getIslands().getSafeHomeLocation(w, User.getInstance(e.getPlayer().getUniqueId()), "");
if (respawnLocation != null) {
e.setRespawnLocation(respawnLocation);
}
}
// Run respawn commands, if any
Util.runCommands(User.getInstance(e.getPlayer()), getIWM().getOnRespawnCommands(world), "respawn");

View File

@ -5,6 +5,7 @@ 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.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.util.Util;
@ -25,6 +26,7 @@ public class VisitorKeepInventoryListener extends FlagListener {
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
this.report(User.getInstance(e.getEntity()), e, e.getEntity().getLocation(), Flags.VISITOR_KEEP_INVENTORY, Why.SETTING_NOT_ALLOWED_IN_WORLD);
return;
}
@ -34,6 +36,7 @@ public class VisitorKeepInventoryListener extends FlagListener {
e.setKeepLevel(true);
e.getDrops().clear();
e.setDroppedExp(0);
this.report(User.getInstance(e.getEntity()), e, e.getEntity().getLocation(), Flags.VISITOR_KEEP_INVENTORY, Why.SETTING_ALLOWED_IN_WORLD);
}
}
}

View File

@ -392,6 +392,14 @@ public final class Flags {
public static final Flag TNT_DAMAGE = new Flag.Builder("TNT_DAMAGE", Material.TNT).type(Type.SETTING)
.mode(Flag.Mode.ADVANCED).build();
/**
* If {@code false}, prevents Block Explode from breaking blocks and damaging nearby entities.
* @since 1.19.1
* @see TNTListener
*/
public static final Flag BLOCK_EXPLODE_DAMAGE = new Flag.Builder("BLOCK_EXPLODE_DAMAGE", Material.TNT_MINECART).type(Type.SETTING)
.mode(Flag.Mode.ADVANCED).build();
/**
* If {@code false}, prevents TNT from breaking blocks and damaging nearby entities outside of island boundaries.
* @since 1.15.3
@ -401,6 +409,15 @@ public final class Flags {
.type(Type.WORLD_SETTING)
.build();
/**
* If {@code false}, prevents Block Explode from breaking blocks and damaging nearby entities outside of island boundaries.
* @since 1.19.1
* @see TNTListener
*/
public static final Flag WORLD_BLOCK_EXPLODE_DAMAGE = new Flag.Builder("WORLD_BLOCK_EXPLODE_DAMAGE", Material.TNT_MINECART)
.type(Type.WORLD_SETTING)
.build();
/*
* World Settings - they apply to every island in the game worlds.
*/

View File

@ -96,7 +96,7 @@ public class BlueprintClipboardManager {
plugin.logError(LOAD_ERROR + zipFile.getName());
throw new IOException(LOAD_ERROR + zipFile.getName());
}
unzip(zipFile.getAbsolutePath());
unzip(zipFile.getCanonicalPath());
File file = new File(blueprintFolder, BlueprintsManager.sanitizeFileName(fileName));
if (!file.exists()) {
plugin.logError(LOAD_ERROR + file.getName());
@ -194,7 +194,7 @@ public class BlueprintClipboardManager {
if (!entry.isDirectory()) {
unzipFiles(zipInputStream, filePath);
} else {
if (!filePath.startsWith(blueprintFolder.getAbsolutePath())) {
if (!filePath.startsWith(blueprintFolder.getCanonicalPath())) {
throw new IOException("Entry is outside of the target directory");
}
Files.createDirectories(filePath);
@ -207,10 +207,10 @@ public class BlueprintClipboardManager {
}
private void unzipFiles(final ZipInputStream zipInputStream, final Path unzipFilePath) throws IOException {
if (!unzipFilePath.toAbsolutePath().toString().startsWith(blueprintFolder.getAbsolutePath())) {
if (!unzipFilePath.toFile().getCanonicalPath().startsWith(blueprintFolder.getCanonicalPath())) {
throw new IOException("Entry is outside of the target directory");
}
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(unzipFilePath.toAbsolutePath().toString()))) {
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(unzipFilePath.toFile().getCanonicalPath()))) {
byte[] bytesIn = new byte[1024];
int read;
while ((read = zipInputStream.read(bytesIn)) != -1) {
@ -220,7 +220,7 @@ public class BlueprintClipboardManager {
}
private void zip(File targetFile) throws IOException {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(targetFile.getAbsolutePath() + BlueprintsManager.BLUEPRINT_SUFFIX))) {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(targetFile.getCanonicalPath() + BlueprintsManager.BLUEPRINT_SUFFIX))) {
zipOutputStream.putNextEntry(new ZipEntry(targetFile.getName()));
try (FileInputStream inputStream = new FileInputStream(targetFile)) {
final byte[] buffer = new byte[1024];

View File

@ -0,0 +1,60 @@
package world.bentobox.bentobox.managers;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.objects.IslandDeletion;
import world.bentobox.bentobox.util.DeleteIslandChunks;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicReference;
/**
* Manages the queue of island chunks to be deleted.
*/
public class IslandChunkDeletionManager implements Runnable {
private final boolean slowDeletion;
private final BentoBox plugin;
private final AtomicReference<DeleteIslandChunks> currentTask;
private final Queue<IslandDeletion> queue;
public IslandChunkDeletionManager(BentoBox plugin) {
this.plugin = plugin;
this.currentTask = new AtomicReference<>();
this.queue = new LinkedList<>();
this.slowDeletion = plugin.getSettings().isSlowDeletion();
if (slowDeletion) {
plugin.getServer().getScheduler().runTaskTimer(plugin, this, 0L, 20L);
}
}
@Override
public void run() {
if (queue.isEmpty()) {
return;
}
DeleteIslandChunks task = this.currentTask.get();
if (task != null && !task.isCompleted()) {
return;
}
IslandDeletion islandDeletion = queue.remove();
currentTask.set(startDeleteTask(islandDeletion));
}
private DeleteIslandChunks startDeleteTask(IslandDeletion islandDeletion) {
return new DeleteIslandChunks(plugin, islandDeletion);
}
/**
* Adds an island deletion to the queue.
*
* @param islandDeletion island deletion
*/
public void add(IslandDeletion islandDeletion) {
if (slowDeletion) {
queue.add(islandDeletion);
} else {
startDeleteTask(islandDeletion);
}
}
}

View File

@ -16,7 +16,6 @@ import world.bentobox.bentobox.api.events.island.IslandDeleteChunksEvent;
import world.bentobox.bentobox.api.events.island.IslandDeletedEvent;
import world.bentobox.bentobox.database.Database;
import world.bentobox.bentobox.database.objects.IslandDeletion;
import world.bentobox.bentobox.util.DeleteIslandChunks;
import world.bentobox.bentobox.util.Util;
/**
@ -57,7 +56,7 @@ public class IslandDeletionManager implements Listener {
} else {
plugin.log("Resuming deletion of island at " + di.getLocation().getWorld().getName() + " " + Util.xyz(di.getLocation().toVector()));
inDeletion.add(di.getLocation());
new DeleteIslandChunks(plugin, di);
plugin.getIslandChunkDeletionManager().add(di);
}
});
}

View File

@ -54,7 +54,6 @@ import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.database.objects.IslandDeletion;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.island.IslandCache;
import world.bentobox.bentobox.util.DeleteIslandChunks;
import world.bentobox.bentobox.util.Util;
import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
@ -346,7 +345,7 @@ public class IslandsManager {
// Remove players from island
removePlayersFromIsland(island);
// Remove blocks from world
new DeleteIslandChunks(plugin, new IslandDeletion(island));
plugin.getIslandChunkDeletionManager().add(new IslandDeletion(island));
}
}
@ -512,7 +511,7 @@ public class IslandsManager {
int islandMax = island.getMaxMembers(rank) == null ? worldDefault : island.getMaxMembers(rank);
// Update based on owner permissions if online
if (Bukkit.getPlayer(island.getOwner()) != null) {
if (island.getOwner() != null && Bukkit.getPlayer(island.getOwner()) != null) {
User owner = User.getInstance(island.getOwner());
islandMax = owner.getPermissionValue(plugin.getIWM().getPermissionPrefix(island.getWorld())
+ perm, islandMax);
@ -544,7 +543,7 @@ public class IslandsManager {
public int getMaxHomes(@NonNull Island island) {
int islandMax = island.getMaxHomes() == null ? plugin.getIWM().getMaxHomes(island.getWorld()) : island.getMaxHomes();
// Update based on owner permissions if online
if (Bukkit.getPlayer(island.getOwner()) != null) {
if (island.getOwner() != null && Bukkit.getPlayer(island.getOwner()) != null) {
User owner = User.getInstance(island.getOwner());
islandMax = owner.getPermissionValue(plugin.getIWM().getPermissionPrefix(island.getWorld())
+ "island.maxhomes", islandMax);
@ -622,11 +621,11 @@ public class IslandsManager {
return result;
}
private void tryIsland(CompletableFuture<Location> result, Location islandLoc, @NonNull User user, String number) {
private void tryIsland(CompletableFuture<Location> result, Location islandLoc, @NonNull User user, String name) {
Util.getChunkAtAsync(islandLoc).thenRun(() -> {
World w = islandLoc.getWorld();
if (isSafeLocation(islandLoc)) {
setHomeLocation(user, islandLoc, number);
setHomeLocation(user, islandLoc, name);
result.complete(islandLoc.clone().add(new Vector(0.5D,0,0.5D)));
return;
} else {
@ -634,14 +633,14 @@ public class IslandsManager {
// Try the default location
Location dl = islandLoc.clone().add(new Vector(0.5D, 5D, 2.5D));
if (isSafeLocation(dl)) {
setHomeLocation(user, dl, number);
setHomeLocation(user, dl, name);
result.complete(dl);
return;
}
// Try just above the bedrock
dl = islandLoc.clone().add(new Vector(0.5D, 5D, 0.5D));
if (isSafeLocation(dl)) {
setHomeLocation(user, dl, number);
setHomeLocation(user, dl, name);
result.complete(dl);
return;
}
@ -649,7 +648,7 @@ public class IslandsManager {
for (int y = islandLoc.getBlockY(); y < w.getMaxHeight(); y++) {
dl = new Location(w, islandLoc.getX() + 0.5D, y, islandLoc.getZ() + 0.5D);
if (isSafeLocation(dl)) {
setHomeLocation(user, dl, number);
setHomeLocation(user, dl, name);
result.complete(dl);
return;
}
@ -705,10 +704,13 @@ public class IslandsManager {
return l;
} else {
// try owner's home
Location tlh = getHomeLocation(world, getOwner(world, user.getUniqueId()));
if (tlh != null && isSafeLocation(tlh)) {
setHomeLocation(user, tlh, name);
return tlh;
UUID owner = getOwner(world, user.getUniqueId());
if (owner != null) {
Location tlh = getHomeLocation(world, owner);
if (tlh != null && isSafeLocation(tlh)) {
setHomeLocation(user, tlh, name);
return tlh;
}
}
}
} else {
@ -866,6 +868,7 @@ public class IslandsManager {
return getHomeLocation(island, name);
}
@SuppressWarnings("removal")
private void migrateHomes(@NonNull World world, @NonNull UUID uuid, String name, Island island) {
Map<Location, Integer> homes = plugin
.getPlayers()
@ -874,7 +877,7 @@ public class IslandsManager {
// No migration required
return;
}
if (island.getOwner().equals(uuid)) {
if (island.getOwner() != null && island.getOwner().equals(uuid)) {
// Owner
island.setHomes(homes.entrySet().stream().collect(Collectors.toMap(this::getHomeName, Map.Entry::getKey)));
plugin.getPlayers().clearHomeLocations(world, uuid);

View File

@ -547,7 +547,7 @@ public class PlayersManager {
// Remove any tamed animals
world.getEntitiesByClass(Tameable.class).stream()
.filter(Tameable::isTamed)
.filter(t -> t.getOwner() != null && t.getOwner().equals(target.getPlayer()))
.filter(t -> t.getOwner() != null && t.getOwner().getUniqueId().equals(target.getUniqueId()))
.forEach(t -> t.setOwner(null));
// Remove money inventory etc.
@ -555,7 +555,10 @@ public class PlayersManager {
if (target.isOnline()) {
target.getPlayer().getEnderChest().clear();
} else {
getPlayer(target.getUniqueId()).addToPendingKick(world);
Players p = getPlayer(target.getUniqueId());
if (p != null) {
p.addToPendingKick(world);
}
}
}
if ((kicked && plugin.getIWM().isOnLeaveResetInventory(world) && !plugin.getIWM().isKickedKeepInventory(world))
@ -563,7 +566,10 @@ public class PlayersManager {
if (target.isOnline()) {
target.getPlayer().getInventory().clear();
} else {
getPlayer(target.getUniqueId()).addToPendingKick(world);
Players p = getPlayer(target.getUniqueId());
if (p != null) {
p.addToPendingKick(world);
}
}
}

View File

@ -5,6 +5,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@ -141,7 +142,11 @@ public class IslandCache {
*/
@Nullable
public Island get(@NonNull World world, @NonNull UUID uuid) {
return islandsByUUID.containsKey(Util.getWorld(world)) ? islandsByUUID.get(Util.getWorld(world)).get(uuid) : null;
World w = Util.getWorld(world);
if (w == null) {
return null;
}
return islandsByUUID.containsKey(w) ? islandsByUUID.get(w).get(uuid) : null;
}
/**
@ -153,10 +158,11 @@ public class IslandCache {
*/
@Nullable
public Island getIslandAt(@NonNull Location location) {
if (!grids.containsKey(Util.getWorld(location.getWorld()))) {
World w = Util.getWorld(location.getWorld());
if (w == null || !grids.containsKey(w)) {
return null;
}
return grids.get(Util.getWorld(location.getWorld())).getIslandAt(location.getBlockX(), location.getBlockZ());
return grids.get(w).getIslandAt(location.getBlockX(), location.getBlockZ());
}
/**
@ -177,7 +183,9 @@ public class IslandCache {
@NonNull
public Collection<Island> getIslands(@NonNull World world) {
World overworld = Util.getWorld(world);
if (overworld == null) {
return Collections.emptyList();
}
return islandsByLocation.entrySet().stream()
.filter(entry -> overworld.equals(Util.getWorld(entry.getKey().getWorld()))) // shouldn't make NPEs
.map(Map.Entry::getValue).toList();
@ -191,7 +199,11 @@ public class IslandCache {
*/
@NonNull
public Set<UUID> getMembers(@NonNull World world, @NonNull UUID uuid, int minimumRank) {
Island island = islandsByUUID.computeIfAbsent(Util.getWorld(world), k -> new HashMap<>()).get(uuid);
World w = Util.getWorld(world);
if (w == null) {
return new HashSet<>();
}
Island island = islandsByUUID.computeIfAbsent(w, k -> new HashMap<>()).get(uuid);
return island != null ? island.getMemberSet(minimumRank) : new HashSet<>();
}
@ -202,7 +214,11 @@ public class IslandCache {
*/
@Nullable
public UUID getOwner(@NonNull World world, @NonNull UUID uuid) {
Island island = islandsByUUID.computeIfAbsent(Util.getWorld(world), k -> new HashMap<>()).get(uuid);
World w = Util.getWorld(world);
if (w == null) {
return null;
}
Island island = islandsByUUID.computeIfAbsent(w, k -> new HashMap<>()).get(uuid);
return island != null ? island.getOwner() : null;
}
@ -213,7 +229,11 @@ public class IslandCache {
* @return true if player has island and owns it
*/
public boolean hasIsland(@NonNull World world, @NonNull UUID uuid) {
Island island = islandsByUUID.computeIfAbsent(Util.getWorld(world), k -> new HashMap<>()).get(uuid);
World w = Util.getWorld(world);
if (w == null) {
return false;
}
Island island = islandsByUUID.computeIfAbsent(w, k -> new HashMap<>()).get(uuid);
return island != null && uuid.equals(island.getOwner());
}
@ -226,7 +246,11 @@ public class IslandCache {
*/
@Nullable
public Island removePlayer(@NonNull World world, @NonNull UUID uuid) {
Island island = islandsByUUID.computeIfAbsent(Util.getWorld(world), k -> new HashMap<>()).get(uuid);
World w = Util.getWorld(world);
if (w == null) {
return null;
}
Island island = islandsByUUID.computeIfAbsent(w, k -> new HashMap<>()).get(uuid);
if (island != null) {
if (uuid.equals(island.getOwner())) {
// Clear ownership and members
@ -237,7 +261,7 @@ public class IslandCache {
island.removeMember(uuid);
}
}
islandsByUUID.get(world).remove(uuid);
islandsByUUID.get(w).remove(uuid);
return island;
}
@ -267,7 +291,7 @@ public class IslandCache {
public void setOwner(@NonNull Island island, @Nullable UUID newOwnerUUID) {
island.setOwner(newOwnerUUID);
if (newOwnerUUID != null) {
islandsByUUID.computeIfAbsent(Util.getWorld(island.getWorld()), k -> new HashMap<>()).put(newOwnerUUID, island);
islandsByUUID.computeIfAbsent(Objects.requireNonNull(Util.getWorld(island.getWorld())), k -> new HashMap<>()).put(newOwnerUUID, island);
}
islandsByLocation.put(island.getCenter(), island);
islandsById.put(island.getUniqueId(), island);
@ -292,11 +316,15 @@ public class IslandCache {
public void removeIsland(@NonNull Island island) {
islandsByLocation.values().removeIf(island::equals);
islandsById.values().removeIf(island::equals);
if (islandsByUUID.containsKey(Util.getWorld(island.getWorld()))) {
islandsByUUID.get(Util.getWorld(island.getWorld())).values().removeIf(island::equals);
World w = Util.getWorld(island.getWorld());
if (w == null) {
return;
}
if (grids.containsKey(Util.getWorld(island.getWorld()))) {
grids.get(Util.getWorld(island.getWorld())).removeFromGrid(island);
if (islandsByUUID.containsKey(w)) {
islandsByUUID.get(w).values().removeIf(island::equals);
}
if (grids.containsKey(w)) {
grids.get(w).removeFromGrid(island);
}
}
@ -307,6 +335,9 @@ public class IslandCache {
*/
public void resetAllFlags(World world) {
World w = Util.getWorld(world);
if (w == null) {
return;
}
islandsById.values().stream().filter(i -> i.getWorld().equals(w)).forEach(Island::setFlagsDefaults);
}
@ -318,6 +349,9 @@ public class IslandCache {
*/
public void resetFlag(World world, Flag flag) {
World w = Util.getWorld(world);
if (w == null) {
return;
}
int setting = BentoBox.getInstance().getIWM().getDefaultIslandFlags(w).getOrDefault(flag, flag.getDefaultRank());
islandsById.values().stream().filter(i -> i.getWorld().equals(w)).forEach(i -> i.setFlag(flag, setting));
}

View File

@ -2,9 +2,37 @@ package world.bentobox.bentobox.nms;
import org.bukkit.Chunk;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.util.BoundingBox;
public interface NMSAbstraction {
/**
* Copy the chunk data and biome grid to the given chunk.
* @param chunk - chunk to copy to
* @param chunkData - chunk data to copy
* @param biomeGrid - biome grid to copy to
* @param limitBox - bounding box to limit the copying
*/
default void copyChunkDataToChunk(Chunk chunk, ChunkGenerator.ChunkData chunkData, ChunkGenerator.BiomeGrid biomeGrid, BoundingBox limitBox) {
double baseX = chunk.getX() << 4;
double baseZ = chunk.getZ() << 4;
int minHeight = chunk.getWorld().getMinHeight();
int maxHeight = chunk.getWorld().getMaxHeight();
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
if (!limitBox.contains(baseX + x, 0, baseZ + z)) {
continue;
}
for (int y = minHeight; y < maxHeight; y++) {
setBlockInNativeChunk(chunk, x, y, z, chunkData.getBlockData(x, y, z), false);
// 3D biomes, 4 blocks separated
if (x % 4 == 0 && y % 4 == 0 && z % 4 == 0) {
chunk.getBlock(x, y, z).setBiome(biomeGrid.getBiome(x, y, z));
}
}
}
}
}
/**
* Update the low-level chunk information for the given block to the new block ID and data. This

View File

@ -183,7 +183,7 @@ public class BlueprintManagementPanel {
// Toggle permission - default is always allowed
pb.item(39, getNoPermissionIcon());
} else {
// Panel has a Trash icon. If right clicked it is discarded
// Panel has a Trash icon. If right clicked it is discarded
pb.item(36, getTrashIcon(addon, bb));
// Toggle permission - default is always allowed
pb.item(39, getPermissionIcon(addon, bb));
@ -256,22 +256,22 @@ public class BlueprintManagementPanel {
Material icon;
String worldName;
switch (env) {
case NORMAL -> {
icon = Material.GRASS_BLOCK;
worldName = normalBlueprint.getName();
}
case NETHER -> {
icon = Material.NETHERRACK;
worldName = netherBlueprint.getName();
}
case THE_END -> {
icon = Material.END_STONE;
worldName = endBlueprint.getName();
}
default -> {
icon = Material.STONE;
worldName = Util.prettifyText(env.name());
}
case NORMAL -> {
icon = Material.GRASS_BLOCK;
worldName = normalBlueprint.getName();
}
case NETHER -> {
icon = Material.NETHERRACK;
worldName = netherBlueprint.getName();
}
case THE_END -> {
icon = Material.END_STONE;
worldName = endBlueprint.getName();
}
default -> {
icon = Material.STONE;
worldName = Util.prettifyText(env.name());
}
}
return new PanelItemBuilder()
@ -297,7 +297,7 @@ public class BlueprintManagementPanel {
})
.build();
}
private PanelItem getNoTrashIcon() {
return new PanelItemBuilder()
.name(t("no-trash"))
@ -321,7 +321,7 @@ public class BlueprintManagementPanel {
return true;
}).build();
}
private PanelItem getNoPermissionIcon() {
return new PanelItemBuilder().icon(Material.PAINTING).name(t("no-permission"))
.description(t("no-perm-required"))
@ -350,7 +350,7 @@ public class BlueprintManagementPanel {
return new PanelItemBuilder()
.name(blueprint.getDisplayName() == null ? blueprint.getName() : blueprint.getDisplayName())
.description(desc)
.icon(blueprint.getIcon() == null ? Material.PAPER : blueprint.getIcon())
.icon(blueprint.getIcon())
.glow(selected != null && pos == selected.getKey())
.clickHandler((panel, u, clickType, slot) -> {
// Handle the world squares

View File

@ -1,9 +1,6 @@
package world.bentobox.bentobox.util;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
@ -13,8 +10,6 @@ import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.ChunkGenerator.ChunkData;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.scheduler.BukkitTask;
import io.papermc.lib.PaperLib;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.events.island.IslandEvent;
@ -22,6 +17,13 @@ import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
import world.bentobox.bentobox.database.objects.IslandDeletion;
import world.bentobox.bentobox.nms.NMSAbstraction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Deletes islands chunk by chunk
*
@ -29,21 +31,23 @@ import world.bentobox.bentobox.nms.NMSAbstraction;
*/
public class DeleteIslandChunks {
private final IslandDeletion di;
private final BentoBox plugin;
private final World netherWorld;
private final World endWorld;
private final AtomicBoolean completed;
private final NMSAbstraction nms;
private int chunkX;
private int chunkZ;
private BukkitTask task;
private final IslandDeletion di;
private boolean inDelete;
private final BentoBox plugin;
private NMSAbstraction nms;
private final World netherWorld;
private final World endWorld;
private CompletableFuture<Void> currentTask = CompletableFuture.completedFuture(null);
public DeleteIslandChunks(BentoBox plugin, IslandDeletion di) {
this.plugin = plugin;
this.chunkX = di.getMinXChunk();
this.chunkZ = di.getMinZChunk();
this.di = di;
completed = new AtomicBoolean(false);
// Nether
if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) {
netherWorld = plugin.getIWM().getNetherWorld(di.getWorld());
@ -57,9 +61,8 @@ public class DeleteIslandChunks {
endWorld = null;
}
// NMS
try {
this.nms = Util.getNMS();
} catch (Exception e) {
this.nms = Util.getNMS();
if (nms == null) {
plugin.logError("Could not delete chunks because of NMS error");
return;
}
@ -73,55 +76,58 @@ public class DeleteIslandChunks {
private void regenerateChunks() {
// Run through all chunks of the islands and regenerate them.
task = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
if (inDelete) return;
inDelete = true;
if (!currentTask.isDone()) return;
if (isEnded(chunkX)) {
finish();
return;
}
List<CompletableFuture<Void>> newTasks = new ArrayList<>();
for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) {
boolean last = i == plugin.getSettings().getDeleteSpeed() -1;
if (isEnded(chunkX)) {
break;
}
final int x = chunkX;
final int z = chunkZ;
plugin.getIWM().getAddon(di.getWorld()).ifPresent(gm ->
// Overworld
processChunk(gm, di.getWorld(), x, z).thenRun(() ->
// Nether
processChunk(gm, netherWorld, x, z).thenRun(() ->
// End
processChunk(gm, endWorld, x, z).thenRun(() -> finish(last, x)))));
plugin.getIWM().getAddon(di.getWorld()).ifPresent(gm -> {
newTasks.add(processChunk(gm, di.getWorld(), x, z)); // Overworld
newTasks.add(processChunk(gm, netherWorld, x, z)); // Nether
newTasks.add(processChunk(gm, endWorld, x, z)); // End
});
chunkZ++;
if (chunkZ > di.getMaxZChunk()) {
chunkZ = di.getMinZChunk();
chunkX++;
}
}
currentTask = CompletableFuture.allOf(newTasks.toArray(new CompletableFuture[0]));
}, 0L, 20L);
}
private void finish(boolean last, int x) {
if (x > di.getMaxXChunk()) {
// Fire event
IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build();
// We're done
task.cancel();
}
if (last) {
inDelete = false;
}
private boolean isEnded(int chunkX) {
return chunkX > di.getMaxXChunk();
}
private CompletableFuture<Boolean> processChunk(GameModeAddon gm, World world, int x, int z) {
private void finish() {
// Fire event
IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build();
// We're done
completed.set(true);
task.cancel();
}
private CompletableFuture<Void> processChunk(GameModeAddon gm, World world, int x, int z) {
if (world != null) {
CompletableFuture<Boolean> r = new CompletableFuture<>();
PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk -> regenerateChunk(r, gm, chunk, x, z));
return r;
return PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk -> regenerateChunk(gm, chunk, x, z));
} else {
return CompletableFuture.completedFuture(null);
}
return CompletableFuture.completedFuture(false);
}
private void regenerateChunk(CompletableFuture<Boolean> r, GameModeAddon gm, Chunk chunk, int x, int z) {
private void regenerateChunk(GameModeAddon gm, Chunk chunk, int x, int z) {
// Clear all inventories
Arrays.stream(chunk.getTileEntities()).filter(te -> (te instanceof InventoryHolder))
.filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ()))
.forEach(te -> ((InventoryHolder)te).getInventory().clear());
Arrays.stream(chunk.getTileEntities()).filter(InventoryHolder.class::isInstance)
.filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ()))
.forEach(te -> ((InventoryHolder) te).getInventory().clear());
// Remove all entities
for (Entity e : chunk.getEntities()) {
if (!(e instanceof Player)) {
@ -133,30 +139,18 @@ public class DeleteIslandChunks {
ChunkGenerator cg = gm.getDefaultWorldGenerator(chunk.getWorld().getName(), "delete");
// Will be null if use-own-generator is set to true
if (cg != null) {
ChunkData cd = cg.generateChunkData(chunk.getWorld(), new Random(), chunk.getX(), chunk.getZ(), grid);
createChunk(cd, chunk, grid);
}
r.complete(true);
}
private void createChunk(ChunkData cd, Chunk chunk, MyBiomeGrid grid) {
int baseX = chunk.getX() << 4;
int baseZ = chunk.getZ() << 4;
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
if (di.inBounds(baseX + x, baseZ + z)) {
for (int y = 0; y < chunk.getWorld().getMaxHeight(); y++) {
nms.setBlockInNativeChunk(chunk, x, y, z, cd.getBlockData(x, y, z), false);
// 3D biomes, 4 blocks separated
if (x%4 == 0 && y%4 == 0 && z%4 == 0) {
chunk.getBlock(x, y, z).setBiome(grid.getBiome(x, y, z));
}
}
}
}
}
nms.copyChunkDataToChunk(chunk, cd, grid, di.getBox());
// Remove all entities in chunk, including any dropped items as a result of clearing the blocks above
Arrays.stream(chunk.getEntities()).filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())).forEach(Entity::remove);
}
public boolean isCompleted() {
return completed.get();
}
}

View File

@ -1,6 +1,5 @@
package world.bentobox.bentobox.util;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -65,6 +64,7 @@ public class Util {
private static final String THE_END = "_the_end";
private static String serverVersion = null;
private static BentoBox plugin = BentoBox.getInstance();
private static NMSAbstraction nms = null;
private Util() {}
@ -238,7 +238,7 @@ public class Util {
/**
* Convert world to an overworld
* @param world - world
* @return over world
* @return over world or null if world is null or a world cannot be found
*/
@Nullable
public static World getWorld(@Nullable World world) {
@ -284,21 +284,21 @@ public class Util {
*/
public static float blockFaceToFloat(BlockFace face) {
return switch (face) {
case EAST -> 90F;
case EAST_NORTH_EAST -> 67.5F;
case NORTH_EAST -> 45F;
case NORTH_NORTH_EAST -> 22.5F;
case NORTH_NORTH_WEST -> 337.5F;
case NORTH_WEST -> 315F;
case SOUTH -> 180F;
case SOUTH_EAST -> 135F;
case SOUTH_SOUTH_EAST -> 157.5F;
case SOUTH_SOUTH_WEST -> 202.5F;
case SOUTH_WEST -> 225F;
case WEST -> 270F;
case WEST_NORTH_WEST -> 292.5F;
case WEST_SOUTH_WEST -> 247.5F;
default -> 0F;
case EAST -> 90F;
case EAST_NORTH_EAST -> 67.5F;
case NORTH_EAST -> 45F;
case NORTH_NORTH_EAST -> 22.5F;
case NORTH_NORTH_WEST -> 337.5F;
case NORTH_WEST -> 315F;
case SOUTH -> 180F;
case SOUTH_EAST -> 135F;
case SOUTH_SOUTH_EAST -> 157.5F;
case SOUTH_SOUTH_WEST -> 202.5F;
case SOUTH_WEST -> 225F;
case WEST -> 270F;
case WEST_NORTH_WEST -> 292.5F;
case WEST_SOUTH_WEST -> 247.5F;
default -> 0F;
};
}
@ -548,21 +548,21 @@ public class Util {
if (group.length() == 6) {
// Parses #ffffff to a color text.
matcher.appendReplacement(buffer, ChatColor.COLOR_CHAR + "x"
+ ChatColor.COLOR_CHAR + group.charAt(0) + ChatColor.COLOR_CHAR + group.charAt(1)
+ ChatColor.COLOR_CHAR + group.charAt(2) + ChatColor.COLOR_CHAR + group.charAt(3)
+ ChatColor.COLOR_CHAR + group.charAt(4) + ChatColor.COLOR_CHAR + group.charAt(5));
+ ChatColor.COLOR_CHAR + group.charAt(0) + ChatColor.COLOR_CHAR + group.charAt(1)
+ ChatColor.COLOR_CHAR + group.charAt(2) + ChatColor.COLOR_CHAR + group.charAt(3)
+ ChatColor.COLOR_CHAR + group.charAt(4) + ChatColor.COLOR_CHAR + group.charAt(5));
} else {
// Parses #fff to a color text.
matcher.appendReplacement(buffer, ChatColor.COLOR_CHAR + "x"
+ ChatColor.COLOR_CHAR + group.charAt(0) + ChatColor.COLOR_CHAR + group.charAt(0)
+ ChatColor.COLOR_CHAR + group.charAt(1) + ChatColor.COLOR_CHAR + group.charAt(1)
+ ChatColor.COLOR_CHAR + group.charAt(2) + ChatColor.COLOR_CHAR + group.charAt(2));
+ ChatColor.COLOR_CHAR + group.charAt(0) + ChatColor.COLOR_CHAR + group.charAt(0)
+ ChatColor.COLOR_CHAR + group.charAt(1) + ChatColor.COLOR_CHAR + group.charAt(1)
+ ChatColor.COLOR_CHAR + group.charAt(2) + ChatColor.COLOR_CHAR + group.charAt(2));
}
}
// transform normal codes and strip spaces after color code.
return Util.stripSpaceAfterColorCodes(
ChatColor.translateAlternateColorCodes('&', matcher.appendTail(buffer).toString()));
ChatColor.translateAlternateColorCodes('&', matcher.appendTail(buffer).toString()));
}
@ -689,35 +689,25 @@ public class Util {
}
/**
* Checks what version the server is running and picks the appropriate NMS handler, or fallback
* @return an NMS accelerated class for this server, or a fallback Bukkit API based one
* @throws ClassNotFoundException - thrown if there is no fallback class - should never be thrown
* @throws SecurityException - thrown if security violation - should never be thrown
* @throws NoSuchMethodException - thrown if no constructor for NMS package
* @throws InvocationTargetException - should never be thrown
* @throws IllegalArgumentException - should never be thrown
* @throws IllegalAccessException - should never be thrown
* @throws InstantiationException - should never be thrown
* Set the NMS handler the plugin will use
* @param nms the NMS handler
*/
public static NMSAbstraction getNMS() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
String serverPackageName = Bukkit.getServer().getClass().getPackage().getName();
String pluginPackageName = plugin.getClass().getPackage().getName();
String version = serverPackageName.substring(serverPackageName.lastIndexOf('.') + 1);
Class<?> clazz;
try {
clazz = Class.forName(pluginPackageName + ".nms." + version + ".NMSHandler");
} catch (Exception e) {
plugin.logWarning("No NMS Handler found for " + version + ", falling back to Bukkit API.");
clazz = Class.forName(pluginPackageName + ".nms.fallback.NMSHandler");
}
// Check if we have a NMSAbstraction implementing class at that location.
if (NMSAbstraction.class.isAssignableFrom(clazz)) {
return (NMSAbstraction) clazz.getConstructor().newInstance();
} else {
throw new IllegalStateException("Class " + clazz.getName() + " does not implement NMSAbstraction");
}
public static void setNms(NMSAbstraction nms) {
Util.nms = nms;
}
/**
* Get the NMS handler the plugin will use
* @return an NMS accelerated class for this server
*/
public static NMSAbstraction getNMS() {
if (nms == null) {
plugin.log("No NMS Handler was set, falling back to Bukkit API.");
setNms(new world.bentobox.bentobox.nms.fallback.NMSHandler());
}
return nms;
}
/**
* Broadcast a localized message to all players with the permission {@link Server#BROADCAST_CHANNEL_USERS}
*

View File

@ -1,15 +1,6 @@
package world.bentobox.bentobox.util.teleport;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.World.Environment;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
@ -17,45 +8,51 @@ import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A class that calculates finds a safe spot asynchronously and then teleports the player there.
* @author tastybento
*
* @author tastybento
*/
public class SafeSpotTeleport {
private static final int MAX_CHUNKS = 6;
private static final long SPEED = 1;
private static final int MAX_RADIUS = 50;
private boolean notChecking;
private BukkitTask task;
// Parameters
private final Entity entity;
private final Location location;
private boolean portal;
private final int homeNumber;
// Locations
private Location bestSpot;
private final BentoBox plugin;
private List<Pair<Integer, Integer>> chunksToScan;
private final Runnable runnable;
private final Runnable failRunnable;
private final CompletableFuture<Boolean> result;
private final String homeName;
private final int maxHeight;
private final World world;
private final AtomicBoolean checking = new AtomicBoolean();
private BukkitTask task;
private boolean portal;
// Locations
private Location bestSpot;
private Iterator<Pair<Integer, Integer>> chunksToScanIterator;
private int checkedChunks = 0;
/**
* Teleports and entity to a safe spot on island
*
* @param builder - safe spot teleport builder
*/
SafeSpotTeleport(Builder builder) {
@ -68,12 +65,13 @@ public class SafeSpotTeleport {
this.runnable = builder.getRunnable();
this.failRunnable = builder.getFailRunnable();
this.result = builder.getResult();
this.maxHeight = location.getWorld().getMaxHeight() - 20;
this.world = Objects.requireNonNull(location.getWorld());
this.maxHeight = world.getMaxHeight() - 20;
// Try to go
Util.getChunkAtAsync(location).thenRun(()-> tryToGo(builder.getFailureMessage()));
Util.getChunkAtAsync(location).thenRun(() -> tryToGo(builder.getFailureMessage()));
}
private void tryToGo(String failureMessage) {
void tryToGo(String failureMessage) {
if (plugin.getIslands().isSafeLocation(location)) {
if (portal) {
// If the desired location is safe, then that's where you'll go if there's no portal
@ -88,44 +86,47 @@ public class SafeSpotTeleport {
}
}
// Get chunks to scan
chunksToScan = getChunksToScan();
// Start checking
notChecking = true;
chunksToScanIterator = getChunksToScan().iterator();
// Start a recurring task until done or cancelled
task = Bukkit.getScheduler().runTaskTimer(plugin, () -> gatherChunks(failureMessage), 0L, SPEED);
}
private void gatherChunks(String failureMessage) {
if (!notChecking) {
return;
boolean gatherChunks(String failureMessage) {
// Set a flag so this is only run if it's not already in progress
if (checking.get()) {
return false;
}
notChecking = false;
List<ChunkSnapshot> chunkSnapshot = new ArrayList<>();
Iterator<Pair<Integer, Integer>> it = chunksToScan.iterator();
if (!it.hasNext()) {
checking.set(true);
if (checkedChunks > MAX_CHUNKS || !chunksToScanIterator.hasNext()) {
// Nothing left
tidyUp(entity, failureMessage);
return;
return false;
}
// Add chunk snapshots to the list
while (it.hasNext() && chunkSnapshot.size() < MAX_CHUNKS) {
Pair<Integer, Integer> pair = it.next();
if (location.getWorld() != null) {
boolean isLoaded = location.getWorld().getChunkAt(pair.x, pair.z).isLoaded();
chunkSnapshot.add(location.getWorld().getChunkAt(pair.x, pair.z).getChunkSnapshot());
if (!isLoaded) {
location.getWorld().getChunkAt(pair.x, pair.z).unload();
}
// Get the chunk
Pair<Integer, Integer> chunkPair = chunksToScanIterator.next();
chunksToScanIterator.remove();
checkedChunks++;
if (checkedChunks >= MAX_CHUNKS) {
checking.set(false);
return false;
}
// Get the chunk snapshot and scan it
Util.getChunkAtAsync(world, chunkPair.x, chunkPair.z)
.thenApply(Chunk::getChunkSnapshot)
.whenCompleteAsync((snapshot, e) -> {
if (snapshot != null && scanChunk(snapshot)) {
task.cancel();
} else {
checking.set(false);
}
it.remove();
}
// Move to next step
checkChunks(chunkSnapshot);
});
return true;
}
private void tidyUp(Entity entity, String failureMessage) {
void tidyUp(Entity entity, String failureMessage) {
// Still Async!
// Nothing left to check and still not canceled
task.cancel();
@ -133,7 +134,7 @@ public class SafeSpotTeleport {
if (portal && bestSpot != null) {
// Portals found, teleport to the best spot we found
teleportEntity(bestSpot);
} else if (entity instanceof Player) {
} else if (entity instanceof Player player) {
// Return to main thread and teleport the player
Bukkit.getScheduler().runTask(plugin, () -> {
// Failed, no safe spot
@ -142,15 +143,15 @@ public class SafeSpotTeleport {
}
if (!plugin.getIWM().inWorld(entity.getLocation())) {
// Last resort
((Player)entity).performCommand("spawn");
player.performCommand("spawn");
} else {
// Create a spot for the player to be
if (location.getWorld().getEnvironment().equals(Environment.NETHER)) {
makeAndTelport(Material.NETHERRACK);
} else if (location.getWorld().getEnvironment().equals(Environment.THE_END)) {
makeAndTelport(Material.END_STONE);
if (world.getEnvironment().equals(Environment.NETHER)) {
makeAndTeleport(Material.NETHERRACK);
} else if (world.getEnvironment().equals(Environment.THE_END)) {
makeAndTeleport(Material.END_STONE);
} else {
makeAndTelport(Material.COBBLESTONE);
makeAndTeleport(Material.COBBLESTONE);
}
}
if (failRunnable != null) {
@ -166,7 +167,7 @@ public class SafeSpotTeleport {
}
}
private void makeAndTelport(Material m) {
void makeAndTeleport(Material m) {
location.getBlock().getRelative(BlockFace.DOWN).setType(m, false);
location.getBlock().setType(Material.AIR, false);
location.getBlock().getRelative(BlockFace.UP).setType(Material.AIR, false);
@ -179,20 +180,21 @@ public class SafeSpotTeleport {
/**
* Gets a set of chunk coords that will be scanned.
*
* @return - list of chunk coords to be scanned
*/
private List<Pair<Integer, Integer>> getChunksToScan() {
List<Pair<Integer, Integer>> getChunksToScan() {
List<Pair<Integer, Integer>> chunksToScan = new ArrayList<>();
int maxRadius = plugin.getIslands().getIslandAt(location).map(Island::getProtectionRange).orElseGet(() -> plugin.getIWM().getIslandProtectionRange(location.getWorld()));
int maxRadius = plugin.getIslands().getIslandAt(location).map(Island::getProtectionRange).orElseGet(() -> plugin.getIWM().getIslandProtectionRange(world));
maxRadius = Math.min(MAX_RADIUS, maxRadius);
int x = location.getBlockX();
int z = location.getBlockZ();
// Create ever increasing squares around the target location
int radius = 0;
do {
for (int i = x - radius; i <= x + radius; i+=16) {
for (int j = z - radius; j <= z + radius; j+=16) {
addChunk(chunksToScan, new Pair<>(i,j), new Pair<>(i >> 4, j >> 4));
for (int i = x - radius; i <= x + radius; i += 16) {
for (int j = z - radius; j <= z + radius; j += 16) {
addChunk(chunksToScan, new Pair<>(i, j), new Pair<>(i >> 4, j >> 4));
}
}
radius++;
@ -206,48 +208,66 @@ public class SafeSpotTeleport {
}
}
/**
* Loops through the chunks and if a safe spot is found, fires off the teleportation
* @param chunkSnapshot - list of chunk snapshots to check
*/
private void checkChunks(final List<ChunkSnapshot> chunkSnapshot) {
// Run async task to scan chunks
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
for (ChunkSnapshot chunk: chunkSnapshot) {
if (scanChunk(chunk)) {
task.cancel();
return;
}
}
// Nothing happened, change state
notChecking = true;
});
}
/**
* @param chunk - chunk snapshot
* @return true if a safe spot was found
*/
private boolean scanChunk(ChunkSnapshot chunk) {
// Run through the chunk
for (int x = 0; x< 16; x++) {
boolean scanChunk(ChunkSnapshot chunk) {
int startY = location.getBlockY();
int minY = world.getMinHeight();
int maxY = 60; // Just a dummy value
// Check the safe spot at the current height
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
// Work down from the entry point up
for (int y = Math.min(chunk.getHighestBlockYAt(x, z), maxHeight); y >= 0; y--) {
if (checkBlock(chunk, x,y,z)) {
if (minY >= startY && checkBlock(chunk, x, startY, z)) {
return true;
}
maxY = Math.max(chunk.getHighestBlockYAt(x, z), maxY);
}
}
maxY = Math.min(maxY, maxHeight);
// Expand the height up and down until a safe spot is found
int upperY = startY + 1;
int lowerY = startY - 1;
boolean checkUpper = upperY <= maxY;
boolean checkLower = lowerY >= minY;
int limitRange = plugin.getSettings().getSafeSpotSearchVerticalRange(); // Limit the y-coordinate range
while (limitRange > 0 && (checkUpper || checkLower)) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
if (checkUpper && checkBlock(chunk, x, upperY, z)) {
return true;
}
} // end y
} //end z
} // end x
if (checkLower && checkBlock(chunk, x, lowerY, z)) {
return true;
}
}
}
if (checkUpper) {
upperY++;
if (upperY > maxY) {
checkUpper = false;
}
}
if (checkLower) {
lowerY--;
if (lowerY < minY) {
checkLower = false;
}
}
limitRange--;
}
// We can't find a safe spot
return false;
}
/**
* Teleports entity to the safe spot
*/
private void teleportEntity(final Location loc) {
void teleportEntity(final Location loc) {
task.cancel();
// Return to main thread and teleport the player
Bukkit.getScheduler().runTask(plugin, () -> {
@ -264,14 +284,14 @@ public class SafeSpotTeleport {
/**
* Returns true if the location is a safe one.
*
* @param chunk - chunk snapshot
* @param x - x coordinate
* @param y - y coordinate
* @param z - z coordinate
* @param x - x coordinate
* @param y - y coordinate
* @param z - z coordinate
* @return true if this is a safe spot, false if this is a portal scan
*/
boolean checkBlock(ChunkSnapshot chunk, int x, int y, int z) {
World world = location.getWorld();
Material type = chunk.getBlockType(x, y, z);
Material space1 = chunk.getBlockType(x, Math.min(y + 1, maxHeight), z);
Material space2 = chunk.getBlockType(x, Math.min(y + 2, maxHeight), z);
@ -285,7 +305,7 @@ public class SafeSpotTeleport {
return false;
}
private boolean safe(ChunkSnapshot chunk, int x, int y, int z, World world) {
boolean safe(ChunkSnapshot chunk, int x, int y, int z, World world) {
Vector newSpot = new Vector((chunk.getX() << 4) + x + 0.5D, y + 1.0D, (chunk.getZ() << 4) + z + 0.5D);
if (portal) {
if (bestSpot == null) {
@ -301,6 +321,7 @@ public class SafeSpotTeleport {
public static class Builder {
private final BentoBox plugin;
private final CompletableFuture<Boolean> result = new CompletableFuture<>();
private Entity entity;
private int homeNumber = 0;
private String homeName = "";
@ -309,7 +330,6 @@ public class SafeSpotTeleport {
private Location location;
private Runnable runnable;
private Runnable failRunnable;
private final CompletableFuture<Boolean> result = new CompletableFuture<>();
public Builder(BentoBox plugin) {
this.plugin = plugin;
@ -317,6 +337,7 @@ public class SafeSpotTeleport {
/**
* Set who or what is going to teleport
*
* @param entity entity to teleport
* @return Builder
*/
@ -327,6 +348,7 @@ public class SafeSpotTeleport {
/**
* Set the island to teleport to
*
* @param island island destination
* @return Builder
*/
@ -337,6 +359,7 @@ public class SafeSpotTeleport {
/**
* Set the home number to this number
*
* @param homeNumber home number
* @return Builder
* @deprecated use {@link #homeName}
@ -349,6 +372,7 @@ public class SafeSpotTeleport {
/**
* Set the home name
*
* @param homeName - home name
* @return Builder
* @since 1.16.0
@ -360,6 +384,7 @@ public class SafeSpotTeleport {
/**
* This is a portal teleportation
*
* @return Builder
*/
public Builder portal() {
@ -369,6 +394,7 @@ public class SafeSpotTeleport {
/**
* Set the failure message if this teleport cannot happen
*
* @param failureMessage failure message to report to user
* @return Builder
*/
@ -379,6 +405,7 @@ public class SafeSpotTeleport {
/**
* Set the desired location
*
* @param location the location
* @return Builder
*/
@ -389,6 +416,7 @@ public class SafeSpotTeleport {
/**
* Try to teleport the player
*
* @return CompletableFuture that will become true if successful and false if not
* @since 1.14.0
*/
@ -400,6 +428,7 @@ public class SafeSpotTeleport {
/**
* Try to teleport the player
*
* @return SafeSpotTeleport
*/
@Nullable
@ -415,6 +444,11 @@ public class SafeSpotTeleport {
result.complete(null);
return null;
}
if (location.getWorld() == null) {
plugin.logError("Attempt to safe teleport to a null world!");
result.complete(null);
return null;
}
if (failureMessage.isEmpty() && entity instanceof Player) {
failureMessage = "general.errors.no-safe-location-found";
}
@ -423,6 +457,7 @@ public class SafeSpotTeleport {
/**
* The task to run after the player is safely teleported.
*
* @param runnable - task
* @return Builder
* @since 1.13.0
@ -434,14 +469,16 @@ public class SafeSpotTeleport {
/**
* The task to run if the player is not safely teleported
*
* @param runnable - task
* @return Builder
* @since 1.18.0
*/
public Builder ifFail(Runnable rannable) {
public Builder ifFail(Runnable runnable) {
this.failRunnable = runnable;
return this;
}
/**
* @return the plugin
*/

View File

@ -75,8 +75,6 @@ public class ServerCompatibility {
GLOWSTONE(Compatibility.INCOMPATIBLE),
SPIGOT(Compatibility.COMPATIBLE),
PAPER(Compatibility.SUPPORTED),
TUINITY(Compatibility.SUPPORTED),
AIRPLANE(Compatibility.SUPPORTED),
PURPUR(Compatibility.SUPPORTED),
TACOSPIGOT(Compatibility.NOT_SUPPORTED),
AKARIN(Compatibility.NOT_SUPPORTED),
@ -187,7 +185,15 @@ public class ServerCompatibility {
/**
* @since 1.17.1
*/
V1_17_1(Compatibility.INCOMPATIBLE)
V1_17_1(Compatibility.INCOMPATIBLE),
/**
* @since 1.19.0
*/
V1_18(Compatibility.INCOMPATIBLE),
/**
* @since 1.19.0
*/
V1_18_1(Compatibility.INCOMPATIBLE),
;
private final Compatibility compatibility;

View File

@ -195,6 +195,20 @@ island:
# This is the default behaviour.
# Added since 1.13.0.
keep-previous-island-on-reset: false
# Toggles how the islands are deleted.
# * If set to 'false', all islands will be deleted at once.
# This is fast but may cause an impact on the performance
# as it'll load all the chunks of the in-deletion islands.
# * If set to 'true', the islands will be deleted one by one.
# This is slower but will not cause any impact on the performance.
# Added since 1.19.1.
slow-deletion: false
# By default, If the destination is not safe, the plugin will try to search for a safe spot around the destination,
# then it will try to expand the y-coordinate up and down from the destination.
# This setting limits how far the y-coordinate will be expanded.
# If set to 0 or lower, the plugin will not expand the y-coordinate.
# Added since 1.19.1.
safe-spot-search-vertical-range: 400
web:
github:
# Toggle whether BentoBox can connect to GitHub to get data about updates and addons.

View File

@ -1373,7 +1373,7 @@ successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &7 od &a tastybento &7 a &a Poslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &b v&e [version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8 Načteno v &e [time]&8 ms.

File diff suppressed because it is too large Load Diff

View File

@ -391,6 +391,10 @@ commands:
description: "deletes a player's island"
cannot-delete-owner: "&c All island members have to be kicked from the island before deleting it."
deleted-island: "&a Island at &e [xyz] &a has been successfully deleted."
deletehomes:
parameters: "<player>"
description: "deletes all named homes from an island"
warning: "&c All named homes will be deleted from the island!"
why:
parameters: "<player>"
description: "toggle console protection debug reporting"
@ -811,6 +815,12 @@ protection:
name: "Barrels"
description: "Toggle barrel interaction"
hint: "Barrel access disabled"
BLOCK_EXPLODE_DAMAGE:
description: |-
&a Allow Bed & Respawn Anchors
&a to break blocks and damage
&a entities.
name: "Block explode damage"
COMPOSTER:
name: "Composters"
description: "Toggle composter interaction"
@ -1316,6 +1326,12 @@ protection:
description: |-
&a If active, withers can
&a damage blocks and players
WORLD_BLOCK_EXPLODE_DAMAGE:
description: |-
&a Allow Bed & Respawn Anchors
&a to break blocks and damage
&a entities outside of island limits.
name: "World block explode damage"
WORLD_TNT_DAMAGE:
description: |-
&a Allow TNT and TNT minecarts
@ -1563,7 +1579,7 @@ successfully-loaded: |
&6 ____ _ ____
&6 | _ \ | | | _ \ &7 by &a tastybento &7 and &a Poslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &b v&e [version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8 Loaded in &e [time]&8 ms.

View File

@ -1460,7 +1460,7 @@ panel:
successfully-loaded: |-
&6 ____ _ ____
&6 | _ \ | | | _ \ &7Por &atastybento &7y &aPoslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ / &7Traducido por &aSrAcosta
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &bv&e[version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8Recargado en &e[time]&8ms.

View File

@ -1437,7 +1437,7 @@ ranks:
successfully-loaded: |-
&6 ____ _ ____
&6 | _ \ | | | _ \ &7 par &a tastybento &7 et &a Poslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &b v&e [version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8 Chargé en &e [time]&8 ms.

View File

@ -1392,7 +1392,7 @@ successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &7by &atastybento &7and &aPoslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &bv&e[version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8Caricato in &e[time]&8ms.

View File

@ -1476,7 +1476,7 @@ panel:
successfully-loaded: |-
&6 ____ _ ____
&6 | _ \ | | | _ \ &7by &atastybento &7and &aPoslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &bv&e[version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8Loaded in &e[time]&8ms.

View File

@ -1358,7 +1358,7 @@ successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &7 by &a tastybento &7 and &a Poslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &b v&e [version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8 Loaded in &e [time]&8 ms.

View File

@ -1441,7 +1441,7 @@ successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &7veidoja &atastybento &7un &aPoslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &bv&e[version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8Ielādēts &e[time]&8ms.

View File

@ -1,12 +1,14 @@
---
meta:
banner: WHITE_BANNER
authors:
- tastybento
-
banner: WHITE_BANNER
prefixes:
bentobox: "&6 BentoBox &7 &l> &r"
general:
success: "&a Succes!"
invalid: Ongeldig
errors:
command-cancelled: "&c Commando geannuleerd."
no-permission: "&c U heeft geen toestemming om deze opdracht uit te voeren (&7
@ -29,10 +31,9 @@ general:
general: "&c Dat commando is nog niet klaar - neem contact op met de beheerder"
unknown-command: "&c Onbekend commando. Doe &b /[label] help &c voor hulp."
wrong-world: "&c Je bent niet in de juiste wereld om dat te doen!"
you-must-wait: "&c Je moet [number]seconden wachten voordat je dat commando
opnieuw kunt doen."
you-must-wait: "&c Je moet [number]seconden wachten voordat je dat commando opnieuw
kunt doen."
must-be-positive-number: "&c [number] is geen geldig positief getal."
invalid: Ongeldig
worlds:
overworld: Bovenwereld
nether: Nether
@ -59,26 +60,26 @@ commands:
reset:
description: reset hoe vaak deze speler zijn eiland terugzet op 0
parameters: "<speler>"
success-everyone: "&a Succesvol gereset &b iedereen &a 's resetten naar
&b 0 &a."
success-everyone: "&a Succesvol gereset &b iedereen &a 's resetten naar &b
0 &a."
success: "&a Succesvol gereset &b [name] &a 's resetten naar &b 0 &a."
add:
description: telt op bij het aantal keren dat deze speler zijn eiland reset
parameters: "<speler> <resets>"
success: "&a Met succes toegevoegd &b [number] &a wordt gereset naar &
b [name], waardoor het totaal wordt verhoogd naar &b [total] &a wordt
gereset."
success: "&a Met succes toegevoegd &b [number] &a wordt gereset naar & b [name],
waardoor het totaal wordt verhoogd naar &b [total] &a wordt gereset."
remove:
description: verwijdert uit hoe vaak deze speler zijn eiland reset
parameters: "<speler> <resets>"
success: "&a Succesvol verwijderd &b [number] &a wordt gereset naar &b
[name], waardoor het totaal wordt verlaagd naar &b [total] &a wordt gereset."
success: "&a Succesvol verwijderd &b [number] &a wordt gereset naar &b [name],
waardoor het totaal wordt verlaagd naar &b [total] &a wordt gereset."
purge:
parameters: "[days]"
description: eilanden zuiveren die al meer dan [days] zijn verlaten
days-one-or-more: Moet minimaal 1 dag of langer duren
purgable-islands: "&a Gevonden &b [number]&a zuiverbare eilanden."
purge-in-progress: "&c Bezig met spoelen. Gebruik &b /[label] purge stop
&c om te annuleren."
purge-in-progress: "&c Bezig met spoelen. Gebruik &b /[label] purge stop &c
om te annuleren."
number-error: "&c Argument moet een aantal dagen zijn"
confirm: "&d Typ &b /[label] purge confirm &d om te beginnen met purgeren"
completed: "&a zuivering gestopt."
@ -98,9 +99,8 @@ commands:
unowned-islands: "&a Found &b [number]&a eilanden zonder eigendom."
status:
description: geeft de status van de zuivering weer
status: "&b [purged] &a eilanden gezuiverd uit &b [purgeable] &7 (&
b [percentage]% &7) &a."
days-one-or-more: Moet minimaal 1 dag of langer duren
status: "&b [purged] &a eilanden gezuiverd uit &b [purgeable] &7 (& b [percentage]%
&7) &a."
team:
add:
parameters: "<eigenaar> <speler>"
@ -115,6 +115,16 @@ commands:
use-disband-owner: "&c Geen eigenaar! Gebruik ontbinden [owner]."
disbanded: "&c Admin heeft je team ontbonden!"
success: "&b [name] &a 's team is opgeheven."
fix:
description: scant en repareert het lidmaatschap van meerdere eilanden in
de database
scanning: Database scannen ...
duplicate-owner: "&c Speler bezit meer dan één eiland in de database: [name]"
player-has: "&c Speler [name] heeft [number]eilanden"
duplicate-member: "&c Speler [name] is lid van meer dan één eiland in de database"
rank-on-island: "&c [rank] op het eiland op [xyz]"
fixed: "&a vaste"
done: "&a scan"
kick:
parameters: "<teamspeler>"
description: schop een speler uit een team
@ -127,17 +137,6 @@ commands:
description: draagt het eilandbezit over aan de speler
already-owner: "&c [name] is al de eigenaar van dit eiland!"
success: "&b [name] &a is nu de eigenaar van dit eiland."
fix:
description: scant en repareert het lidmaatschap van meerdere eilanden in
de database
scanning: Database scannen ...
duplicate-owner: "&c Speler bezit meer dan één eiland in de database: [name]"
player-has: "&c Speler [name] heeft [number]eilanden"
fixed: "&a vaste"
done: "&a scan"
duplicate-member: "&c Speler [name] is lid van meer dan één eiland in de
database"
rank-on-island: "&c [rank] op het eiland op [xyz]"
range:
description: admin eilandbereik commando
invalid-value:
@ -159,8 +158,7 @@ commands:
set:
parameters: "<speler> <bereik>"
description: stelt het beschermde eilandbereik in
success: "&a Stel het bereik van de eilandbescherming in op &b [number]
&a."
success: "&a Stel het bereik van de eilandbescherming in op &b [number] &a."
reset:
parameters: "<speler>"
description: zet het beschermde eilandbereik terug naar de wereldstandaard
@ -168,13 +166,13 @@ commands:
add:
description: vergroot het beschermde bereik van het eiland
parameters: "<speler> <bereik>"
success: "&a Met succes het beschermde bereik van &b [name] &a 's op het
eiland vergroot tot &b [total] &7 (&b + [number]&7) &a."
success: "&a Met succes het beschermde bereik van &b [name] &a 's op het eiland
vergroot tot &b [total] &7 (&b + [number]&7) &a."
remove:
description: verkleint het beschermde bereik van het eiland
parameters: "<speler> <bereik>"
success: "&a Succesvol verkleind &b [name] &a 's beschermde eilandbereik
naar &b [total] &7 (&b - [number]&7) &a."
success: "&a Succesvol verkleind &b [name] &a 's beschermde eilandbereik naar
&b [total] &7 (&b - [number]&7) &a."
register:
parameters: "<speler>"
description: registreer speler op het eiland waar je je bevindt
@ -206,6 +204,8 @@ commands:
team-members-title: 'Leden van het team:'
team-owner-format: "&a [name] [rank]"
team-member-format: "&b [name] [rank]"
island-protection-center: 'Beschermingsgebied centrum: [xyz]'
island-center: 'Eilandcentrum: [xyz]'
island-coords: 'Eilandcoördinaten: [xz1] tot [xz2]'
islands-in-trash: "&d Player heeft eilanden in de prullenbak."
protection-range: 'Beschermingsbereik: [range]'
@ -216,12 +216,9 @@ commands:
banned-players: 'Verbannen spelers:'
banned-format: "&c [name]"
unowned: "&c Onbeheerd"
island-protection-center: 'Beschermingsgebied centrum: [xyz]'
island-center: 'Eilandcentrum: [xyz]'
switch:
description: bescherming bypass in- / uitschakelen
op: "&c Ops kunnen de bescherming altijd omzeilen. Deop om het commando te
gebruiken."
op: "&c Ops kunnen de bescherming altijd omzeilen. Deop om het commando te gebruiken."
removing: Bypass verwijderen ...
adding: Beveiligingsbypass toevoegen ...
switchto:
@ -240,10 +237,10 @@ commands:
de prullenbak
title: "&d =========== Eilanden in prullenbak ==========="
count: "&l &d Island [number]:"
use-switch: "&a Gebruik &l [label] switcht naar <player> <nummer> &r &a
om speler naar eiland in prullenbak te schakelen"
use-emptytrash: "&a Gebruik &l [label] lege prullenbak [player] &r &a om
prullenbakitems permanent te verwijderen"
use-switch: "&a Gebruik &l [label] switcht naar <player> <nummer> &r &a om speler
naar eiland in prullenbak te schakelen"
use-emptytrash: "&a Gebruik &l [label] lege prullenbak [player] &r &a om prullenbakitems
permanent te verwijderen"
emptytrash:
parameters: "[speler]"
description: Wis de prullenbak voor de speler of verwijder alle eilanden die
@ -273,14 +270,26 @@ commands:
eiland van de eigenaar
unknown-rank: "&c Onbekende rang!"
not-possible: "&c Rang moet hoger zijn dan bezoeker."
rank-set: "&a Rang ingesteld van &b [from] &a naar &b [to] &a op &b
[name] &a 's eiland."
rank-set: "&a Rang ingesteld van &b [from] &a naar &b [to] &a op &b [name] &a
's eiland."
setprotectionlocation:
parameters: "[x y z coördinaten]"
description: stel de huidige locatie in of [x y z] als centrum van het beschermde
gebied van het eiland
island: "&c Dit heeft gevolgen voor het eiland op [xyz] dat eigendom is van
'[name]'."
confirmation: "&c Weet u zeker dat u [xyz] wilt instellen als het beschermingscentrum?"
success: "&a [xyz] met succes ingesteld als het beschermingscentrum."
fail: "&a Kan [xyz] niet instellen als het beschermingscentrum."
island-location-changed: "&a [user] heeft het beschermingscentrum van het eiland
gewijzigd in [xyz]."
xyz-error: "&c Specificeer drie integer-coördinaten: bijvoorbeeld 100120100"
setspawn:
description: stel een eiland in als spawn voor deze spelmodus
already-spawn: "&c Dit eiland is al een spawn!"
no-island-here: "&c Er is hier geen eiland."
confirmation: "&c Weet je zeker dat je dit eiland wilt instellen als spawn
voor deze wereld?"
confirmation: "&c Weet je zeker dat je dit eiland wilt instellen als spawn voor
deze wereld?"
success: "&a Stel dit eiland met succes in als de spawn voor deze wereld."
setspawnpoint:
description: stel de huidige locatie in als spawn-punt voor dit eiland
@ -288,8 +297,7 @@ commands:
confirmation: "&c Weet u zeker dat u deze locatie wilt instellen als het spawn-punt
voor dit eiland?"
success: "&a Stel deze locatie met succes in als het spawn-punt voor dit eiland."
island-spawnpoint-changed: "&a [user] heeft dit spawnpunt op het eiland
gewijzigd."
island-spawnpoint-changed: "&a [user] heeft dit spawnpunt op het eiland gewijzigd."
settings:
parameters: "[speler] / [wereldvlag] / spawn-island [vlag / active / disable]
[rang / active / disable]"
@ -348,8 +356,7 @@ commands:
rename:
parameters: "<blauwdruknaam> <nieuwe naam>"
description: hernoem een blauwdruk
success: "&a Blauwdruk &b [old] &a is met succes hernoemd naar &b [name]
&a."
success: "&a Blauwdruk &b [old] &a is met succes hernoemd naar &b [name] &a."
pick-different-name: "&c Geef een naam op die verschilt van de huidige naam
van de blueprint."
management:
@ -364,9 +371,13 @@ commands:
Plaats blauwdruk
naar rechts om in te stellen
trash: Prullenbak
no-trash: Kan niet weggooien
trash-instructions: Klik hier met de rechtermuisknop om te verwijderen
no-trash-instructions: Kan standaardbundel niet weggooien
permission: Toestemming
no-permission: Geen toestemming
perm-required: Verplicht
no-perm-required: Kan geen permissie instellen voor standaardbundel
perm-not-required: Niet verplicht
perm-format: "&e"
remove: Klik met de rechtermuisknop om te verwijderen
@ -395,10 +406,6 @@ commands:
slot-instructions: |
&a linkermuisknop om te verhogen
&a Klik met de rechtermuisknop om te verlagen
no-trash: Kan niet weggooien
no-trash-instructions: Kan standaardbundel niet weggooien
no-permission: Geen toestemming
no-perm-required: Kan geen permissie instellen voor standaardbundel
resetflags:
parameters: "[vlag]"
description: Reset alle eilanden naar de standaard vlaginstellingen in config.yml
@ -413,6 +420,7 @@ commands:
cannot-delete-owner: "&c Alle eilandleden moeten van het eiland worden verwijderd
voordat ze het kunnen verwijderen."
deleted-island: "&a Island at &e [xyz] &a is succesvol verwijderd."
deletehomes: {}
why:
parameters: "<speler>"
description: schakel foutopsporingsrapportage voor consolebeveiliging in
@ -423,35 +431,22 @@ commands:
reset:
description: reset de dood van de speler
parameters: "<speler>"
success: "&a Met succes &b [name] &a 's sterfgevallen gereset naar &b
0 &a."
success: "&a Met succes &b [name] &a 's sterfgevallen gereset naar &b 0 &a."
set:
description: stelt de dood van de speler in
parameters: "<speler> <doden>"
success: "&a De sterfgevallen van &b [name] &a zijn succesvol ingesteld
op &b [number] &a."
success: "&a De sterfgevallen van &b [name] &a zijn succesvol ingesteld op
&b [number] &a."
add:
description: voegt sterfgevallen toe aan de speler
parameters: "<speler> <doden>"
success: "&a Met succes &b [number]&a sterfgevallen toegevoegd aan &b
[name], waardoor het totaal is verhoogd naar &b [total] &a sterfgevallen."
success: "&a Met succes &b [number]&a sterfgevallen toegevoegd aan &b [name],
waardoor het totaal is verhoogd naar &b [total] &a sterfgevallen."
remove:
description: verwijdert sterfgevallen voor de speler
parameters: "<speler> <doden>"
success: "&a Met succes verwijderd &b [number]&a sterfgevallen aan &b
[name], waardoor het totaal afneemt tot &b [total] &a sterfgevallen."
setprotectionlocation:
parameters: "[x y z coördinaten]"
description: stel de huidige locatie in of [x y z] als centrum van het beschermde
gebied van het eiland
island: "&c Dit heeft gevolgen voor het eiland op [xyz] dat eigendom is van
'[name]'."
confirmation: "&c Weet u zeker dat u [xyz] wilt instellen als het beschermingscentrum?"
success: "&a [xyz] met succes ingesteld als het beschermingscentrum."
fail: "&a Kan [xyz] niet instellen als het beschermingscentrum."
island-location-changed: "&a [user] heeft het beschermingscentrum van
het eiland gewijzigd in [xyz]."
xyz-error: "&c Specificeer drie integer-coördinaten: bijvoorbeeld 100120100"
success: "&a Met succes verwijderd &b [number]&a sterfgevallen aan &b [name],
waardoor het totaal afneemt tot &b [total] &a sterfgevallen."
bentobox:
description: BentoBox admin-opdracht
about:
@ -529,8 +524,7 @@ commands:
creating-island: "&a Een plek vinden voor uw eiland ..."
pasting:
estimated-time: "&a Geschatte tijd: &b [number]&a seconden."
blocks: "&a Building it blok voor blok: &b [number] &a blokken in totaal
..."
blocks: "&a Building it blok voor blok: &b [number] &a blokken in totaal ..."
entities: "&a Het vullen met entiteiten: &b [number]&a entiteiten in totaal
..."
done: "&a Klaar! Je eiland staat klaar en wacht op je!"
@ -540,6 +534,11 @@ commands:
het voorbereiden van uw eiland."
you-can-teleport-to-your-island: "&a Je kunt naar je eiland teleporteren wanneer
je maar wilt."
deletehome:
description: een thuislocatie verwijderen
parameters: "[naam huis]"
homes:
description: lijst uw huizen
info:
description: informatie weergeven over uw eiland of het eiland van de speler
parameters: "<speler>"
@ -561,12 +560,15 @@ commands:
&c Weet u zeker dat u dit wilt doen?
&c Alle eilandleden worden van het eiland getrapt, je zult ze daarna opnieuw moeten uitnodigen.
&c Er is geen weg meer terug: als je huidige eiland eenmaal is verwijderd, is er &l geen &r &c manier om het later terug te halen.
kicked-from-island: "&c Je wordt van je eiland getrapt in [gamemode] omdat
de eigenaar het aan het resetten is."
kicked-from-island: "&c Je wordt van je eiland getrapt in [gamemode] omdat de
eigenaar het aan het resetten is."
sethome:
description: stel uw thuisteleporteerpunt in
must-be-on-your-island: "&c Je moet op je eiland zijn om naar huis te gaan!"
too-many-homes: "&c Kan niet instellen - je eiland heeft maximaal [number] huizen."
home-set: "&6 Uw eilandhuis is ingesteld op uw huidige locatie."
homes-are: "&6 eilandwoningen zijn:"
home-list-syntax: "&6 [name]"
nether:
not-allowed: "&c Het is niet toegestaan om uw huis in Nederland te plaatsen."
confirmation: "&c Weet u zeker dat u uw huis in de Nether wilt plaatsen?"
@ -574,10 +576,6 @@ commands:
not-allowed: "&c Je mag je huis niet op het einde zetten."
confirmation: "&c Weet u zeker dat u uw huis op het einde wilt zetten?"
parameters: "[naam huis]"
too-many-homes: "&c Kan niet instellen - je eiland heeft maximaal [number]
huizen."
homes-are: "&6 eilandwoningen zijn:"
home-list-syntax: "&6 [name]"
setname:
description: stel een naam voor je eiland in
name-too-short: "&c Te kort. Minimale grootte is [number]tekens."
@ -585,6 +583,11 @@ commands:
name-already-exists: "&c Er is al een eiland met die naam in deze spelmodus."
parameters: "<naam>"
success: "&a Stel de naam van je eiland succesvol in op &b [name] &a."
renamehome:
description: de naam van een thuislocatie wijzigen
parameters: "[naam huis]"
enter-new-name: "&6 Voer de nieuwe naam in"
already-exists: "&c Die naam bestaat al, probeer een andere naam."
resetname:
description: reset je eilandnaam
success: "&a Reset je eilandnaam succesvol."
@ -654,8 +657,8 @@ commands:
removing-invite: "&c Uitnodiging verwijderen."
name-has-invited-you: "&a [name] heeft je uitgenodigd om lid te worden van
hun eiland."
to-accept-or-reject: "&a Do /[label] team accepteren om te accepteren,
of /[label] team weigeren om te weigeren"
to-accept-or-reject: "&a Do /[label] team accepteren om te accepteren, of
/[label] team weigeren om te weigeren"
you-will-lose-your-island: "&c WAARSCHUWING! Je verliest je eiland als je
accepteert!"
errors:
@ -671,8 +674,8 @@ commands:
you-can-invite: "&a Je kunt nog [number]spelers uitnodigen."
accept:
description: accepteer een uitnodiging
you-joined-island: "&a Je bent lid geworden van een eiland! Gebruik &b
/[label] team &a om de andere leden te zien."
you-joined-island: "&a Je bent lid geworden van een eiland! Gebruik &b /[label]
team &a om de andere leden te zien."
name-joined-your-island: "&a [name] kwam bij je eiland!"
confirmation: |-
&c Weet u zeker dat u deze uitnodiging wilt accepteren?
@ -759,16 +762,6 @@ commands:
not-on-island: "&c Die speler is niet op jouw eiland!"
player-expelled-you: "&b [name] &c hebben je van het eiland verdreven!"
success: "&a Je hebt &b [name] &a van het eiland verdreven."
deletehome:
description: een thuislocatie verwijderen
parameters: "[naam huis]"
renamehome:
description: de naam van een thuislocatie wijzigen
parameters: "[naam huis]"
enter-new-name: "&6 Voer de nieuwe naam in"
already-exists: "&c Die naam bestaat al, probeer een andere naam."
homes:
description: lijst uw huizen
ranks:
owner: Eigenaar
sub-owner: Ondereigenaar
@ -856,6 +849,13 @@ protection:
&7 Andere containers worden behandeld
&7 door speciale vlaggen.
hint: Containertoegang uitgeschakeld
CHEST: {}
BARREL: {}
BLOCK_EXPLODE_DAMAGE: {}
COMPOSTER: {}
FLOWER_POT: {}
SHULKER_BOX: {}
TRAPPED_CHEST: {}
DISPENSER:
name: Dispensers
description: Wissel de interactie tussen de dispenser
@ -1026,6 +1026,10 @@ protection:
&a buitenkant beschermd
&a eilandruimte
name: "&e Beperk mobs tot het eiland"
HIVE:
description: "&a Toggle bijenkorf oogsten."
name: Bijenkorf oogsten
hint: Oogsten uitgeschakeld
HURT_ANIMALS:
description: Schakel pijn uit
name: Gekwetste dieren
@ -1186,6 +1190,13 @@ protection:
&a Kan vertraging helpen verminderen.
&a Heeft geen invloed op het spawn-eiland.
name: Offline Redstone
PETS_STAY_AT_HOME:
description: |-
&a Bij actieve, getemde huisdieren
&a kan alleen naar en gaan
&a kan de eigenaar niet verlaten
&a thuiseiland.
name: Huisdieren blijven thuis
PISTON_PUSH:
description: |-
&a Schakel dit in om te voorkomen
@ -1322,28 +1333,19 @@ protection:
&a terug naar hun eiland met behulp van commando's
&a als ze vallen.
hint: "&c Dat kun je niet doen terwijl je valt."
VISITOR_KEEP_INVENTORY: {}
WITHER_DAMAGE:
name: Schakel schoftschade in
description: |-
&a Indien actief, kan de schoft
&a schadeblokken en spelers
WORLD_BLOCK_EXPLODE_DAMAGE: {}
WORLD_TNT_DAMAGE:
description: |-
&a Allow TNT en TNT mijnkarren
&a om blokken te breken en te beschadigen
&a entiteit buiten de eilandgrenzen.
name: Wereld TNT-schade
PETS_STAY_AT_HOME:
description: |-
&a Bij actieve, getemde huisdieren
&a kan alleen naar en gaan
&a kan de eigenaar niet verlaten
&a thuiseiland.
name: Huisdieren blijven thuis
HIVE:
description: "&a Toggle bijenkorf oogsten."
name: Bijenkorf oogsten
hint: Oogsten uitgeschakeld
locked: "&c Dit eiland is op slot!"
protected: "&c Eiland beschermd: [omschrijving]."
world-protected: "&c Wereld beschermd: [omschrijving]."
@ -1361,8 +1363,8 @@ protection:
expert:
name: "&c Expertinstellingen"
description: "&a Geeft alle beschikbare instellingen weer."
click-to-switch: "&e Klik op &7 om over te schakelen naar de &r [volgende]
&r &7."
click-to-switch: "&e Klik op &7 om over te schakelen naar de &r [volgende] &r
&7."
reset-to-default:
name: "&c Reset naar standaard"
description: |
@ -1530,29 +1532,6 @@ catalog:
&a Sta BentoBox toe om verbinding te maken met GitHub in
&a de configuratie of probeer het later opnieuw.
panel:
credits:
title: "&8 [name] &2 credits"
contributor:
name: "&a naam]"
description: "&a Commits: &b [commits]\n"
empty-here:
name: "&c Dit ziet er hier leeg uit ..."
description: |+
&c BentoBox kon de bijdragers niet verzamelen
&c voor deze add-on.
&a Sta BentoBox toe om verbinding te maken met GitHub in
&a de configuratie of probeer het later opnieuw.
successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &7 door &a tastybento &7 en &a Poslovitch
&6 | | _) | ___ _ __ | | _ ___ | | _) | _____ __ &7 2017-2021
&6 | _ </ _ \ '_ \ | __ / _ \ | _ </ _ \ \ / /
&6 | | _) | __ / | | | || (_) | | _) | (_)> <&b v &e [version]
&6 | ____ / \ ___ | _ | | _ | \ __ \ ___ / | ____ / \ ___ / _ / \ _ \ &8 Geladen in &e [time] &8 ms.
enums:
DamageCause:
CONTACT: Contact
@ -1583,3 +1562,26 @@ enums:
HOT_FLOOR: Hete vloer
CRAMMING: Proppen
DRYOUT: Uitdrogen
panel:
credits:
title: "&8 [name] &2 credits"
contributor:
name: "&a naam]"
description: "&a Commits: &b [commits]\n"
empty-here:
name: "&c Dit ziet er hier leeg uit ..."
description: |+
&c BentoBox kon de bijdragers niet verzamelen
&c voor deze add-on.
&a Sta BentoBox toe om verbinding te maken met GitHub in
&a de configuratie of probeer het later opnieuw.
successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &7 door &a tastybento &7 en &a Poslovitch
&6 | | _) | ___ _ __ | | _ ___ | | _) | _____ __ &7 2017-2022
&6 | _ </ _ \ '_ \ | __ / _ \ | _ </ _ \ \ / /
&6 | | _) | __ / | | | || (_) | | _) | (_)> <&b v &e [version]
&6 | ____ / \ ___ | _ | | _ | \ __ \ ___ / | ____ / \ ___ / _ / \ _ \ &8 Geladen in &e [time] &8 ms.

View File

@ -1435,7 +1435,7 @@ successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &7 by &a tastybento &7 i &a Poslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &b v&e [version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8 Wczytano w &e [time]&8 ms.

View File

@ -1463,7 +1463,7 @@ successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &7 por &a tastybento &7 e &a Poslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &b v&e [version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8 Carregado em &e [time]&8 ms.

View File

@ -1520,7 +1520,7 @@ successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &7 by &a tastybento &7 and &a Poslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &7 2017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &b v&e [version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8 Loaded in &e [time]&8 ms.

View File

@ -911,7 +911,7 @@ successfully-loaded: |
&6 ____ _ ____
&6 | _ \ | | | _ \ &7by &atastybento &7and &aPoslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &bv&e[version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8Loaded in &e[time]&8ms.

View File

@ -1,9 +1,9 @@
---
meta:
banner: RED_BANNER:1:RHOMBUS_MIDDLE:WHITE:CIRCLE_MIDDLE:RED:HALF_HORIZONTAL_MIRROR:RED:TRIANGLE_BOTTOM:WHITE:STRIPE_BOTTOM:RED
authors:
- Over_Brave
- None
banner: RED_BANNER:1:RHOMBUS_MIDDLE:WHITE:CIRCLE_MIDDLE:RED:HALF_HORIZONTAL_MIRROR:RED:TRIANGLE_BOTTOM:WHITE:STRIPE_BOTTOM:RED
prefixes:
bentobox: "&6 BentoBox &7 &l > &r "
general:
@ -74,6 +74,7 @@ commands:
purge:
parameters: "[gün]"
description: Belirlediğiniz süreden uzun olan adaları siler.
days-one-or-more: Gün sayısı 1 veya daha uzun olmalıdır.
purgable-islands: "&e[number] &5silinebilecek ada bulundu!"
purge-in-progress: "&cTemizleme devam ediyor. Durdurmak için durdurma komutunu
kullanın."
@ -100,7 +101,6 @@ commands:
description: temizlemenin durumunu gösterir
status: "&b [purgeable] &a adadan &b [purged]'si temizlendi &7(&b[percentage]
%&7)&a."
days-one-or-more: Gün sayısı 1 veya daha uzun olmalıdır.
team:
add:
parameters: "<owner> <player>"
@ -114,6 +114,16 @@ commands:
use-disband-owner: "&4Sahibi degilsin. Sahibi &d[owner]"
disbanded: "&4Admin takımınızı dağıttı!"
success: "&d[name] &btakımı dağıtıldı!"
fix:
description: Veritabanındaki adalar arası üyeliği tarar ve düzeltir
scanning: Veritabanı taranıyor ...
duplicate-owner: "&c Oyuncunun veritabanında birden fazla adası var: [name]"
player-has: "&c Oyuncu [name], [number] adaya sahip"
duplicate-member: "&c [name], veri tabanında bulunan birden fazla adanın
üyesi"
rank-on-island: "&c [rank] adada [xyz]"
fixed: "&a Sabit"
done: "&a Tarama"
kick:
parameters: "<team player>"
description: takımından oyuncu at
@ -126,16 +136,6 @@ commands:
description: Oyuncuya ada liderligini ver!
already-owner: "&d[name] &4zaten ada sahibi!"
success: "&d[name] &bartık adanın sahibi!"
fix:
description: Veritabanındaki adalar arası üyeliği tarar ve düzeltir
scanning: Veritabanı taranıyor ...
duplicate-owner: "&c Oyuncunun veritabanında birden fazla adası var: [name]"
player-has: "&c Oyuncu [name], [number] adaya sahip"
fixed: "&a Sabit"
done: "&a Tarama"
duplicate-member: "&c [name], veri tabanında bulunan birden fazla adanın
üyesi"
rank-on-island: "&c [rank] adada [xyz]"
range:
description: Admin ada menzili ayarlama
invalid-value:
@ -198,11 +198,14 @@ commands:
island-uuid: "&5UUID: [uuid]"
owner: "&bSahibi: &5[owner] ([uuid])"
last-login: "&bSon giris: &5[date]"
last-login-date-time-format: EEE MMM dd HH:mm:ss zzz yyyy
deaths: "&bÖlme sayısı: &5[number]"
resets-left: "&bSıfırlama hakkı: &5[number] (Max: [total])"
team-members-title: "&bTakım üyeleri:"
team-owner-format: "&d[name] [rank]"
team-member-format: "&d[name] [rank]"
island-protection-center: 'Korumalı alan merkezi: [xyz]'
island-center: 'Ada merkezi: [xyz]'
island-coords: "&bAda kordinatları: [xz1] ile [xz2] arasında"
islands-in-trash: "&bOyuncun adası çöp kutusunda!"
protection-range: "&bKoruma alanı: [range]"
@ -213,8 +216,6 @@ commands:
banned-players: "&b Banlanan oyuncular:"
banned-format: "&4[name]"
unowned: "&eBilinmiyor"
island-protection-center: 'Korumalı alan merkezi: [xyz]'
island-center: 'Ada merkezi: [xyz]'
island-location: "&bAda lokasyonu: [xyz]"
switch:
description: Koruma izinlerine açma/kapama
@ -266,6 +267,18 @@ commands:
unknown-rank: "&4Bilinmeyen rütbe!"
not-possible: "&cRütbe türü ziyaretçiden yüksek olmalıdır."
rank-set: "&aRütbe &c[from]&a'dan &e[to] &ayükseltildi."
setprotectionlocation:
parameters: "[x y z coords]"
description: Şu anki konumu veya [x y z] konumunu adanın koruma bölgesinin merkezi
olarak ayarla
island: "&c Bu [xyz] konumundaki '[name]' adlı kullanıcının adasını etkileyecek."
confirmation: "&c Koruma bölgesinin merkezi olarak [xyz] konumunu ayarlamak
istediğine emin misin?"
success: "&a [xyz] konumu başarıyla koruma bölgesinin merkezi olarak ayarlandı."
fail: "&a [xyz] noktası koruma bölgesinin merkezi olarak ayarlanamadı."
island-location-changed: "&a [user] adlı kullanıcı adanın koruma alanı merkezini
[xyz] konumuna değiştirdi."
xyz-error: "&c Üç adet tam sayı koordinat girin: örn, 100 120 100"
setspawn:
description: Dünyanın adanın spawlanacağı bölgeyi seçer.
already-spawn: "&cBu ada zaten spawnalacak bölgede."
@ -349,9 +362,13 @@ commands:
Place blueprint
to right to set
trash: Çöp
no-trash: Çöp Kutusu olamaz
trash-instructions: Silmek için sağ tıkla
no-trash-instructions: Varsayılan grup çöp kutusuna atılamaz
permission: Yetki
no-permission: İzin yok
perm-required: Yetki ihtyacı gerekli.
no-perm-required: Varsayılan paket için izin ayarlanamaz
perm-not-required: Yetki ihtiyacı yok.
perm-format: "&e"
remove: Silmek için sağ tıkla
@ -380,13 +397,9 @@ commands:
slot-instructions: |
&aArtırmak için sol
&aAzaltmak için sağ tıklayın.
no-trash: Çöp Kutusu olamaz
no-trash-instructions: Varsayılan grup çöp kutusuna atılamaz
no-permission: İzin yok
no-perm-required: Varsayılan paket için izin ayarlanamaz
end: The End
nether: Nether
normal: Normal
nether: Nether
end: The End
resetflags:
parameters: "[flag]"
description: Config.yml'deki tüm adaları varsayılan etiket ayarlarına sıfırla
@ -400,6 +413,10 @@ commands:
description: Oyuncunun adasını siler.
cannot-delete-owner: "&4Silmeden önce adadaki herkesi at."
deleted-island: "&e[xyz] &bkordinatlarındaki ada başarıyla silindi"
deletehomes:
parameters: "<oyuncu>"
description: bir adadan tüm adlandırılmış evleri siler
warning: "&c Adlandırılmış tüm evler adadan silinecek!"
why:
parameters: "<player>"
description: Konsolu koruma hata ayıklama raporlaması
@ -425,18 +442,6 @@ commands:
parameters: "<oyuncu> <ölme sayısı>"
success: "&d[name] &5oyuncusunun ölme sayısından &a[number] &5silindi. Toplam
ölme sayısı: &5[total]'"
setprotectionlocation:
parameters: "[x y z coords]"
description: Şu anki konumu veya [x y z] konumunu adanın koruma bölgesinin merkezi
olarak ayarla
island: "&c Bu [xyz] konumundaki '[name]' adlı kullanıcının adasını etkileyecek."
confirmation: "&c Koruma bölgesinin merkezi olarak [xyz] konumunu ayarlamak
istediğine emin misin?"
success: "&a [xyz] konumu başarıyla koruma bölgesinin merkezi olarak ayarlandı."
fail: "&a [xyz] noktası koruma bölgesinin merkezi olarak ayarlanamadı."
island-location-changed: "&a [user] adlı kullanıcı adanın koruma alanı merkezini
[xyz] konumuna değiştirdi."
xyz-error: "&c Üç adet tam sayı koordinat girin: örn, 100 120 100"
bentobox:
description: BentoBox admin komudu
about:
@ -522,6 +527,11 @@ commands:
on-first-login: "&9Hoşgeldin, adan bir kaç saniye içerisinde hazır olacaktır!"
you-can-teleport-to-your-island: "&6İstediğiniz zaman adanıza ışınlanabilirsiniz."
unknown-schem: "&4Taslak yüklenemedi!"
deletehome:
description: bir ev noktasını sil
parameters: "[home name]"
homes:
description: evlerini listele
info:
description: Ada hakkında bilgi verir.
parameters: "<player>"
@ -549,7 +559,10 @@ commands:
sethome:
description: Oldugun noktaya ev olarak kaydeder
must-be-on-your-island: "&4Önce adanda olmalısın!"
too-many-homes: "&c Ayarlanamadı - adanızda maksimum sayıda [number] ev var."
home-set: "&9Oldugun lokasyon ev olarak kaydedildi!"
homes-are: "&6 Adadaki evler:"
home-list-syntax: "&6 [name]"
nether:
not-allowed: "&4Netherde ev kaydetmek icin yetkin yok!."
confirmation: "&4Nethera ev kaydetmek istedigine emin misin?"
@ -557,9 +570,6 @@ commands:
not-allowed: "&4Ende ev kaydetmek icin yetin yok!"
confirmation: "&4Ende ev kaydetmek istedigine emin misin?"
parameters: "[home number]"
too-many-homes: "&c Ayarlanamadı - adanızda maksimum sayıda [number] ev var."
homes-are: "&6 Adadaki evler:"
home-list-syntax: "&6 [name]"
setname:
description: Ada ismini degistir.
name-too-short: "&4Cok kısa. En az &e[number] &4karakter olabilir!."
@ -567,6 +577,11 @@ commands:
name-already-exists: "&cBöyle bir isim zaten bulunmakta."
parameters: "<name>"
success: "&9Yeni ada ismin &8- &a[name]"
renamehome:
description: bir evi yeniden adlandır
parameters: "[home name]"
enter-new-name: "&6 Yeni bir isim gir"
already-exists: "&c Bu isim mevcut, lütfen farklı bir isim dene."
resetname:
description: Ada ismini resetle.
success: "&9Ada ismini başarıyla sıfırladın."
@ -733,16 +748,6 @@ commands:
player-expelled-you: "&d[name] &4seni adadan attı!"
success: "&d[name] &9oyuncusunu adadan attın!"
cannot-ban-member: "&9Takımdan birisini adadan atamazsın!"
deletehome:
description: bir ev noktasını sil
parameters: "[home name]"
renamehome:
description: bir evi yeniden adlandır
parameters: "[home name]"
enter-new-name: "&6 Yeni bir isim gir"
already-exists: "&c Bu isim mevcut, lütfen farklı bir isim dene."
homes:
description: evlerini listele
ranks:
owner: Ada-Sahibi
sub-owner: Yardımcı
@ -826,6 +831,39 @@ protection:
&7Diğer kutular diğer etiketleri ilgilendirmekte.
hint: Kutu kullanımı kapalı.
CHEST:
name: Sandıklar ve minecart sandıkları
description: |-
&a Toggle interaction with chests
&a and chest minecarts.
&a (does not include trapped chests)
hint: Sandık erişimi devre dışı
BARREL:
name: Variller
description: Varil etkileşimini aç/kapat
hint: Varil erişimi devre dışı bırakıldı
BLOCK_EXPLODE_DAMAGE:
description: |-
&a Allow Bed & Respawn Anchors
&a to break blocks and damage
&a entities.
name: Patlama hasarını engelle
COMPOSTER:
name: Kompostolar
description: Komposter etkileşimini aç/kapat
hint: Komposto etkileşimi devre dışı bırakıldı
FLOWER_POT:
name: Çiçek saksıları
description: Saksı etkileşimini aç/kapat
hint: Saksı etkileşimi devre dışı bırakıldı
SHULKER_BOX:
name: Shulker kutuları
description: Shulker kutusu etkileşimini aç/kapat
hint: Shulker kutusu erişimi devre dışı bırakıldı
TRAPPED_CHEST:
name: Kapana kısılmış sandıklar
description: Sıkışmış göğüs etkileşimini aç/kapat
hint: Kapana kısılmış sandık erişimi devre dışı
DISPENSER:
name: Fırlatıcı
description: Fırlatıcı kullanımı değiş.
@ -981,6 +1019,10 @@ protection:
GEO_LIMIT_MOBS:
description: "&aAdanın koruma alanının dışındaki \n&amobları siler."
name: "&eVarlıkları adayla sınırla."
HIVE:
description: "&a Kovan hasatını değiştir."
name: Kovan hasatı
hint: Hasat kapalı
HURT_ANIMALS:
description: Hasar alımını değiş
name: Hayvan Hasarı
@ -1117,6 +1159,13 @@ protection:
&adeğilse redstonelar çalışmaz.
&aLagı azaltmanızda yardımcı olabilir.
name: Kapalı Redstone
PETS_STAY_AT_HOME:
description: |-
&a Aktif iken, eğitilmiş
&a hayvanlar sadece sahibin
&a adasına gidebilir ve
&a ana adadan ayrılamaz.
name: Evcil hayvanlar evde kalır.
PISTON_PUSH:
description: |-
&a Pistonların blokları adanın
@ -1229,28 +1278,32 @@ protection:
&aOyuncular adadan aşağa düşerken
&aadaya geri dönme komudunu engeller.
hint: "&cAdandan aşağaya düşerken adana geri ışınlanamazsın!"
VISITOR_KEEP_INVENTORY:
name: Ziyaretçiler ölümle ilgili envanter tutuyor
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: Wither hasarını ayarlar.
description: |-
&aEğer etkin olursa oyunculara
&ave adalara hasar verebilir.
WORLD_BLOCK_EXPLODE_DAMAGE:
description: |-
&a Allow Bed & Respawn Anchors
&a to break blocks and damage
&a entities outside of island limits.
name: Dünya bloğu patlama hasarı
WORLD_TNT_DAMAGE:
description: |-
&a TNT ve TNT minecarts'ın blokları
&a kırmasına ve ada sınırları dışındaki
&a varlıklara zarar vermesine izin verin.
name: Dünya TNT hasarı
PETS_STAY_AT_HOME:
description: |-
&a Aktif iken, eğitilmiş
&a hayvanlar sadece sahibin
&a adasına gidebilir ve
&a ana adadan ayrılamaz.
name: Evcil hayvanlar evde kalır.
HIVE:
description: "&a Kovan hasatını değiştir."
name: Kovan hasatı
hint: Hasat kapalı
ANIMAL_SPAWN:
description: Doğmayı değiş
name: Hayvan doğuşu
@ -1399,26 +1452,6 @@ catalog:
&aBentoBox'un konfigürasyondaki GitHub'a
&abağlanmasına izin verin veya daha sonra tekrar deneyin.
panel:
credits:
title: "&8[name] &2Emeği geçenler"
contributor:
name: "&a[name]"
description: "&a Commits: &b [commits]"
empty-here:
name: "&cBurası boş gibi gözüküyor..."
description: |-
&e● &cBentoBox bu Eklenti için Katkıda\n&cBulunanları &ctoplayamadı\
.\n&f\n&e● &aBentoBox'ın yapılandırmada GitHub'a\n&abağlanmasına izin verin\
\ veya daha \n&asonra tekrar deneyin.\n
successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &bYazım yanlışları varsa iletişim:
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &cOver_Brave#9324 &b2017 - 2020
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ / &bYapımcısı : &atastybento &band &ePoslovitch
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &bVersiyon &e[version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &e[time] &bsaniyede yüklendi!
enums:
DamageCause:
CONTACT: Temas
@ -1449,3 +1482,23 @@ enums:
HOT_FLOOR: Sıcak Zemin
CRAMMING: Sıkışma
DRYOUT: Kuruma
panel:
credits:
title: "&8[name] &2Emeği geçenler"
contributor:
name: "&a[name]"
description: "&a Commits: &b [commits]"
empty-here:
name: "&cBurası boş gibi gözüküyor..."
description: |-
&e● &cBentoBox bu Eklenti için Katkıda\n&cBulunanları &ctoplayamadı\
.\n&f\n&e● &aBentoBox'ın yapılandırmada GitHub'a\n&abağlanmasına izin verin\
\ veya daha \n&asonra tekrar deneyin.\n
successfully-loaded: |2
&6 ____ _ ____
&6 | _ \ | | | _ \ &bYazım yanlışları varsa iletişim:
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &cOver_Brave#9324 &b2017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ / &bYapımcısı : &atastybento &band &ePoslovitch
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &bVersiyon &e[version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &e[time] &bsaniyede yüklendi!

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -862,7 +862,7 @@ successfully-loaded: |
&6 ____ _ ____
&6 | _ \ | | | _ \ &7by &atastybento &7and &aPoslovitch
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2020
&6 | |_) | ___ _ __ | |_ ___ | |_) | _____ __ &72017 - 2022
&6 | _ < / _ \ '_ \| __/ _ \| _ < / _ \ \/ /
&6 | |_) | __/ | | | || (_) | |_) | (_) > < &bv&e[version]
&6 |____/ \___|_| |_|\__\___/|____/ \___/_/\_\ &8使用了 &e[time]&8ms 來完成載入。

View File

@ -26,6 +26,7 @@ softdepend:
- WildStacker
- LuckPerms
- HolographicDisplays
- EconomyPlus
permissions:
bentobox.admin:

View File

@ -1,9 +1,7 @@
package world.bentobox.bentobox.database.objects;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -11,7 +9,6 @@ import static org.mockito.Mockito.when;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.World;
@ -101,17 +98,6 @@ public class PlayersTest {
assertNotNull(new Players(plugin, UUID.randomUUID()));
}
@Test
public void testSetHomeLocationLocation() {
Location l = mock(Location.class);
when(l.getWorld()).thenReturn(world);
p.setHomeLocation(l, 5);
assertEquals(l, p.getHomeLocation(world, 5));
assertNotEquals(l, p.getHomeLocation(world, 0));
p.clearHomeLocations(world);
assertTrue(p.getHomeLocations(world).isEmpty());
}
@Test
public void testDeaths() {
assertEquals(0, p.getDeaths(world));

View File

@ -96,6 +96,7 @@ public class BlockEndDragonTest {
when(block.getZ()).thenReturn(0);
when(block.getWorld()).thenReturn(world);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(block);
when(world.getMaxHeight()).thenReturn(256);
when(world.getEnvironment()).thenReturn(Environment.THE_END);
// Player
UUID uuid = UUID.randomUUID();

View File

@ -34,6 +34,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.listeners.flags.AbstractCommonSetup;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.util.Util;
@ -271,8 +272,8 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
*/
@Test
public void testOnPlayerInteractEntityNamingWanderingTraderAllowedNoTrading() {
when(island.isAllowed(any(), eq(Flags.TRADING))).thenReturn(false);
when(island.isAllowed(any(), eq(Flags.NAME_TAG))).thenReturn(true);
when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(false);
when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(true);
clickedEntity = mock(WanderingTrader.class);
when(clickedEntity.getType()).thenReturn(EntityType.WANDERING_TRADER);
when(clickedEntity.getLocation()).thenReturn(location);
@ -287,8 +288,8 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
*/
@Test
public void testOnPlayerInteractEntityNamingWanderingTraderAllowedTradingNoNaming() {
when(island.isAllowed(any(), eq(Flags.TRADING))).thenReturn(true);
when(island.isAllowed(any(), eq(Flags.NAME_TAG))).thenReturn(false);
when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(true);
when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(false);
clickedEntity = mock(WanderingTrader.class);
when(clickedEntity.getType()).thenReturn(EntityType.WANDERING_TRADER);
when(clickedEntity.getLocation()).thenReturn(location);

View File

@ -107,7 +107,7 @@ public class TNTListenerTest extends AbstractCommonSetup {
list.add(block);
EntityExplodeEvent e = new EntityExplodeEvent(entity, location, list, 0);
listener.onExplosion(e);
assertTrue(list.isEmpty());
assertTrue(e.isCancelled());
}
@Test
@ -119,7 +119,7 @@ public class TNTListenerTest extends AbstractCommonSetup {
list.add(block);
EntityExplodeEvent e = new EntityExplodeEvent(entity, location, list, 0);
listener.onExplosion(e);
assertTrue(list.isEmpty());
assertTrue(e.isCancelled());
}
@Test
@ -131,6 +131,7 @@ public class TNTListenerTest extends AbstractCommonSetup {
list.add(block);
EntityExplodeEvent e = new EntityExplodeEvent(entity, location, list, 0);
listener.onExplosion(e);
assertFalse(e.isCancelled());
assertFalse(list.isEmpty());
}
@ -141,6 +142,7 @@ public class TNTListenerTest extends AbstractCommonSetup {
list.add(block);
EntityExplodeEvent e = new EntityExplodeEvent(entity, location, list, 0);
listener.onExplosion(e);
assertFalse(e.isCancelled());
assertFalse(list.isEmpty());
}

View File

@ -0,0 +1,223 @@
package world.bentobox.bentobox.listeners.flags.worldsettings;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.util.Vector;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import com.google.common.collect.ImmutableSet;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.configuration.WorldSettings;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.util.Util;
/**
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({BentoBox.class, Util.class})
public class VisitorKeepInventoryListenerTest {
// Class under test
private VisitorKeepInventoryListener l;
@Mock
private Player player;
/* IslandWorldManager */
@Mock
private IslandWorldManager iwm;
/* World */
@Mock
private World world;
/* Islands */
@Mock
private IslandsManager islandsManager;
@Mock
private Island island;
private PlayerDeathEvent e;
@Mock
private Location location;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
// Set up plugin
BentoBox plugin = mock(BentoBox.class);
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
/* Island World Manager */
when(plugin.getIWM()).thenReturn(iwm);
// User
User.setPlugin(plugin);
UUID uuid = UUID.randomUUID();
when(player.getUniqueId()).thenReturn(uuid);
when(player.getName()).thenReturn("tastybento");
when(player.getLocation()).thenReturn(location);
when(location.getWorld()).thenReturn(world);
when(location.toVector()).thenReturn(new Vector(1,2,3));
// Turn on why for player
when(player.getMetadata(eq("bskyblock_world_why_debug"))).thenReturn(Collections.singletonList(new FixedMetadataValue(plugin, true)));
when(player.getMetadata(eq("bskyblock_world_why_debug_issuer"))).thenReturn(Collections.singletonList(new FixedMetadataValue(plugin, uuid.toString())));
User.getInstance(player);
// WorldSettings and World Flags
WorldSettings ws = mock(WorldSettings.class);
when(iwm.getWorldSettings(any())).thenReturn(ws);
Map<String, Boolean> worldFlags = new HashMap<>();
when(ws.getWorldFlags()).thenReturn(worldFlags);
// World
when(world.getName()).thenReturn("bskyblock_world");
// By default everything is in world
when(iwm.inWorld(any(World.class))).thenReturn(true);
when(iwm.inWorld(any(Location.class))).thenReturn(true);
when(iwm.getAddon(any())).thenReturn(Optional.empty());
// Default not set
Flags.VISITOR_KEEP_INVENTORY.setSetting(world, false);
/* Islands */
when(plugin.getIslands()).thenReturn(islandsManager);
// Visitor
when(island.getMemberSet()).thenReturn(ImmutableSet.of());
// By default, there should be an island.
when(islandsManager.getProtectedIslandAt(any())).thenReturn(Optional.of(island));
// Util
PowerMockito.mockStatic(Util.class, Mockito.CALLS_REAL_METHODS);
when(Util.getWorld(any())).thenReturn(world);
// Default death event
List<ItemStack> drops = new ArrayList<>();
drops.add(new ItemStack(Material.ACACIA_BOAT));
e = new PlayerDeathEvent(player, drops, 100, 0, 0, 0, "Death message");
// Make new
l = new VisitorKeepInventoryListener();
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
User.clearUsers();
Mockito.framework().clearInlineMocks();
}
/**
* Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.VisitorKeepInventoryListener#onVisitorDeath(org.bukkit.event.entity.PlayerDeathEvent)}.
*/
@Test
public void testOnVisitorDeath() {
l.onVisitorDeath(e);
}
/**
* Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.VisitorKeepInventoryListener#onVisitorDeath(org.bukkit.event.entity.PlayerDeathEvent)}.
*/
@Test
public void testOnVisitorDeathFalseFlag() {
l.onVisitorDeath(e);
assertFalse(e.getKeepInventory());
assertFalse(e.getKeepLevel());
assertFalse(e.getDrops().isEmpty());
assertEquals(100, e.getDroppedExp());
// Why
verify(player).sendMessage("Why: PlayerDeathEvent in world bskyblock_world at 1,2,3");
verify(player).sendMessage("Why: tastybento VISITOR_KEEP_INVENTORY - SETTING_NOT_ALLOWED_IN_WORLD");
}
/**
* Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.VisitorKeepInventoryListener#onVisitorDeath(org.bukkit.event.entity.PlayerDeathEvent)}.
*/
@Test
public void testOnVisitorDeathTrueFlag() {
Flags.VISITOR_KEEP_INVENTORY.setSetting(world, true);
l.onVisitorDeath(e);
assertTrue(e.getKeepInventory());
assertTrue(e.getKeepLevel());
assertTrue(e.getDrops().isEmpty());
assertEquals(0, e.getDroppedExp());
// Why
verify(player).sendMessage("Why: PlayerDeathEvent in world bskyblock_world at 1,2,3");
verify(player).sendMessage("Why: tastybento VISITOR_KEEP_INVENTORY - SETTING_ALLOWED_IN_WORLD");
}
/**
* Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.VisitorKeepInventoryListener#onVisitorDeath(org.bukkit.event.entity.PlayerDeathEvent)}.
*/
@Test
public void testOnVisitorDeathNotInWorld() {
when(iwm.inWorld(eq(world))).thenReturn(false);
Flags.VISITOR_KEEP_INVENTORY.setSetting(world, true);
l.onVisitorDeath(e);
assertFalse(e.getKeepInventory());
assertFalse(e.getKeepLevel());
assertFalse(e.getDrops().isEmpty());
assertEquals(100, e.getDroppedExp());
// Why
verify(player).sendMessage("Why: PlayerDeathEvent in world bskyblock_world at 1,2,3");
verify(player).sendMessage("Why: tastybento VISITOR_KEEP_INVENTORY - SETTING_NOT_ALLOWED_IN_WORLD");
}
/**
* Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.VisitorKeepInventoryListener#onVisitorDeath(org.bukkit.event.entity.PlayerDeathEvent)}.
*/
@Test
public void testOnVisitorDeathTrueFlagNoIsland() {
when(islandsManager.getProtectedIslandAt(any())).thenReturn(Optional.empty());
Flags.VISITOR_KEEP_INVENTORY.setSetting(world, true);
l.onVisitorDeath(e);
assertFalse(e.getKeepInventory());
assertFalse(e.getKeepLevel());
assertFalse(e.getDrops().isEmpty());
assertEquals(100, e.getDroppedExp());
// Why
verify(player, never()).sendMessage(anyString());
}
}

View File

@ -22,6 +22,7 @@ import java.util.zip.ZipOutputStream;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.block.data.BlockData;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.After;
@ -98,6 +99,8 @@ public class BlueprintClipboardManagerTest {
" \"ySize\": 10,\n" +
" \"zSize\": 10\n" +
"}";
@Mock
private Server server;
private void zip(File targetFile) throws IOException {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(targetFile.getAbsolutePath() + BlueprintsManager.BLUEPRINT_SUFFIX))) {
@ -129,6 +132,9 @@ public class BlueprintClipboardManagerTest {
BlockData blockData = mock(BlockData.class);
when(Bukkit.createBlockData(any(Material.class))).thenReturn(blockData);
when(blockData.getAsString()).thenReturn("test123");
when(server.getBukkitVersion()).thenReturn("version");
when(Bukkit.getServer()).thenReturn(server);
}
/**

View File

@ -28,6 +28,7 @@ import java.util.jar.Manifest;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.scheduler.BukkitScheduler;
@ -93,6 +94,8 @@ public class BlueprintsManagerTest {
private BukkitTask task;
private int times;
@Mock
private Server server;
/**
* @throws java.lang.Exception
*/
@ -116,6 +119,8 @@ public class BlueprintsManagerTest {
// Scheduler
PowerMockito.mockStatic(Bukkit.class);
when(Bukkit.getScheduler()).thenReturn(scheduler);
when(server.getBukkitVersion()).thenReturn("version");
when(Bukkit.getServer()).thenReturn(server);
}

View File

@ -67,6 +67,8 @@ public class IslandDeletionManagerTest {
private BukkitScheduler scheduler;
@Mock
private IslandWorldManager iwm;
@Mock
private IslandChunkDeletionManager chunkDeletionManager;
/**
@ -101,6 +103,8 @@ public class IslandDeletionManagerTest {
// IWM
when(plugin.getIWM()).thenReturn(iwm);
when(iwm.getIslandDistance(any())).thenReturn(64);
// Chunk deletion manager
when(plugin.getIslandChunkDeletionManager()).thenReturn(chunkDeletionManager);
// Island Deletion Manager
idm = new IslandDeletionManager(plugin);

View File

@ -108,6 +108,8 @@ public class IslandsManagerTest {
@Mock
private IslandWorldManager iwm;
@Mock
private IslandChunkDeletionManager chunkDeletionManager;
@Mock
private IslandCache islandCache;
private Optional<Island> optionalIsland;
@Mock
@ -158,6 +160,9 @@ public class IslandsManagerTest {
when(iwm.inWorld(any(Location.class))).thenReturn(true);
when(plugin.getIWM()).thenReturn(iwm);
// Chunk deletion manager
when(plugin.getIslandChunkDeletionManager()).thenReturn(chunkDeletionManager);
// Settings
Settings s = mock(Settings.class);
when(plugin.getSettings()).thenReturn(s);

View File

@ -147,9 +147,17 @@ public class PlayersManagerTest {
when(nether.getSpawnLocation()).thenReturn(netherSpawn);
when(iwm.getNetherSpawnRadius(Mockito.any())).thenReturn(100);
// UUID
uuid = UUID.randomUUID();
notUUID = UUID.randomUUID();
while(notUUID.equals(uuid)) {
notUUID = UUID.randomUUID();
}
// Player
when(p.getEnderChest()).thenReturn(inv);
when(p.getInventory()).thenReturn(playerInv);
when(p.getUniqueId()).thenReturn(uuid);
AttributeInstance at = mock(AttributeInstance.class);
when(at.getValue()).thenReturn(20D);
when(p.getAttribute(Attribute.GENERIC_MAX_HEALTH)).thenReturn(at);
@ -157,11 +165,6 @@ public class PlayersManagerTest {
// Sometimes use Mockito.withSettings().verboseLogging()
user = mock(User.class);
when(user.isOp()).thenReturn(false);
uuid = UUID.randomUUID();
notUUID = UUID.randomUUID();
while(notUUID.equals(uuid)) {
notUUID = UUID.randomUUID();
}
when(user.getUniqueId()).thenReturn(uuid);
when(user.getPlayer()).thenReturn(p);
when(user.getName()).thenReturn("tastybento");

View File

@ -1,149 +0,0 @@
package world.bentobox.bentobox.util.teleport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Optional;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.LocalesManager;
@RunWith(PowerMockRunner.class)
@PrepareForTest(SafeSpotTeleport.Builder.class)
public class SafeSpotTeleportBuilderTest {
@Mock
private SafeSpotTeleport sst;
@Mock
private BentoBox plugin;
@Mock
private Player player;
@Mock
private Location loc;
@InjectMocks
private SafeSpotTeleport.Builder sstb;
@Before
public void setUp() throws Exception {
PowerMockito.whenNew(SafeSpotTeleport.class).withAnyArguments().thenReturn(sst);
// Users
User.setPlugin(plugin);
// Locales - final
LocalesManager lm = mock(LocalesManager.class);
when(plugin.getLocalesManager()).thenReturn(lm);
when(lm.get(any(), any())).thenReturn("mock translation");
// Addon
IslandWorldManager iwm = mock(IslandWorldManager.class);
when(iwm.getAddon(Mockito.any())).thenReturn(Optional.empty());
when(plugin.getIWM()).thenReturn(iwm);
}
@After
public void tearDown() {
Mockito.framework().clearInlineMocks();
}
@Test
public void testBuilder() {
sstb = new SafeSpotTeleport.Builder(plugin);
// Should fail because no data
assertNull(sstb.build());
}
@Test
public void testEntity() throws Exception {
// Start builder
sstb = new SafeSpotTeleport.Builder(plugin);
// Add entity
sstb.entity(player);
// Test for error
assertNull(sstb.build());
// Add location
sstb.location(loc);
// Build - expect success
SafeSpotTeleport result = sstb.build();
assertEquals(sst, result);
}
@Test
public void testIsland() {
// Start builder
SafeSpotTeleport.Builder sstb = new SafeSpotTeleport.Builder(plugin);
// Add entity
sstb.entity(player);
// Add island
Island island = mock(Island.class);
when(island.getProtectionCenter()).thenReturn(loc);
sstb.island(island);
// Build - expect success
SafeSpotTeleport result = sstb.build();
assertEquals(sst, result);
}
@Test
public void testHomeNumber() {
// Start builder
SafeSpotTeleport.Builder sstb = new SafeSpotTeleport.Builder(plugin);
// Add entity
sstb.entity(player);
// Add location
sstb.location(loc);
// Add home
sstb.homeName("my name");
// Build - expect success
SafeSpotTeleport result = sstb.build();
assertEquals(sst, result);
}
@Test
public void testPortal() {
// Start builder
SafeSpotTeleport.Builder sstb = new SafeSpotTeleport.Builder(plugin);
// Add entity
sstb.entity(player);
// Add location
sstb.location(loc);
// Portal
sstb.portal();
// Build - expect success
SafeSpotTeleport result = sstb.build();
assertEquals(sst, result);
}
@Test
public void testFailureMessage() {
// Start builder
SafeSpotTeleport.Builder sstb = new SafeSpotTeleport.Builder(plugin);
// Add entity
sstb.entity(player);
// Add location
sstb.location(loc);
// Add failure
sstb.failureMessage("testing 123");
// Build - expect success
SafeSpotTeleport result = sstb.build();
assertEquals(sst, result);
}
}

View File

@ -0,0 +1,281 @@
package world.bentobox.bentobox.util.teleport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import org.eclipse.jdt.annotation.NonNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
/**
* Test class for safe teleporting
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Util.class, Bukkit.class})
public class SafeSpotTeleportTest {
// Class under test
private SafeSpotTeleport sst;
@Mock
private SafeSpotTeleport.Builder builder;
@Mock
private BentoBox plugin;
@Mock
private Location location;
@Mock
private World world;
@Mock
private Entity entity;
private boolean portal;
private int num;
private String name;
@Mock
private Runnable runnable;
@Mock
private Runnable failRunnable;
@Mock
private CompletableFuture<Boolean> result;
@Mock
private @NonNull CompletableFuture<Chunk> cfChunk;
@Mock
private IslandsManager im;
@Mock
private BukkitScheduler scheduler;
private Island island;
@Mock
private IslandWorldManager iwm;
@Mock
private BukkitTask task;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
// Setup instance
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
// IWM
when(iwm.getIslandProtectionRange(any())).thenReturn(100);
when(iwm.getIslandDistance(any())).thenReturn(400);
when(plugin.getIWM()).thenReturn(iwm);
// Mock static Util
PowerMockito.mockStatic(Util.class, Mockito.RETURNS_MOCKS);
when(Util.getChunkAtAsync(any(Location.class))).thenReturn(cfChunk);
// Same world
when(Util.sameWorld(any(), any())).thenReturn(true);
// Set up a mock builder
when(builder.getPlugin()).thenReturn(plugin);
when(builder.getEntity()).thenReturn(entity);
when(builder.getLocation()).thenReturn(location);
when(builder.isPortal()).thenReturn(portal);
when(builder.getHomeNumber()).thenReturn(num);
when(builder.getHomeName()).thenReturn(name);
when(builder.getRunnable()).thenReturn(runnable);
when(builder.getFailRunnable()).thenReturn(failRunnable);
when(builder.getResult()).thenReturn(result);
// Set the default world
when(location.getWorld()).thenReturn(world);
// Island
island = new Island(location, UUID.randomUUID(), 50);
// Plugin Island Manager
// Default that locations are safe
when(im.isSafeLocation(any(Location.class))).thenReturn(true);
// Provide an island
when(im.getIslandAt(any(Location.class))).thenReturn(Optional.of(island));
when(plugin.getIslands()).thenReturn(im);
// Bukkit scheduler
when(scheduler.runTaskTimer(eq(plugin), any(Runnable.class), anyLong(), anyLong())).thenReturn(task);
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
when(Bukkit.getScheduler()).thenReturn(scheduler);
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#SafeSpotTeleport(world.bentobox.bentobox.util.teleport.SafeSpotTeleport.Builder)}.
*/
@Test(expected = NullPointerException.class)
public void testSafeSpotTeleportNullWorld() {
when(location.getWorld()).thenReturn(null);
sst = new SafeSpotTeleport(builder);
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#SafeSpotTeleport(world.bentobox.bentobox.util.teleport.SafeSpotTeleport.Builder)}.
*/
@Test
public void testSafeSpotTeleport() {
sst = new SafeSpotTeleport(builder);
verify(cfChunk).thenRun(any(Runnable.class));
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#tryToGo(java.lang.String)}.
*/
@Test
public void testTryToGoSafeNotPortal() {
portal = false;
testSafeSpotTeleport();
sst.tryToGo("failure message");
PowerMockito.verifyStatic(Util.class);
// Verify that the teleport is done immediately
Util.teleportAsync(entity, location);
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#tryToGo(java.lang.String)}.
*/
@Test
public void testTryToGoUnsafe() {
when(im.isSafeLocation(any(Location.class))).thenReturn(false);
// Set up fields
testSafeSpotTeleport();
sst.tryToGo("failure message");
verify(scheduler).runTaskTimer(eq(plugin), any(Runnable.class), eq(0L), eq(1L));
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#gatherChunks(java.lang.String)}.
*/
@Test
public void testGatherChunks() {
// Setup fields
testTryToGoUnsafe();
// run test
assertTrue(sst.gatherChunks("failure message"));
PowerMockito.verifyStatic(Util.class);
Util.getChunkAtAsync(eq(world), anyInt(), anyInt());
// run test again - should be blocked because of atomic boolean
assertFalse(sst.gatherChunks("failure message"));
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#tidyUp(org.bukkit.entity.Entity, java.lang.String)}.
*/
@Test
public void testTidyUpNoPlayerFailRunnable() {
when(im.isSafeLocation(any(Location.class))).thenReturn(false);
sst = new SafeSpotTeleport(builder);
sst.tryToGo("failure message");
sst.tidyUp(entity, "failure note");
verify(task).cancel();
verify(scheduler).runTask(plugin, failRunnable);
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#tidyUp(org.bukkit.entity.Entity, java.lang.String)}.
*/
@Test
public void testTidyUpPlayer() {
when(im.isSafeLocation(any(Location.class))).thenReturn(false);
sst = new SafeSpotTeleport(builder);
sst.tryToGo("failure message");
sst.tidyUp(entity, "failure note");
verify(task).cancel();
verify(scheduler).runTask(plugin, failRunnable);
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#makeAndTeleport(org.bukkit.Material)}.
*/
@Test
public void testMakeAndTeleport() {
//fail("Not yet implemented"); // TODO
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#getChunksToScan()}.
*/
@Test
public void testGetChunksToScan() {
testSafeSpotTeleport();
List<Pair<Integer, Integer>> pairs = sst.getChunksToScan();
assertEquals(62, pairs.size());
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#scanChunk(org.bukkit.ChunkSnapshot)}.
*/
@Test
public void testScanChunk() {
//fail("Not yet implemented"); // TODO
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#teleportEntity(org.bukkit.Location)}.
*/
@Test
public void testTeleportEntity() {
//fail("Not yet implemented"); // TODO
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#checkBlock(org.bukkit.ChunkSnapshot, int, int, int)}.
*/
@Test
public void testCheckBlock() {
//fail("Not yet implemented"); // TODO
}
/**
* Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#safe(org.bukkit.ChunkSnapshot, int, int, int, org.bukkit.World)}.
*/
@Test
public void testSafe() {
//fail("Not yet implemented"); // TODO
}
}