mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2025-02-03 05:51:43 +01:00
Version 1.20.1 (#1966)
* Version 1.20.1 * Added name of the addon causing the issue. https://github.com/BentoBoxWorld/BentoBox/issues/1944 * Use world min-height for island bounding box. * Fixes a bug when fallback could not use reusable There was an issue in PanelItemTemplate that prevented fallback buttons to be "reusable" things. The issue was that reusable items were not passed to the panel item reader. * Adjusted test to try to avoid errors * Fix for random test failures. * Added 1.18.2 support * Address unnecessary PVP reports on each teleport (#1948) If a player is teleporting on the same island in the same dimension, it keeps spamming that PVP is enabled in dimension. It should be enough with sending messages when the player teleports to the island. Fixes #1885 * Fixes bug with Safe Spot Teleport (#1951) There was a bug that prevented finding a safe spot if all valid blocks were in height with the `startY` location. Reported via discord. * Fix Exception error reported by IDE I am not sure why Eclipse is saying this is an error. * Fix for kicking offline players https://github.com/BentoBoxWorld/BentoBox/issues/1950 * Add an option in SafeSpotTeleport to cancel if fail (#1952) There was no option to cancel teleportation if SafeSpotTeleport could not find a valid spot. This option could be used to avoid creating "backup" blocks in situations when teleportation is avoidable, f.e. visiting an island. * Replace peplaceAll with replace It does the same thing if the first argument is not a regex. * Use constants for common strings * Use constants for common strings * Go back to replaceAll This is required. * Clearer paster (#1953) * WIP - make easier to understand. * Small refactor of paster to make it easier to understand * Fix tabs to spaces. Sorry - new editor! * Fix tabs to spaces * Fix tab to spaces * Improve team kick command (#1957) The kick command has an unnecessary owner check. As command should be configurable by island owners, then limiting it to an owner is wrong. Add a code that allows kicking only lower-ranked players. Add message that shows who kicked from the island. Add message that shows that rank does not allow to kick. * Solve crashes with Addon#allLoaded call (#1959) If some addon has code in Addon#allLoaded that crashes the call, then it did not disable addon as well as did not call allLoaded for every other addon that was left in the list. This should be solved by adding an extra try-catch. * using java 16 syntax (#1958) * Fixes kick command (#1960) PR #1957 broke kick command and noone could kick players from teams. This should fix it. * Fixes a bug with blueprint height (#1961) Blueprint clipboard was preventing setting Y below 0 or above 255. The code was not adjusted to 1.18 changes. Reported via discord. * Fixes Lava Duplication Glitch (#1964) Due to the fact, that Obsidian Scooping uses one tick delay to remove obsidian, a player with a bucket in hand and offhand duplicated lava. To avoid that, added an extra check that ignores the interact event if a player holds a bucket in both hands, and interacted hand is offhand. Fixes #1963 * Fixes failures in obsidian cooping listener. (#1965) Failures happened after implementing #1964 Co-authored-by: BONNe <bonne@bonne.id.lv> Co-authored-by: Invvk <70810073+Invvk@users.noreply.github.com>
This commit is contained in:
parent
73a432d75c
commit
928f1eb1eb
2
pom.xml
2
pom.xml
@ -83,7 +83,7 @@
|
||||
<!-- Do not change unless you want different name for local builds. -->
|
||||
<build.number>-LOCAL</build.number>
|
||||
<!-- This allows to change between versions. -->
|
||||
<build.version>1.20.0</build.version>
|
||||
<build.version>1.20.1</build.version>
|
||||
<sonar.organization>bentobox-world</sonar.organization>
|
||||
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
||||
</properties>
|
||||
|
@ -20,6 +20,7 @@ import world.bentobox.bentobox.util.Util;
|
||||
*/
|
||||
public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
|
||||
private static final String INVALID_INVITE = "commands.island.team.invite.errors.invalid-invite";
|
||||
private final IslandTeamCommand itc;
|
||||
private UUID playerUUID;
|
||||
private UUID prospectiveOwnerUUID;
|
||||
@ -47,7 +48,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
// Get the island owner
|
||||
prospectiveOwnerUUID = itc.getInviter(playerUUID);
|
||||
if (prospectiveOwnerUUID == null) {
|
||||
user.sendMessage("commands.island.team.invite.errors.invalid-invite");
|
||||
user.sendMessage(INVALID_INVITE);
|
||||
return false;
|
||||
}
|
||||
Invite invite = itc.getInvite(playerUUID);
|
||||
@ -56,7 +57,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
Island island = getIslands().getIsland(getWorld(), prospectiveOwnerUUID);
|
||||
String inviteUsage = getParent().getSubCommand("invite").map(CompositeCommand::getUsage).orElse("");
|
||||
if (island == null || island.getRank(prospectiveOwnerUUID) < island.getRankCommand(inviteUsage)) {
|
||||
user.sendMessage("commands.island.team.invite.errors.invalid-invite");
|
||||
user.sendMessage(INVALID_INVITE);
|
||||
itc.removeInvite(playerUUID);
|
||||
return false;
|
||||
}
|
||||
@ -149,7 +150,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
// Get the team's island
|
||||
Island teamIsland = getIslands().getIsland(getWorld(), prospectiveOwnerUUID);
|
||||
if (teamIsland == null) {
|
||||
user.sendMessage("commands.island.team.invite.errors.invalid-invite");
|
||||
user.sendMessage(INVALID_INVITE);
|
||||
return;
|
||||
}
|
||||
if (teamIsland.getMemberSet(RanksManager.MEMBER_RANK, true).size() > getIslands().getMaxMembers(teamIsland, RanksManager.MEMBER_RANK)) {
|
||||
|
@ -42,10 +42,6 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
|
||||
user.sendMessage("general.errors.no-team");
|
||||
return false;
|
||||
}
|
||||
if (!user.getUniqueId().equals(getOwner(getWorld(), user))) {
|
||||
user.sendMessage("general.errors.not-owner");
|
||||
return false;
|
||||
}
|
||||
// Check rank to use command
|
||||
Island island = getIslands().getIsland(getWorld(), user);
|
||||
int rank = Objects.requireNonNull(island).getRank(user);
|
||||
@ -72,6 +68,14 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
|
||||
user.sendMessage("general.errors.not-in-team");
|
||||
return false;
|
||||
}
|
||||
|
||||
int targetRank = Objects.requireNonNull(island).getRank(targetUUID);
|
||||
if (rank <= targetRank) {
|
||||
user.sendMessage("commands.island.team.kick.cannot-kick-rank",
|
||||
TextVariables.NAME, getPlayers().getName(targetUUID));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getSettings().isKickConfirmation()) {
|
||||
kick(user, targetUUID);
|
||||
return true;
|
||||
@ -93,7 +97,9 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
target.sendMessage("commands.island.team.kick.owner-kicked", TextVariables.GAMEMODE, getAddon().getDescription().getName());
|
||||
target.sendMessage("commands.island.team.kick.player-kicked",
|
||||
TextVariables.GAMEMODE, getAddon().getDescription().getName(),
|
||||
TextVariables.NAME, user.getName());
|
||||
|
||||
getIslands().removePlayer(getWorld(), targetUUID);
|
||||
// Clean the target player
|
||||
|
@ -41,6 +41,12 @@ public class TemplateReader
|
||||
private static final String BORDER = "border";
|
||||
private static final String FORCE_SHOWN = "force-shown";
|
||||
private static final String FALLBACK = "fallback";
|
||||
private static final String YML = ".yml";
|
||||
private static final String ACTIONS = "actions";
|
||||
private static final String TOOLTIP = "tooltip";
|
||||
private static final String CLICK_TYPE = "click-type";
|
||||
private static final String CONTENT = "content";
|
||||
private static final String TYPE = "type";
|
||||
|
||||
|
||||
/**
|
||||
@ -73,7 +79,7 @@ public class TemplateReader
|
||||
return null;
|
||||
}
|
||||
|
||||
File file = new File(panelLocation, templateName.endsWith(".yml") ? templateName : templateName + ".yml");
|
||||
File file = new File(panelLocation, templateName.endsWith(YML) ? templateName : templateName + YML);
|
||||
|
||||
if (!file.exists())
|
||||
{
|
||||
@ -82,7 +88,7 @@ public class TemplateReader
|
||||
}
|
||||
|
||||
final String panelKey = file.getAbsolutePath() + ":" + panelName;
|
||||
|
||||
|
||||
// Check if panel is already crafted.
|
||||
if (TemplateReader.loadedPanels.containsKey(panelKey))
|
||||
{
|
||||
@ -125,7 +131,7 @@ public class TemplateReader
|
||||
|
||||
String title = configurationSection.getString(TITLE);
|
||||
Panel.Type type =
|
||||
Enums.getIfPresent(Panel.Type.class, configurationSection.getString("type", "INVENTORY")).
|
||||
Enums.getIfPresent(Panel.Type.class, configurationSection.getString(TYPE, "INVENTORY")).
|
||||
or(Panel.Type.INVENTORY);
|
||||
|
||||
PanelTemplateRecord.TemplateItem borderItem = null;
|
||||
@ -194,7 +200,7 @@ public class TemplateReader
|
||||
PanelTemplateRecord template = new PanelTemplateRecord(type, title, borderItem, backgroundItem, forcedRows);
|
||||
|
||||
// Read content
|
||||
ConfigurationSection content = configurationSection.getConfigurationSection("content");
|
||||
ConfigurationSection content = configurationSection.getConfigurationSection(CONTENT);
|
||||
|
||||
if (content == null)
|
||||
{
|
||||
@ -219,7 +225,7 @@ public class TemplateReader
|
||||
// If it contains a section, then build a new button template from it.
|
||||
template.addButtonTemplate(rowIndex,
|
||||
columnIndex,
|
||||
readPanelItemTemplate(line.getConfigurationSection(String.valueOf(columnIndex + 1))));
|
||||
readPanelItemTemplate(line.getConfigurationSection(String.valueOf(columnIndex + 1)), null, panelItemDataMap));
|
||||
}
|
||||
else if (line.isString(String.valueOf(columnIndex + 1)))
|
||||
{
|
||||
@ -337,9 +343,9 @@ public class TemplateReader
|
||||
}
|
||||
|
||||
// Read Click data
|
||||
if (section.isConfigurationSection("actions"))
|
||||
if (section.isConfigurationSection(ACTIONS))
|
||||
{
|
||||
ConfigurationSection actionSection = section.getConfigurationSection("actions");
|
||||
ConfigurationSection actionSection = section.getConfigurationSection(ACTIONS);
|
||||
|
||||
if (actionSection != null)
|
||||
{
|
||||
@ -354,9 +360,9 @@ public class TemplateReader
|
||||
{
|
||||
ItemTemplateRecord.ActionRecords actionData =
|
||||
new ItemTemplateRecord.ActionRecords(clickType,
|
||||
actionDataSection.getString("type"),
|
||||
actionDataSection.getString("content"),
|
||||
actionDataSection.getString("tooltip"));
|
||||
actionDataSection.getString(TYPE),
|
||||
actionDataSection.getString(CONTENT),
|
||||
actionDataSection.getString(TOOLTIP));
|
||||
itemRecord.addAction(actionData);
|
||||
}
|
||||
}
|
||||
@ -364,42 +370,42 @@ public class TemplateReader
|
||||
{
|
||||
ConfigurationSection actionDataSection = actionSection.getConfigurationSection(actionKey);
|
||||
|
||||
if (actionDataSection != null && actionDataSection.contains("click-type"))
|
||||
if (actionDataSection != null && actionDataSection.contains(CLICK_TYPE))
|
||||
{
|
||||
clickType = Enums.getIfPresent(ClickType.class,
|
||||
actionDataSection.getString("click-type", "UNKNOWN").toUpperCase()).
|
||||
or(ClickType.UNKNOWN);
|
||||
|
||||
actionDataSection.getString(CLICK_TYPE, "UNKNOWN").toUpperCase()).
|
||||
or(ClickType.UNKNOWN);
|
||||
|
||||
ItemTemplateRecord.ActionRecords actionData =
|
||||
new ItemTemplateRecord.ActionRecords(clickType,
|
||||
actionKey,
|
||||
actionDataSection.getString("content"),
|
||||
actionDataSection.getString("tooltip"));
|
||||
new ItemTemplateRecord.ActionRecords(clickType,
|
||||
actionKey,
|
||||
actionDataSection.getString(CONTENT),
|
||||
actionDataSection.getString(TOOLTIP));
|
||||
itemRecord.addAction(actionData);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (section.isList("actions"))
|
||||
else if (section.isList(ACTIONS))
|
||||
{
|
||||
// Read Click data as list which allows to have duplicate click types.
|
||||
|
||||
List<Map<?, ?>> actionList = section.getMapList("actions");
|
||||
List<Map<?, ?>> actionList = section.getMapList(ACTIONS);
|
||||
|
||||
if (!actionList.isEmpty())
|
||||
{
|
||||
actionList.forEach(valueMap -> {
|
||||
ClickType clickType = Enums.getIfPresent(ClickType.class,
|
||||
String.valueOf(valueMap.get("click-type")).toUpperCase()).orNull();
|
||||
String.valueOf(valueMap.get(CLICK_TYPE)).toUpperCase()).orNull();
|
||||
|
||||
if (clickType != null)
|
||||
{
|
||||
ItemTemplateRecord.ActionRecords actionData =
|
||||
new ItemTemplateRecord.ActionRecords(clickType,
|
||||
valueMap.containsKey("type") ? String.valueOf(valueMap.get("type")) : null,
|
||||
valueMap.containsKey("content") ? String.valueOf(valueMap.get("content")) : null,
|
||||
valueMap.containsKey("tooltip") ? String.valueOf(valueMap.get("tooltip")) : null);
|
||||
new ItemTemplateRecord.ActionRecords(clickType,
|
||||
valueMap.containsKey(TYPE) ? String.valueOf(valueMap.get(TYPE)) : null,
|
||||
valueMap.containsKey(CONTENT) ? String.valueOf(valueMap.get(CONTENT)) : null,
|
||||
valueMap.containsKey(TOOLTIP) ? String.valueOf(valueMap.get(TOOLTIP)) : null);
|
||||
itemRecord.addAction(actionData);
|
||||
}
|
||||
});
|
||||
|
@ -257,8 +257,8 @@ public class BlueprintClipboard {
|
||||
}
|
||||
|
||||
// Banners
|
||||
if (blockState instanceof Banner) {
|
||||
b.setBannerPatterns(((Banner) blockState).getPatterns());
|
||||
if (blockState instanceof Banner banner) {
|
||||
b.setBannerPatterns(banner.getPatterns());
|
||||
}
|
||||
|
||||
return b;
|
||||
@ -282,8 +282,8 @@ public class BlueprintClipboard {
|
||||
BlueprintEntity bpe = new BlueprintEntity();
|
||||
bpe.setType(entity.getType());
|
||||
bpe.setCustomName(entity.getCustomName());
|
||||
if (entity instanceof Villager) {
|
||||
setVillager(entity, bpe);
|
||||
if (entity instanceof Villager villager) {
|
||||
setVillager(villager, bpe);
|
||||
}
|
||||
if (entity instanceof Colorable c) {
|
||||
if (c.getColor() != null) {
|
||||
@ -321,11 +321,10 @@ public class BlueprintClipboard {
|
||||
|
||||
/**
|
||||
* Set the villager stats
|
||||
* @param entity - villager
|
||||
* @param v - villager
|
||||
* @param bpe - Blueprint Entity
|
||||
*/
|
||||
private void setVillager(LivingEntity entity, BlueprintEntity bpe) {
|
||||
Villager v = (Villager)entity;
|
||||
private void setVillager(Villager v, BlueprintEntity bpe) {
|
||||
bpe.setExperience(v.getVillagerExperience());
|
||||
bpe.setLevel(v.getVillagerLevel());
|
||||
bpe.setProfession(v.getProfession());
|
||||
@ -371,11 +370,17 @@ public class BlueprintClipboard {
|
||||
public void setPos1(@Nullable Location pos1) {
|
||||
origin = null;
|
||||
if (pos1 != null) {
|
||||
if (pos1.getBlockY() < 0) {
|
||||
pos1.setY(0);
|
||||
final int minHeight = pos1.getWorld() == null ? 0 : pos1.getWorld().getMinHeight();
|
||||
final int maxHeight = pos1.getWorld() == null ? 255 : pos1.getWorld().getMaxHeight();
|
||||
|
||||
if (pos1.getBlockY() < minHeight)
|
||||
{
|
||||
pos1.setY(minHeight);
|
||||
}
|
||||
if (pos1.getBlockY() > 255) {
|
||||
pos1.setY(255);
|
||||
|
||||
if (pos1.getBlockY() > maxHeight)
|
||||
{
|
||||
pos1.setY(maxHeight);
|
||||
}
|
||||
}
|
||||
this.pos1 = pos1;
|
||||
@ -387,11 +392,17 @@ public class BlueprintClipboard {
|
||||
public void setPos2(@Nullable Location pos2) {
|
||||
origin = null;
|
||||
if (pos2 != null) {
|
||||
if (pos2.getBlockY() < 0) {
|
||||
pos2.setY(0);
|
||||
final int minHeight = pos2.getWorld() == null ? 0 : pos2.getWorld().getMinHeight();
|
||||
final int maxHeight = pos2.getWorld() == null ? 255 : pos2.getWorld().getMaxHeight();
|
||||
|
||||
if (pos2.getBlockY() < minHeight)
|
||||
{
|
||||
pos2.setY(minHeight);
|
||||
}
|
||||
if (pos2.getBlockY() > 255) {
|
||||
pos2.setY(255);
|
||||
|
||||
if (pos2.getBlockY() > maxHeight)
|
||||
{
|
||||
pos2.setY(maxHeight);
|
||||
}
|
||||
}
|
||||
this.pos2 = pos2;
|
||||
|
@ -134,99 +134,110 @@ public class BlueprintPaster {
|
||||
this.location = island.getProtectionCenter().toVector().subtract(off).toLocation(world);
|
||||
}
|
||||
|
||||
private record Bits(Map<Vector, BlueprintBlock> blocks,
|
||||
Map<Vector, BlueprintBlock> attached,
|
||||
Map<Vector, List<BlueprintEntity>> entities,
|
||||
Iterator<Entry<Vector, BlueprintBlock>> it,
|
||||
Iterator<Entry<Vector, BlueprintBlock>> it2,
|
||||
Iterator<Entry<Vector, List<BlueprintEntity>>> it3,
|
||||
int pasteSpeed) {}
|
||||
/**
|
||||
* The main pasting method
|
||||
*/
|
||||
public CompletableFuture<Boolean> paste() {
|
||||
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||
// Iterators for the various maps to paste
|
||||
Map<Vector, BlueprintBlock> blocks = blueprint.getBlocks() == null ? Collections.emptyMap() : blueprint.getBlocks();
|
||||
Map<Vector, BlueprintBlock> attached = blueprint.getAttached() == null ? Collections.emptyMap() : blueprint.getAttached();
|
||||
Map<Vector, List<BlueprintEntity>> entities = blueprint.getEntities() == null ? Collections.emptyMap() : blueprint.getEntities();
|
||||
Iterator<Entry<Vector, BlueprintBlock>> it = blocks.entrySet().iterator();
|
||||
Iterator<Entry<Vector, BlueprintBlock>> it2 = attached.entrySet().iterator();
|
||||
Iterator<Entry<Vector, List<BlueprintEntity>>> it3 = entities.entrySet().iterator();
|
||||
final Map<Vector, BlueprintBlock> blocks = blueprint.getBlocks() == null ? Collections.emptyMap() : blueprint.getBlocks();
|
||||
final Map<Vector, BlueprintBlock> attached = blueprint.getAttached() == null ? Collections.emptyMap() : blueprint.getAttached();
|
||||
final Map<Vector, List<BlueprintEntity>> entities = blueprint.getEntities() == null ? Collections.emptyMap() : blueprint.getEntities();
|
||||
|
||||
// Initial state & speed
|
||||
pasteState = PasteState.CHUNK_LOAD;
|
||||
final int pasteSpeed = plugin.getSettings().getPasteSpeed();
|
||||
|
||||
// If this is an island OVERWORLD paste, get the island owner.
|
||||
final Optional<User> owner = Optional.ofNullable(island)
|
||||
.filter(i -> location.getWorld().getEnvironment().equals(World.Environment.NORMAL))
|
||||
.map(i -> User.getInstance(i.getOwner()));
|
||||
// Tell the owner we're pasting blocks and how much time it might take
|
||||
owner.ifPresent(user -> {
|
||||
// Estimated time:
|
||||
double total = (double) blocks.size() + attached.size() + entities.size();
|
||||
BigDecimal time = BigDecimal.valueOf(total / (pasteSpeed * 20.0D) + (chunkLoadTime / 1000.0D)).setScale(1, RoundingMode.UP);
|
||||
user.sendMessage("commands.island.create.pasting.estimated-time", TextVariables.NUMBER, String.valueOf(time.doubleValue()));
|
||||
// We're pasting blocks!
|
||||
user.sendMessage("commands.island.create.pasting.blocks", TextVariables.NUMBER, String.valueOf(blocks.size() + attached.size()));
|
||||
});
|
||||
|
||||
pastingTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
||||
long timer = System.currentTimeMillis();
|
||||
int count = 0;
|
||||
if (pasteState.equals(PasteState.CHUNK_LOAD)) {
|
||||
pasteState = PasteState.CHUNK_LOADING;
|
||||
// Load chunk
|
||||
Util.getChunkAtAsync(location).thenRun(() -> {
|
||||
pasteState = PasteState.BLOCKS;
|
||||
long duration = System.currentTimeMillis() - timer;
|
||||
if (duration > chunkLoadTime) {
|
||||
chunkLoadTime = duration;
|
||||
}
|
||||
});
|
||||
}
|
||||
while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && it.hasNext()) {
|
||||
pasteBlock(location, it.next());
|
||||
count++;
|
||||
}
|
||||
while (pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && it2.hasNext()) {
|
||||
pasteBlock(location, it2.next());
|
||||
count++;
|
||||
}
|
||||
while (pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && it3.hasNext()) {
|
||||
pasteEntity(location, it3.next());
|
||||
count++;
|
||||
}
|
||||
// STATE SHIFT
|
||||
if (pasteState.equals(PasteState.BLOCKS) && !it.hasNext()) {
|
||||
// Blocks done
|
||||
// Next paste attachments
|
||||
pasteState = PasteState.ATTACHMENTS;
|
||||
}
|
||||
else if (pasteState.equals(PasteState.ATTACHMENTS) && !it2.hasNext()) {
|
||||
// Attachments done. Next paste entities
|
||||
pasteState = PasteState.ENTITIES;
|
||||
if (entities.size() != 0) {
|
||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(entities.size())));
|
||||
}
|
||||
}
|
||||
else if (pasteState.equals(PasteState.ENTITIES) && !it3.hasNext()) {
|
||||
pasteState = PasteState.DONE;
|
||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done"));
|
||||
}
|
||||
else if (pasteState.equals(PasteState.DONE)) {
|
||||
// All done. Cancel task
|
||||
// Set pos1 and 2 if this was a clipboard paste
|
||||
if (island == null && clipboard != null) {
|
||||
clipboard.setPos1(pos1);
|
||||
clipboard.setPos2(pos2);
|
||||
}
|
||||
result.complete(true);
|
||||
pasteState = PasteState.CANCEL;
|
||||
} else if (pasteState.equals(PasteState.CANCEL)) {
|
||||
// This state makes sure the follow-on task only ever runs once
|
||||
pastingTask.cancel();
|
||||
result.complete(true);
|
||||
}
|
||||
}, 0L, 1L);
|
||||
owner.ifPresent(user -> tellOwner(user, blocks.size(), attached.size(), entities.size(), plugin.getSettings().getPasteSpeed()));
|
||||
Bits bits = new Bits(blocks, attached, entities,
|
||||
blocks.entrySet().iterator(), attached.entrySet().iterator(), entities.entrySet().iterator(),
|
||||
plugin.getSettings().getPasteSpeed());
|
||||
pastingTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> pasterTask(result, owner, bits), 0L, 1L);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void pasterTask(CompletableFuture<Boolean> result, Optional<User> owner, Bits bits) {
|
||||
final int pasteSpeed = plugin.getSettings().getPasteSpeed();
|
||||
|
||||
long timer = System.currentTimeMillis();
|
||||
int count = 0;
|
||||
if (pasteState.equals(PasteState.CHUNK_LOAD)) {
|
||||
pasteState = PasteState.CHUNK_LOADING;
|
||||
// Load chunk
|
||||
Util.getChunkAtAsync(location).thenRun(() -> {
|
||||
pasteState = PasteState.BLOCKS;
|
||||
long duration = System.currentTimeMillis() - timer;
|
||||
if (duration > chunkLoadTime) {
|
||||
chunkLoadTime = duration;
|
||||
}
|
||||
});
|
||||
}
|
||||
while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && bits.it.hasNext()) {
|
||||
pasteBlock(location, bits.it.next());
|
||||
count++;
|
||||
}
|
||||
while (pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && bits.it2.hasNext()) {
|
||||
pasteBlock(location, bits.it2.next());
|
||||
count++;
|
||||
}
|
||||
while (pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && bits.it3.hasNext()) {
|
||||
pasteEntity(location, bits.it3.next());
|
||||
count++;
|
||||
}
|
||||
// STATE SHIFT
|
||||
if (pasteState.equals(PasteState.BLOCKS) && !bits.it.hasNext()) {
|
||||
// Blocks done
|
||||
// Next paste attachments
|
||||
pasteState = PasteState.ATTACHMENTS;
|
||||
}
|
||||
else if (pasteState.equals(PasteState.ATTACHMENTS) && !bits.it2.hasNext()) {
|
||||
// Attachments done. Next paste entities
|
||||
pasteState = PasteState.ENTITIES;
|
||||
if (bits.entities.size() != 0) {
|
||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(bits.entities.size())));
|
||||
}
|
||||
}
|
||||
else if (pasteState.equals(PasteState.ENTITIES) && !bits.it3.hasNext()) {
|
||||
pasteState = PasteState.DONE;
|
||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done"));
|
||||
}
|
||||
else if (pasteState.equals(PasteState.DONE)) {
|
||||
// All done. Cancel task
|
||||
// Set pos1 and 2 if this was a clipboard paste
|
||||
if (island == null && clipboard != null) {
|
||||
clipboard.setPos1(pos1);
|
||||
clipboard.setPos2(pos2);
|
||||
}
|
||||
pasteState = PasteState.CANCEL;
|
||||
result.complete(true);
|
||||
} else if (pasteState.equals(PasteState.CANCEL)) {
|
||||
// This state makes sure the follow-on task only ever runs once
|
||||
pastingTask.cancel();
|
||||
result.complete(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void tellOwner(User user, int blocksSize, int attachedSize, int entitiesSize, int pasteSpeed) {
|
||||
// Estimated time:
|
||||
double total = (double) blocksSize + attachedSize + entitiesSize;
|
||||
BigDecimal time = BigDecimal.valueOf(total / (pasteSpeed * 20.0D) + (chunkLoadTime / 1000.0D)).setScale(1, RoundingMode.UP);
|
||||
user.sendMessage("commands.island.create.pasting.estimated-time", TextVariables.NUMBER, String.valueOf(time.doubleValue()));
|
||||
// We're pasting blocks!
|
||||
user.sendMessage("commands.island.create.pasting.blocks", TextVariables.NUMBER, String.valueOf(blocksSize + attachedSize));
|
||||
}
|
||||
|
||||
private void pasteBlock(Location location, Entry<Vector, BlueprintBlock> entry) {
|
||||
World world = location.getWorld();
|
||||
Location pasteTo = location.clone().add(entry.getKey());
|
||||
@ -294,8 +305,8 @@ public class BlueprintPaster {
|
||||
writeSign(block, bpBlock.getSignLines(), bpBlock.isGlowingText());
|
||||
}
|
||||
// Chests, in general
|
||||
if (bs instanceof InventoryHolder) {
|
||||
Inventory ih = ((InventoryHolder)bs).getInventory();
|
||||
if (bs instanceof InventoryHolder holder) {
|
||||
Inventory ih = holder.getInventory();
|
||||
// Double chests are pasted as two blocks so inventory is filled twice.
|
||||
// This code stops over-filling for the first block.
|
||||
bpBlock.getInventory().forEach(ih::setItem);
|
||||
|
@ -56,23 +56,23 @@ public class BlueprintEntity {
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public void configureEntity(Entity e) {
|
||||
if (e instanceof Villager) {
|
||||
setVillager(e);
|
||||
if (e instanceof Villager villager) {
|
||||
setVillager(villager);
|
||||
}
|
||||
if (e instanceof Colorable) {
|
||||
((Colorable) e).setColor(color);
|
||||
if (e instanceof Colorable c) {
|
||||
c.setColor(color);
|
||||
}
|
||||
if (tamed != null && e instanceof Tameable) {
|
||||
((Tameable)e).setTamed(tamed);
|
||||
if (tamed != null && e instanceof Tameable tameable) {
|
||||
tameable.setTamed(tamed);
|
||||
}
|
||||
if (chest != null && e instanceof ChestedHorse) {
|
||||
((ChestedHorse)e).setCarryingChest(chest);
|
||||
if (chest != null && e instanceof ChestedHorse chestedHorse) {
|
||||
chestedHorse.setCarryingChest(chest);
|
||||
}
|
||||
if (adult != null && e instanceof Ageable) {
|
||||
if (adult != null && e instanceof Ageable ageable) {
|
||||
if (adult) {
|
||||
((Ageable)e).setAdult();
|
||||
ageable.setAdult();
|
||||
} else {
|
||||
((Ageable)e).setBaby();
|
||||
ageable.setBaby();
|
||||
}
|
||||
}
|
||||
if (e instanceof AbstractHorse horse) {
|
||||
@ -81,18 +81,17 @@ public class BlueprintEntity {
|
||||
inventory.forEach(horse.getInventory()::setItem);
|
||||
}
|
||||
}
|
||||
if (style != null && e instanceof Horse) {
|
||||
((Horse)e).setStyle(style);
|
||||
if (style != null && e instanceof Horse horse) {
|
||||
horse.setStyle(style);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param e - villager
|
||||
* @param v - villager
|
||||
* @since 1.16.0
|
||||
*/
|
||||
private void setVillager(Entity e) {
|
||||
Villager v = (Villager)e;
|
||||
private void setVillager(Villager v) {
|
||||
v.setProfession(profession == null ? Profession.NONE : profession);
|
||||
v.setVillagerExperience(experience == null ? 0 : experience);
|
||||
v.setVillagerLevel(level == null ? 0 : level);
|
||||
|
@ -698,7 +698,7 @@ public class Island implements DataObject, MetaDataAble {
|
||||
* @since 1.5.2
|
||||
*/
|
||||
public BoundingBox getBoundingBox() {
|
||||
return new BoundingBox(getMinX(), 0.0D, getMinZ(), getMaxX(), world.getMaxHeight(), getMaxZ());
|
||||
return new BoundingBox(getMinX(), world.getMinHeight(), getMinZ(), getMaxX(), world.getMaxHeight(), getMaxZ());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -289,7 +289,7 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
// Convert any serialized dots back to dots
|
||||
// In YAML dots . cause a lot of problems, so I serialize them as :dot:
|
||||
// There may be a better way to do this.
|
||||
key = key.replaceAll(":dot:", ".");
|
||||
key = key.replace(":dot:", ".");
|
||||
Object mapKey = deserialize(key,Class.forName(keyType.getTypeName()));
|
||||
if (mapKey == null) {
|
||||
continue;
|
||||
@ -452,7 +452,7 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
for (Entry<Object, Object> object : value.entrySet()) {
|
||||
// Serialize all key and values
|
||||
String key = (String)serialize(object.getKey());
|
||||
key = key.replaceAll("\\.", ":dot:");
|
||||
key = key.replace("\\.", ":dot:");
|
||||
result.put(key, serialize(object.getValue()));
|
||||
}
|
||||
// Save the list in the config file
|
||||
|
@ -17,6 +17,7 @@ import world.bentobox.bentobox.api.user.User;
|
||||
public class VaultHook extends Hook {
|
||||
|
||||
private static final String AMOUNT_MUST_BE_POSITIVE = "Amount must be positive.";
|
||||
private static final String PLAYER_OR_OFFLINEPLAYER_REQUIRED = "User must be a Player or an OfflinePlayer";
|
||||
private Economy economy;
|
||||
|
||||
public VaultHook() {
|
||||
@ -109,7 +110,7 @@ public class VaultHook extends Hook {
|
||||
*/
|
||||
public EconomyResponse withdraw(User user, double amount, World world) {
|
||||
if (!user.isOfflinePlayer()) {
|
||||
throw new IllegalArgumentException("User must be a Player or an OfflinePlayer");
|
||||
throw new IllegalArgumentException(PLAYER_OR_OFFLINEPLAYER_REQUIRED);
|
||||
}
|
||||
if (amount < 0.0D) {
|
||||
throw new IllegalArgumentException(AMOUNT_MUST_BE_POSITIVE);
|
||||
@ -149,7 +150,7 @@ public class VaultHook extends Hook {
|
||||
*/
|
||||
public EconomyResponse deposit(User user, double amount, World world) {
|
||||
if (!user.isOfflinePlayer()) {
|
||||
throw new IllegalArgumentException("User must be a Player or an OfflinePlayer");
|
||||
throw new IllegalArgumentException(PLAYER_OR_OFFLINEPLAYER_REQUIRED);
|
||||
}
|
||||
if (amount < 0.0D) {
|
||||
throw new IllegalArgumentException(AMOUNT_MUST_BE_POSITIVE);
|
||||
@ -198,7 +199,7 @@ public class VaultHook extends Hook {
|
||||
}
|
||||
|
||||
if (!user.isOfflinePlayer()) {
|
||||
throw new IllegalArgumentException("User must be a Player or an OfflinePlayer");
|
||||
throw new IllegalArgumentException(PLAYER_OR_OFFLINEPLAYER_REQUIRED);
|
||||
}
|
||||
|
||||
if (world == null)
|
||||
|
@ -30,6 +30,7 @@ import world.bentobox.bentobox.api.events.flags.FlagSettingChangeEvent;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
|
||||
@ -239,10 +240,20 @@ public class PVPListener extends FlagListener {
|
||||
if (e.getTo() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get previous island to skip reporting if island is not changed.
|
||||
Island previousIsland = this.getIslands().getIslandAt(e.getFrom()).orElse(null);
|
||||
|
||||
getIslands().getIslandAt(e.getTo()).ifPresent(island -> {
|
||||
if (island.getMemberSet(RanksManager.COOP_RANK).contains(e.getPlayer().getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.getFrom().getWorld() == e.getTo().getWorld() && island == previousIsland) {
|
||||
// do not report as it is the same world and same island.
|
||||
return;
|
||||
}
|
||||
|
||||
if (island.isAllowed(Flags.PVP_OVERWORLD)) {
|
||||
alertUser(e.getPlayer(), Flags.PVP_OVERWORLD);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
@ -54,6 +55,16 @@ public class ObsidianScoopingListener extends FlagListener {
|
||||
|| e.getClickedBlock().getRelative(e.getBlockFace()).getType().equals(Material.WATER)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Material.BUCKET.equals(e.getPlayer().getInventory().getItemInOffHand().getType()) &&
|
||||
Material.BUCKET.equals(e.getPlayer().getInventory().getItemInMainHand().getType()) &&
|
||||
EquipmentSlot.OFF_HAND.equals(e.getHand()))
|
||||
{
|
||||
// If player is holding bucket in both hands, then allow to interact only with main hand.
|
||||
// Prevents lava duplication glitch.
|
||||
return false;
|
||||
}
|
||||
|
||||
return lookForLava(e);
|
||||
}
|
||||
|
||||
|
@ -666,7 +666,28 @@ public class AddonsManager {
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public void allLoaded() {
|
||||
addons.forEach(Addon::allLoaded);
|
||||
this.getEnabledAddons().forEach(this::allLoaded);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method calls Addon#allLoaded in safe manner. If for some reason addon crashes on Addon#allLoaded, then
|
||||
* it will disable itself without harming other addons.
|
||||
* @param addon Addon that should trigger Addon#allLoaded method.
|
||||
*/
|
||||
private void allLoaded(@NonNull Addon addon) {
|
||||
try {
|
||||
addon.allLoaded();
|
||||
} catch (NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
|
||||
// Looks like the addon is incompatible, because it tries to refer to missing classes...
|
||||
this.handleAddonIncompatibility(addon, e);
|
||||
// Disable addon.
|
||||
this.disable(addon);
|
||||
} catch (Exception e) {
|
||||
// Unhandled exception. We'll give a bit of debug here.
|
||||
this.handleAddonError(addon, e);
|
||||
// Disable addon.
|
||||
this.disable(addon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ public class LocalesManager {
|
||||
} catch (InvalidConfigurationException e) {
|
||||
plugin.logError("Could not update locale file '" + lf + "' due to it being malformed: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Error updating locale file '" + lf + "': " + e.getMessage());
|
||||
plugin.logError("Error updating locale file for " + addon.getDescription().getName() + "'s '" + lf + "': " + e.getMessage());
|
||||
plugin.logStacktrace(e);
|
||||
}
|
||||
}
|
||||
|
@ -577,17 +577,17 @@ public class PlayersManager {
|
||||
plugin.getVault().ifPresent(vault -> vault.withdraw(target, vault.getBalance(target), world));
|
||||
}
|
||||
// Reset the health
|
||||
if (plugin.getIWM().isOnLeaveResetHealth(world)) {
|
||||
if (plugin.getIWM().isOnLeaveResetHealth(world) && target.isPlayer()) {
|
||||
Util.resetHealth(target.getPlayer());
|
||||
}
|
||||
|
||||
// Reset the hunger
|
||||
if (plugin.getIWM().isOnLeaveResetHunger(world)) {
|
||||
if (plugin.getIWM().isOnLeaveResetHunger(world) && target.isPlayer()) {
|
||||
target.getPlayer().setFoodLevel(20);
|
||||
}
|
||||
|
||||
// Reset the XP
|
||||
if (plugin.getIWM().isOnLeaveResetXP(world)) {
|
||||
if (plugin.getIWM().isOnLeaveResetXP(world) && target.isPlayer()) {
|
||||
target.getPlayer().setTotalExperience(0);
|
||||
}
|
||||
// Save player
|
||||
|
@ -196,7 +196,7 @@ public class WebManager {
|
||||
@NonNull
|
||||
private String getContent(@NonNull GitHubRepository repo, String fileName) {
|
||||
try {
|
||||
String content = repo.getContent(fileName).getContent().replaceAll("\\n", "");
|
||||
String content = repo.getContent(fileName).getContent().replaceAll("\\n", ""); // replaceAll is required here
|
||||
return new String(Base64.getDecoder().decode(content), StandardCharsets.UTF_8);
|
||||
} catch (IllegalAccessException e) {
|
||||
// Fail silently
|
||||
|
@ -21,7 +21,9 @@ import world.bentobox.bentobox.managers.RanksManager;
|
||||
*/
|
||||
public class IslandInfo {
|
||||
|
||||
private final BentoBox plugin;
|
||||
private static final String XZ1 = "[xz1]";
|
||||
private static final String RANGE = "[range]";
|
||||
private final BentoBox plugin;
|
||||
private final Island island;
|
||||
private final @Nullable UUID owner;
|
||||
private final World world;
|
||||
@ -57,23 +59,23 @@ public class IslandInfo {
|
||||
try {
|
||||
String dateTimeFormat = plugin.getLocalesManager().get("commands.admin.info.last-login-date-time-format");
|
||||
formattedDate = new SimpleDateFormat(dateTimeFormat).format(new Date(lastPlayed));
|
||||
} catch (NullPointerException | IllegalArgumentException ignored) {
|
||||
} catch (Exception ignored) {
|
||||
formattedDate = new Date(lastPlayed).toString();
|
||||
}
|
||||
user.sendMessage("commands.admin.info.last-login","[date]", formattedDate);
|
||||
|
||||
user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner)));
|
||||
user.sendMessage("commands.admin.info.deaths", TextVariables.NUMBER, String.valueOf(plugin.getPlayers().getDeaths(world, owner)));
|
||||
String resets = String.valueOf(plugin.getPlayers().getResets(world, owner));
|
||||
String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world));
|
||||
user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total);
|
||||
user.sendMessage("commands.admin.info.resets-left", TextVariables.NUMBER, resets, "[total]", total);
|
||||
// Show team members
|
||||
showMembers(user);
|
||||
}
|
||||
Vector location = island.getProtectionCenter().toVector();
|
||||
user.sendMessage("commands.admin.info.island-protection-center", TextVariables.XYZ, Util.xyz(location));
|
||||
user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(island.getCenter().toVector()));
|
||||
user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(new Vector(island.getMinX(), 0, island.getMinZ())), "[xz2]", Util.xyz(new Vector(island.getMaxX(), 0, island.getMaxZ())));
|
||||
user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(island.getProtectionRange()));
|
||||
user.sendMessage("commands.admin.info.island-coords", XZ1, Util.xyz(new Vector(island.getMinX(), 0, island.getMinZ())), "[xz2]", Util.xyz(new Vector(island.getMaxX(), 0, island.getMaxZ())));
|
||||
user.sendMessage("commands.admin.info.protection-range", RANGE, String.valueOf(island.getProtectionRange()));
|
||||
if (!island.getBonusRanges().isEmpty()) {
|
||||
user.sendMessage("commands.admin.info.protection-range-bonus-title");
|
||||
}
|
||||
@ -84,8 +86,8 @@ public class IslandInfo {
|
||||
user.sendMessage(brb.getMessage(), TextVariables.NUMBER, String.valueOf(brb.getRange()));
|
||||
}
|
||||
});
|
||||
user.sendMessage("commands.admin.info.max-protection-range", "[range]", String.valueOf(island.getMaxEverProtectionRange()));
|
||||
user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX() - 1, 0, island.getMaxProtectedZ() - 1)));
|
||||
user.sendMessage("commands.admin.info.max-protection-range", RANGE, String.valueOf(island.getMaxEverProtectionRange()));
|
||||
user.sendMessage("commands.admin.info.protection-coords", XZ1, Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX() - 1, 0, island.getMaxProtectedZ() - 1)));
|
||||
if (island.isSpawn()) {
|
||||
user.sendMessage("commands.admin.info.is-spawn");
|
||||
}
|
||||
@ -110,17 +112,17 @@ public class IslandInfo {
|
||||
user.sendMessage("commands.admin.info.unowned");
|
||||
} else {
|
||||
user.sendMessage("commands.admin.info.owner", "[owner]", plugin.getPlayers().getName(owner), TextVariables.UUID, owner.toString());
|
||||
user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(world, owner)));
|
||||
user.sendMessage("commands.admin.info.deaths", TextVariables.NUMBER, String.valueOf(plugin.getPlayers().getDeaths(world, owner)));
|
||||
String resets = String.valueOf(plugin.getPlayers().getResets(world, owner));
|
||||
String total = plugin.getIWM().getResetLimit(world) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(world));
|
||||
user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total);
|
||||
user.sendMessage("commands.admin.info.resets-left", TextVariables.NUMBER, resets, "[total]", total);
|
||||
// Show team members
|
||||
showMembers(user);
|
||||
}
|
||||
Vector location = island.getProtectionCenter().toVector();
|
||||
user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(location));
|
||||
user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(island.getProtectionRange()));
|
||||
user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX() - 1, 0, island.getMaxProtectedZ() - 1)));
|
||||
user.sendMessage("commands.admin.info.protection-range", RANGE, String.valueOf(island.getProtectionRange()));
|
||||
user.sendMessage("commands.admin.info.protection-coords", XZ1, Util.xyz(new Vector(island.getMinProtectedX(), 0, island.getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(island.getMaxProtectedX() - 1, 0, island.getMaxProtectedZ() - 1)));
|
||||
if (island.isSpawn()) {
|
||||
user.sendMessage("commands.admin.info.is-spawn");
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ public class SafeSpotTeleport {
|
||||
private final AtomicBoolean checking = new AtomicBoolean();
|
||||
private BukkitTask task;
|
||||
private boolean portal;
|
||||
private boolean cancelIfFail;
|
||||
// Locations
|
||||
private Location bestSpot;
|
||||
private Iterator<Pair<Integer, Integer>> chunksToScanIterator;
|
||||
@ -73,6 +74,7 @@ public class SafeSpotTeleport {
|
||||
this.result = builder.getResult();
|
||||
this.world = Objects.requireNonNull(location.getWorld());
|
||||
this.maxHeight = world.getMaxHeight() - 20;
|
||||
this.cancelIfFail = builder.isCancelIfFail();
|
||||
// Try to go
|
||||
Util.getChunkAtAsync(location).thenRun(() -> tryToGo(builder.getFailureMessage()));
|
||||
}
|
||||
@ -150,7 +152,7 @@ public class SafeSpotTeleport {
|
||||
if (!plugin.getIWM().inWorld(entity.getLocation())) {
|
||||
// Last resort
|
||||
player.performCommand("spawn");
|
||||
} else {
|
||||
} else if (!this.cancelIfFail) {
|
||||
// Create a spot for the player to be
|
||||
if (world.getEnvironment().equals(Environment.NETHER)) {
|
||||
makeAndTeleport(Material.NETHERRACK);
|
||||
@ -226,7 +228,7 @@ public class SafeSpotTeleport {
|
||||
// Check the safe spot at the current height
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
if (minY >= startY && checkBlock(chunk, x, startY, z)) {
|
||||
if (minY <= startY && checkBlock(chunk, x, startY, z)) {
|
||||
return true;
|
||||
}
|
||||
maxY = Math.max(chunk.getHighestBlockYAt(x, z), maxY);
|
||||
@ -336,6 +338,7 @@ public class SafeSpotTeleport {
|
||||
private Location location;
|
||||
private Runnable runnable;
|
||||
private Runnable failRunnable;
|
||||
private boolean cancelIfFail;
|
||||
|
||||
public Builder(BentoBox plugin) {
|
||||
this.plugin = plugin;
|
||||
@ -461,6 +464,20 @@ public class SafeSpotTeleport {
|
||||
return new SafeSpotTeleport(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method allows stopping "safe" block generation if teleportation fails.
|
||||
* @param cancelIfFail - value for canceling
|
||||
* @return Builder
|
||||
* @since 1.20.1
|
||||
*/
|
||||
public Builder cancelIfFail(boolean cancelIfFail)
|
||||
{
|
||||
this.cancelIfFail = cancelIfFail;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The task to run after the player is safely teleported.
|
||||
*
|
||||
@ -557,5 +574,13 @@ public class SafeSpotTeleport {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the cancelIfFail
|
||||
* @since 1.20.1
|
||||
*/
|
||||
public boolean isCancelIfFail()
|
||||
{
|
||||
return this.cancelIfFail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,6 +194,10 @@ public class ServerCompatibility {
|
||||
* @since 1.19.0
|
||||
*/
|
||||
V1_18_1(Compatibility.COMPATIBLE),
|
||||
/**
|
||||
* @since 1.20.1
|
||||
*/
|
||||
V1_18_2(Compatibility.COMPATIBLE),
|
||||
;
|
||||
|
||||
private final Compatibility compatibility;
|
||||
|
@ -652,8 +652,9 @@ commands:
|
||||
kick:
|
||||
description: "remove a member from your island"
|
||||
parameters: "<player>"
|
||||
owner-kicked: "&c The owner kicked you from the island in [gamemode]!"
|
||||
player-kicked: "&c The [name] kicked you from the island in [gamemode]!"
|
||||
cannot-kick: "&c You cannot kick yourself!"
|
||||
cannot-kick-rank: "&c Your rank does not allow to kick [name]!"
|
||||
success: "&b [name] &a has been kicked from your island."
|
||||
demote:
|
||||
description: "demote a player on your island down a rank"
|
||||
|
@ -184,6 +184,10 @@ public class IslandTeamKickCommandTest {
|
||||
RanksManager rm = new RanksManager();
|
||||
when(plugin.getRanksManager()).thenReturn(rm);
|
||||
|
||||
// Ranks
|
||||
when(island.getRank(uuid)).thenReturn(RanksManager.OWNER_RANK);
|
||||
when(island.getRank(user)).thenReturn(RanksManager.OWNER_RANK);
|
||||
when(island.getRank(notUUID)).thenReturn(RanksManager.MEMBER_RANK);
|
||||
}
|
||||
|
||||
@After
|
||||
@ -206,11 +210,75 @@ public class IslandTeamKickCommandTest {
|
||||
* Test method for {@link IslandTeamKickCommand#execute(User, String, java.util.List)}
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteNotTeamOwner() {
|
||||
when(im.getOwner(any(), any())).thenReturn(notUUID);
|
||||
public void testExecuteLowerTeamRank() {
|
||||
when(island.getRank(user)).thenReturn(RanksManager.MEMBER_RANK);
|
||||
when(island.getRank(notUUID)).thenReturn(RanksManager.SUB_OWNER_RANK);
|
||||
|
||||
when(pm.getUUID(any())).thenReturn(notUUID);
|
||||
when(pm.getName(notUUID)).thenReturn("poslovitch");
|
||||
|
||||
Set<UUID> members = new HashSet<>();
|
||||
members.add(notUUID);
|
||||
when(im.getMembers(any(), any())).thenReturn(members);
|
||||
|
||||
IslandTeamKickCommand itl = new IslandTeamKickCommand(ic);
|
||||
assertFalse(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch")));
|
||||
verify(user).sendMessage(eq("commands.island.team.kick.cannot-kick-rank"), eq(TextVariables.NAME), eq("poslovitch"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test method for {@link IslandTeamKickCommand#execute(User, String, java.util.List)}
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteEqualTeamRank() {
|
||||
when(island.getRank(user)).thenReturn(RanksManager.SUB_OWNER_RANK);
|
||||
when(island.getRank(notUUID)).thenReturn(RanksManager.SUB_OWNER_RANK);
|
||||
|
||||
when(pm.getUUID(any())).thenReturn(notUUID);
|
||||
when(pm.getName(notUUID)).thenReturn("poslovitch");
|
||||
|
||||
Set<UUID> members = new HashSet<>();
|
||||
members.add(notUUID);
|
||||
when(im.getMembers(any(), any())).thenReturn(members);
|
||||
|
||||
IslandTeamKickCommand itl = new IslandTeamKickCommand(ic);
|
||||
assertFalse(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch")));
|
||||
verify(user).sendMessage(eq("commands.island.team.kick.cannot-kick-rank"), eq(TextVariables.NAME), eq("poslovitch"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link IslandTeamKickCommand#execute(User, String, java.util.List)}
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteLargerTeamRank() {
|
||||
when(island.getRank(user)).thenReturn(RanksManager.SUB_OWNER_RANK);
|
||||
when(island.getRank(notUUID)).thenReturn(RanksManager.MEMBER_RANK);
|
||||
|
||||
when(pm.getUUID(any())).thenReturn(notUUID);
|
||||
when(pm.getName(notUUID)).thenReturn("poslovitch");
|
||||
|
||||
Set<UUID> members = new HashSet<>();
|
||||
members.add(notUUID);
|
||||
when(im.getMembers(any(), any())).thenReturn(members);
|
||||
|
||||
IslandTeamKickCommand itl = new IslandTeamKickCommand(ic);
|
||||
assertTrue(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch")));
|
||||
verify(im).removePlayer(any(), eq(notUUID));
|
||||
verify(user).sendMessage("commands.island.team.kick.success", TextVariables.NAME, "poslovitch");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link IslandTeamKickCommand#execute(User, String, java.util.List)}
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteNoCommandRank() {
|
||||
when(island.getRankCommand(anyString())).thenReturn(RanksManager.SUB_OWNER_RANK);
|
||||
when(island.getRank(user)).thenReturn(RanksManager.MEMBER_RANK);
|
||||
|
||||
IslandTeamKickCommand itl = new IslandTeamKickCommand(ic);
|
||||
assertFalse(itl.execute(user, itl.getLabel(), Collections.emptyList()));
|
||||
verify(user).sendMessage(eq("general.errors.not-owner"));
|
||||
verify(user).sendMessage(eq("general.errors.insufficient-rank"), eq(TextVariables.RANK), eq("ranks.member"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,10 +21,12 @@ import org.bukkit.metadata.MetadataValue;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.After;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@ -57,6 +59,7 @@ import world.bentobox.bentobox.util.Util;
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
public abstract class AbstractCommonSetup {
|
||||
|
||||
protected UUID uuid = UUID.randomUUID();
|
||||
@ -136,7 +139,7 @@ public abstract class AbstractCommonSetup {
|
||||
|
||||
// Island - nothing is allowed by default
|
||||
when(island.isAllowed(any())).thenReturn(false);
|
||||
when(island.isAllowed(any(), any())).thenReturn(false);
|
||||
when(island.isAllowed(any(User.class), any())).thenReturn(false);
|
||||
when(island.getOwner()).thenReturn(uuid);
|
||||
when(island.getMemberSet()).thenReturn(ImmutableSet.of(uuid));
|
||||
|
||||
|
@ -69,6 +69,10 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
position = new Vector(10,10,10);
|
||||
when(inv.getItemInMainHand()).thenReturn(new ItemStack(Material.NAME_TAG));
|
||||
|
||||
// Initialize the Flags class. This is a workaround to prevent weird errors when mocking
|
||||
// I think it's because the flag class needs to be initialized before use in argument matchers
|
||||
Flags.TRADING.setDefaultSetting(false);
|
||||
|
||||
// Class under test
|
||||
eil = new EntityInteractListener();
|
||||
}
|
||||
@ -91,7 +95,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
*/
|
||||
@Test
|
||||
public void testOnPlayerInteractAtEntityArmorStandAllowed() {
|
||||
when(island.isAllowed(any(), any())).thenReturn(true);
|
||||
when(island.isAllowed(any(User.class), any())).thenReturn(true);
|
||||
clickedEntity = mock(ArmorStand.class);
|
||||
when(clickedEntity.getLocation()).thenReturn(location);
|
||||
PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position, hand);
|
||||
@ -118,7 +122,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
*/
|
||||
@Test
|
||||
public void testOnPlayerInteractEntityHorseAllowed() {
|
||||
when(island.isAllowed(any(), any())).thenReturn(true);
|
||||
when(island.isAllowed(any(User.class), any())).thenReturn(true);
|
||||
clickedEntity = mock(Horse.class);
|
||||
when(clickedEntity.getLocation()).thenReturn(location);
|
||||
PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand);
|
||||
@ -145,7 +149,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
*/
|
||||
@Test
|
||||
public void testOnPlayerInteractEntityMinecartAllowed() {
|
||||
when(island.isAllowed(any(), any())).thenReturn(true);
|
||||
when(island.isAllowed(any(User.class), any())).thenReturn(true);
|
||||
clickedEntity = mock(RideableMinecart.class);
|
||||
when(clickedEntity.getLocation()).thenReturn(location);
|
||||
PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand);
|
||||
@ -172,7 +176,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
*/
|
||||
@Test
|
||||
public void testOnPlayerInteractEntityBoatAllowed() {
|
||||
when(island.isAllowed(any(), any())).thenReturn(true);
|
||||
when(island.isAllowed(any(User.class), any())).thenReturn(true);
|
||||
clickedEntity = mock(Boat.class);
|
||||
when(clickedEntity.getLocation()).thenReturn(location);
|
||||
PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand);
|
||||
@ -199,7 +203,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
*/
|
||||
@Test
|
||||
public void testOnPlayerInteractAtEntityVillagerAllowed() {
|
||||
when(island.isAllowed(any(), any())).thenReturn(true);
|
||||
when(island.isAllowed(any(User.class), any())).thenReturn(true);
|
||||
clickedEntity = mock(Villager.class);
|
||||
when(clickedEntity.getLocation()).thenReturn(location);
|
||||
PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand);
|
||||
@ -212,9 +216,10 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
* Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent)}.
|
||||
*/
|
||||
@Test
|
||||
|
||||
public void testOnPlayerInteractEntityNamingVillagerAllowedNoTrading() {
|
||||
when(island.isAllowed(any(), eq(Flags.TRADING))).thenReturn(false);
|
||||
when(island.isAllowed(any(), eq(Flags.NAME_TAG))).thenReturn(true);
|
||||
when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(false);
|
||||
when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(true);
|
||||
clickedEntity = mock(Villager.class);
|
||||
when(clickedEntity.getLocation()).thenReturn(location);
|
||||
PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand);
|
||||
@ -228,8 +233,8 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
*/
|
||||
@Test
|
||||
public void testOnPlayerInteractEntityNamingVillagerAllowedTradingNoNaming() {
|
||||
when(island.isAllowed(any(), eq(Flags.TRADING))).thenReturn(true);
|
||||
when(island.isAllowed(any(), eq(Flags.NAME_TAG))).thenReturn(false);
|
||||
when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(true);
|
||||
when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(false);
|
||||
clickedEntity = mock(Villager.class);
|
||||
when(clickedEntity.getLocation()).thenReturn(location);
|
||||
PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(player, clickedEntity, hand);
|
||||
@ -257,7 +262,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
*/
|
||||
@Test
|
||||
public void testOnPlayerInteractAtEntityWanderingTraderAllowed() {
|
||||
when(island.isAllowed(any(), any())).thenReturn(true);
|
||||
when(island.isAllowed(any(User.class), any())).thenReturn(true);
|
||||
clickedEntity = mock(WanderingTrader.class);
|
||||
when(clickedEntity.getType()).thenReturn(EntityType.WANDERING_TRADER);
|
||||
when(clickedEntity.getLocation()).thenReturn(location);
|
||||
@ -271,9 +276,12 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
* Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent)}.
|
||||
*/
|
||||
@Test
|
||||
|
||||
public void testOnPlayerInteractEntityNamingWanderingTraderAllowedNoTrading() {
|
||||
when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(false);
|
||||
when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(true);
|
||||
when(island.isAllowed(any(),
|
||||
eq(Flags.TRADING))).thenReturn(false);
|
||||
when(island.isAllowed(any(User.class),
|
||||
eq(Flags.NAME_TAG))).thenReturn(true);
|
||||
clickedEntity = mock(WanderingTrader.class);
|
||||
when(clickedEntity.getType()).thenReturn(EntityType.WANDERING_TRADER);
|
||||
when(clickedEntity.getLocation()).thenReturn(location);
|
||||
@ -287,6 +295,7 @@ public class EntityInteractListenerTest extends AbstractCommonSetup {
|
||||
* Test method for {@link world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent)}.
|
||||
*/
|
||||
@Test
|
||||
|
||||
public void testOnPlayerInteractEntityNamingWanderingTraderAllowedTradingNoNaming() {
|
||||
when(island.isAllowed(any(User.class), eq(Flags.TRADING))).thenReturn(true);
|
||||
when(island.isAllowed(any(User.class), eq(Flags.NAME_TAG))).thenReturn(false);
|
||||
|
@ -153,6 +153,11 @@ public class ObsidianScoopingListenerTest {
|
||||
map.put("OBSIDIAN_SCOOPING", true);
|
||||
when(ws.getWorldFlags()).thenReturn(map);
|
||||
|
||||
PlayerInventory playerInventory = mock(PlayerInventory.class);
|
||||
when(playerInventory.getItemInMainHand()).thenReturn(item);
|
||||
when(playerInventory.getItemInOffHand()).thenReturn(new ItemStack(Material.AIR));
|
||||
when(p.getInventory()).thenReturn(playerInventory);
|
||||
|
||||
// Addon
|
||||
when(iwm.getAddon(Mockito.any())).thenReturn(Optional.empty());
|
||||
}
|
||||
|
@ -169,6 +169,7 @@ public class PlayersManagerTest {
|
||||
when(user.getPlayer()).thenReturn(p);
|
||||
when(user.getName()).thenReturn("tastybento");
|
||||
when(user.isOnline()).thenReturn(true);
|
||||
when(user.isPlayer()).thenReturn(true);
|
||||
User.setPlugin(plugin);
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user