diff --git a/pom.xml b/pom.xml
index c856070..92442e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,13 +33,9 @@
-
- codemc-snapshots
- https://repo.codemc.org/repository/maven-snapshots
-
- codemc-releases
- https://repo.codemc.org/repository/maven-releases
+ bentoboxworld
+ https://repo.codemc.org/repository/bentoboxworld/
@@ -49,12 +45,12 @@
17
2.0.9
- 1.20.4-R0.1-SNAPSHOT
- 2.0.0-SNAPSHOT
+ 1.21.3-R0.1-SNAPSHOT
+ 2.7.1-SNAPSHOT
${build.version}-SNAPSHOT
- 4.3.0
+ 4.4.0
-LOCAL
BentoBoxWorld_Border
@@ -109,6 +105,10 @@
spigot-repo
https://hub.spigotmc.org/nexus/content/repositories/snapshots
+
+ bentoboxworld
+ https://repo.codemc.org/repository/bentoboxworld/
+
codemc
https://repo.codemc.org/repository/maven-snapshots/
diff --git a/src/main/java/world/bentobox/border/Border.java b/src/main/java/world/bentobox/border/Border.java
index 54b1ba8..78d7d65 100644
--- a/src/main/java/world/bentobox/border/Border.java
+++ b/src/main/java/world/bentobox/border/Border.java
@@ -14,6 +14,7 @@
import world.bentobox.bentobox.api.configuration.Config;
import world.bentobox.bentobox.api.metadata.MetaDataValue;
import world.bentobox.bentobox.util.Util;
+import world.bentobox.border.commands.BorderTypeCommand;
import world.bentobox.border.commands.IslandBorderCommand;
import world.bentobox.border.listeners.BorderShower;
import world.bentobox.border.listeners.PlayerListener;
@@ -52,6 +53,7 @@ public void onEnable() {
log("Border hooking into " + gameModeAddon.getDescription().getName());
gameModeAddon.getPlayerCommand().ifPresent(c -> new IslandBorderCommand(this, c, "border"));
+ gameModeAddon.getPlayerCommand().ifPresent(c -> new BorderTypeCommand(this, c, "bordertype"));
}
});
diff --git a/src/main/java/world/bentobox/border/commands/BorderTypeCommand.java b/src/main/java/world/bentobox/border/commands/BorderTypeCommand.java
index 44dc8d3..e4f183b 100644
--- a/src/main/java/world/bentobox/border/commands/BorderTypeCommand.java
+++ b/src/main/java/world/bentobox/border/commands/BorderTypeCommand.java
@@ -19,13 +19,12 @@
*/
public final class BorderTypeCommand extends CompositeCommand {
- public static final String BORDER_TYPE_COMMAND_PERM = "border.type";
private final Border addon;
private Island island;
private final List availableTypes;
- public BorderTypeCommand(Border addon, CompositeCommand parent) {
- super(addon, parent, "type");
+ public BorderTypeCommand(Border addon, CompositeCommand parent, String commandLabel) {
+ super(addon, parent, commandLabel);
this.addon = addon;
this.availableTypes = addon.getAvailableBorderTypesView()
.stream()
@@ -35,7 +34,7 @@ public BorderTypeCommand(Border addon, CompositeCommand parent) {
@Override
public void setup() {
- this.setPermission(BORDER_TYPE_COMMAND_PERM);
+ this.setPermission("border." + this.getLabel());
this.setDescription("border.set-type.description");
this.setOnlyPlayer(true);
}
diff --git a/src/main/java/world/bentobox/border/commands/IslandBorderCommand.java b/src/main/java/world/bentobox/border/commands/IslandBorderCommand.java
index 2963909..3a552cc 100644
--- a/src/main/java/world/bentobox/border/commands/IslandBorderCommand.java
+++ b/src/main/java/world/bentobox/border/commands/IslandBorderCommand.java
@@ -28,7 +28,7 @@ public void setup() {
this.setOnlyPlayer(true);
setConfigurableRankCommand();
- new BorderTypeCommand(this.getAddon(), this);
+ new BorderTypeCommand(this.getAddon(), this, "type");
}
@Override
diff --git a/src/main/java/world/bentobox/border/listeners/BlockListener.java b/src/main/java/world/bentobox/border/listeners/BlockListener.java
new file mode 100644
index 0000000..a7d187d
--- /dev/null
+++ b/src/main/java/world/bentobox/border/listeners/BlockListener.java
@@ -0,0 +1,325 @@
+package world.bentobox.border.listeners;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+import org.bukkit.Bukkit;
+import org.bukkit.GameMode;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.BlockFace;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDismountEvent;
+import org.bukkit.event.entity.EntityMountEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.player.PlayerRespawnEvent;
+import org.bukkit.event.player.PlayerTeleportEvent;
+import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
+import org.bukkit.event.vehicle.VehicleMoveEvent;
+import org.bukkit.scheduler.BukkitTask;
+import org.bukkit.util.NumberConversions;
+import org.bukkit.util.RayTraceResult;
+import org.bukkit.util.Vector;
+
+import world.bentobox.bentobox.api.events.island.IslandProtectionRangeChangeEvent;
+import world.bentobox.bentobox.api.flags.Flag;
+import world.bentobox.bentobox.api.metadata.MetaDataValue;
+import world.bentobox.bentobox.api.user.User;
+import world.bentobox.bentobox.util.Util;
+import world.bentobox.border.Border;
+import world.bentobox.border.PerPlayerBorderProxy;
+import world.bentobox.border.commands.IslandBorderCommand;
+
+/**
+ * @author tastybento
+ */
+public class BlockListener implements Listener {
+
+ private static final Vector XZ = new Vector(1,0,1);
+ private final Border addon;
+ private Set inTeleport;
+ private final BorderShower show;
+ private Map mountedPlayers = new HashMap<>();
+
+ public BlockListener(Border addon) {
+ this.addon = addon;
+ inTeleport = new HashSet<>();
+ this.show = addon.getBorderShower();
+ }
+
+ @EventHandler(priority = EventPriority.NORMAL)
+ public void onPlayerJoin(PlayerJoinEvent e) {
+ // Run one-tick after joining because meta data cannot be set otherwise
+ Bukkit.getScheduler().runTask(addon.getPlugin(), () -> processEvent(e));
+ }
+
+ protected void processEvent(PlayerJoinEvent e) {
+ User user = User.getInstance(e.getPlayer());
+
+ show.hideBorder(user);
+ // Just for sure, disable world Border
+ user.getPlayer().setWorldBorder(null);
+
+ // Get the game mode that this player is in
+ addon.getPlugin().getIWM().getAddon(e.getPlayer().getWorld()).map(gma -> gma.getPermissionPrefix()).filter(
+ permPrefix -> !e.getPlayer().hasPermission(permPrefix + IslandBorderCommand.BORDER_COMMAND_PERM))
+ .ifPresent(permPrefix -> {
+ // Restore barrier on/off to default
+ user.putMetaData(BorderShower.BORDER_STATE_META_DATA,
+ new MetaDataValue(addon.getSettings().isShowByDefault()));
+ if (!e.getPlayer().hasPermission(permPrefix + "border.type") && !e.getPlayer().hasPermission(permPrefix + "border.bordertype")) {
+ // Restore default barrier type to player
+ MetaDataValue metaDataValue = new MetaDataValue(addon.getSettings().getType().getId());
+ user.putMetaData(PerPlayerBorderProxy.BORDER_BORDERTYPE_META_DATA, metaDataValue);
+ }
+ });
+
+ // Show the border if required one tick after
+ Bukkit.getScheduler().runTask(addon.getPlugin(), () -> addon.getIslands().getIslandAt(e.getPlayer().getLocation()).ifPresent(i ->
+ show.showBorder(e.getPlayer(), i)));
+ }
+
+ @EventHandler(priority = EventPriority.NORMAL)
+ public void onPlayerQuit(PlayerQuitEvent e) {
+ show.clearUser(User.getInstance(e.getPlayer()));
+ }
+
+ @EventHandler(priority = EventPriority.NORMAL)
+ public void onPlayerRespawn(PlayerRespawnEvent e) {
+ show.clearUser(User.getInstance(e.getPlayer()));
+ Bukkit.getScheduler().runTask(addon.getPlugin(), () -> addon.getIslands().getIslandAt(e.getPlayer().getLocation()).ifPresent(i ->
+ show.showBorder(e.getPlayer(), i)));
+ }
+
+ private boolean isOn(Player player) {
+ // Check if border is off
+ User user = User.getInstance(player);
+ return user.getMetaData(BorderShower.BORDER_STATE_META_DATA).map(MetaDataValue::asBoolean)
+ .orElse(addon.getSettings().isShowByDefault());
+
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onPlayerTeleport(PlayerTeleportEvent e) {
+ Player player = e.getPlayer();
+ if (!isOn(player)) {
+ return;
+ }
+ Location to = e.getTo();
+
+ show.clearUser(User.getInstance(player));
+
+ if (to == null || !addon.inGameWorld(to.getWorld())) {
+ return;
+ }
+
+ TeleportCause cause = e.getCause();
+ boolean isBlacklistedCause = cause == TeleportCause.ENDER_PEARL || cause == TeleportCause.CHORUS_FRUIT;
+
+ Bukkit.getScheduler().runTask(addon.getPlugin(), () ->
+ addon.getIslands().getIslandAt(to).ifPresentOrElse(i -> {
+ Optional boxedEnderPearlFlag = i.getPlugin().getFlagsManager().getFlag("ALLOW_MOVE_BOX");
+
+ if (isBlacklistedCause
+ && (!i.getProtectionBoundingBox().contains(to.toVector())
+ || !i.onIsland(player.getLocation()))) {
+ e.setCancelled(true);
+ }
+
+ if (boxedEnderPearlFlag.isPresent()
+ && boxedEnderPearlFlag.get().isSetForWorld(to.getWorld())
+ && cause == TeleportCause.ENDER_PEARL) {
+ e.setCancelled(false);
+ }
+
+ show.showBorder(player, i);
+ }, () -> {
+ if (isBlacklistedCause) {
+ e.setCancelled(true);
+ return;
+ }
+ })
+ );
+ }
+
+ @EventHandler(priority = EventPriority.NORMAL)
+ public void onPlayerLeaveIsland(PlayerMoveEvent e) {
+ Player p = e.getPlayer();
+ if (!isOn(p)) {
+ return;
+ }
+ Location from = e.getFrom();
+ if (!addon.getSettings().isReturnTeleport() || !outsideCheck(e.getPlayer(), from, e.getTo())) {
+ return;
+ }
+ // Move the player back inside the border
+ if (addon.getIslands().getProtectedIslandAt(from).isPresent()) {
+ e.setCancelled(true);
+ inTeleport.add(p.getUniqueId());
+ Util.teleportAsync(p, from).thenRun(() -> inTeleport.remove(p.getUniqueId()));
+ return;
+ }
+ // Backtrack
+ addon.getIslands().getIslandAt(p.getLocation()).ifPresent(i -> {
+ Vector unitVector = i.getProtectionCenter().toVector().subtract(p.getLocation().toVector()).normalize()
+ .multiply(new Vector(1,0,1));
+ if (unitVector.lengthSquared() <= 0D) {
+ // Direction is zero, so nothing to do; cannot move.
+ return;
+ }
+ RayTraceResult r = i.getProtectionBoundingBox().rayTrace(p.getLocation().toVector(), unitVector, i.getRange());
+ if (r != null && checkFinite(r.getHitPosition())) {
+ inTeleport.add(p.getUniqueId());
+ Location targetPos = r.getHitPosition().toLocation(p.getWorld(), p.getLocation().getYaw(), p.getLocation().getPitch());
+
+ if (!e.getPlayer().isFlying() && addon.getSettings().isReturnTeleportBlock()
+ && !addon.getIslands().isSafeLocation(targetPos)) {
+ switch (targetPos.getWorld().getEnvironment()) {
+ case NETHER:
+ targetPos.getBlock().getRelative(BlockFace.DOWN).setType(Material.NETHERRACK);
+ break;
+ case THE_END:
+ targetPos.getBlock().getRelative(BlockFace.DOWN).setType(Material.END_STONE);
+ break;
+ default:
+ targetPos.getBlock().getRelative(BlockFace.DOWN).setType(Material.STONE);
+ break;
+ }
+ }
+ Util.teleportAsync(p, targetPos).thenRun(() -> inTeleport.remove(p.getUniqueId()));
+ }
+ });
+ }
+
+ public boolean checkFinite(Vector toCheck) {
+ return NumberConversions.isFinite(toCheck.getX()) && NumberConversions.isFinite(toCheck.getY())
+ && NumberConversions.isFinite(toCheck.getZ());
+ }
+
+ /**
+ * Check if the player is outside the island protection zone that they are supposed to be in.
+ * @param player - player moving
+ * @param from - from location
+ * @param to - to location
+ * @return true if outside the island protection zone
+ */
+ private boolean outsideCheck(Player player, Location from, Location to) {
+ User user = Objects.requireNonNull(User.getInstance(player));
+
+ if ((from.getWorld() != null && from.getWorld().equals(to.getWorld())
+ && from.toVector().multiply(XZ).equals(to.toVector().multiply(XZ)))
+ || !addon.inGameWorld(player.getWorld())
+ || user.getPlayer().getGameMode() == GameMode.SPECTATOR
+ // || !addon.getIslands().getIslandAt(to).filter(i -> addon.getIslands().locationIsOnIsland(player, i.getProtectionCenter())).isPresent()
+ || !user.getMetaData(BorderShower.BORDER_STATE_META_DATA).map(MetaDataValue::asBoolean).orElse(addon.getSettings().isShowByDefault())) {
+ return false;
+ }
+ return addon.getIslands().getIslandAt(to).filter(i -> !i.onIsland(to)).isPresent();
+ }
+
+ /**
+ * Runs a task while the player is mounting an entity and eject
+ * if the entity went outside the protection range
+ * @param event - event
+ */
+ @EventHandler
+ public void onEntityMount(EntityMountEvent event) {
+ Entity entity = event.getEntity();
+ if (!(entity instanceof Player player)) {
+ return;
+ }
+
+ mountedPlayers.put(player, Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> {
+ Location loc = player.getLocation();
+
+ if (!addon.inGameWorld(loc.getWorld())) {
+ return;
+ }
+ // Eject from mount if outside the protection range
+ if (addon.getIslands().getProtectedIslandAt(loc).isEmpty()) {
+ // Force the dismount event for custom entities
+ if (!event.getMount().eject()) {
+ var dismountEvent = new EntityDismountEvent(player, event.getMount());
+ Bukkit.getPluginManager().callEvent(dismountEvent);
+ }
+ }
+ }, 1, 20));
+ }
+
+ /**
+ * Cancel the running task if the player was mounting an entity
+ * @param event - event
+ */
+ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
+ public void onEntityDismount(EntityDismountEvent event) {
+ Entity entity = event.getEntity();
+ if (!(entity instanceof Player player)) {
+ return;
+ }
+
+ BukkitTask task = mountedPlayers.get(player);
+ if (task == null) {
+ return;
+ }
+
+ task.cancel();
+ mountedPlayers.remove(player);
+ }
+
+
+ /**
+ * Refreshes the barrier view when the player moves (more than just moving their head)
+ * @param e event
+ */
+ @EventHandler(priority = EventPriority.NORMAL)
+ public void onPlayerMove(PlayerMoveEvent e) {
+ // Remove head movement
+ if (!e.getFrom().toVector().equals(e.getTo().toVector())) {
+ addon.getIslands()
+ .getIslandAt(e.getPlayer().getLocation())
+ .ifPresent(i -> show.refreshView(User.getInstance(e.getPlayer()), i));
+ }
+ }
+
+ /**
+ * Refresh the view when riding in a vehicle
+ * @param e event
+ */
+ @EventHandler(priority = EventPriority.NORMAL)
+ public void onVehicleMove(VehicleMoveEvent e) {
+ // Remove head movement
+ if (!e.getFrom().toVector().equals(e.getTo().toVector())) {
+ e.getVehicle().getPassengers().stream()
+ .filter(Player.class::isInstance)
+ .map(Player.class::cast)
+ .forEach(p -> addon
+ .getIslands()
+ .getIslandAt(p.getLocation())
+ .ifPresent(i -> show.refreshView(User.getInstance(p), i)));
+ }
+ }
+
+ /**
+ * Hide and then show the border to react to the change in protection area
+ * @param e
+ */
+ @EventHandler(priority = EventPriority.NORMAL)
+ public void onProtectionRangeChange(IslandProtectionRangeChangeEvent e) {
+ // Hide and show again
+ e.getIsland().getPlayersOnIsland().forEach(player -> {
+ show.hideBorder(User.getInstance(player));
+ show.showBorder(player, e.getIsland());
+ });
+ }
+}
diff --git a/src/main/java/world/bentobox/border/listeners/PlayerListener.java b/src/main/java/world/bentobox/border/listeners/PlayerListener.java
index 547027f..b62d066 100644
--- a/src/main/java/world/bentobox/border/listeners/PlayerListener.java
+++ b/src/main/java/world/bentobox/border/listeners/PlayerListener.java
@@ -39,7 +39,6 @@
import world.bentobox.bentobox.util.Util;
import world.bentobox.border.Border;
import world.bentobox.border.PerPlayerBorderProxy;
-import world.bentobox.border.commands.BorderTypeCommand;
import world.bentobox.border.commands.IslandBorderCommand;
/**
@@ -75,16 +74,16 @@ protected void processEvent(PlayerJoinEvent e) {
// Get the game mode that this player is in
addon.getPlugin().getIWM().getAddon(e.getPlayer().getWorld()).map(gma -> gma.getPermissionPrefix()).filter(
permPrefix -> !e.getPlayer().hasPermission(permPrefix + IslandBorderCommand.BORDER_COMMAND_PERM))
- .ifPresent(permPrefix -> {
- // Restore barrier on/off to default
- user.putMetaData(BorderShower.BORDER_STATE_META_DATA,
- new MetaDataValue(addon.getSettings().isShowByDefault()));
- if (!e.getPlayer().hasPermission(permPrefix + BorderTypeCommand.BORDER_TYPE_COMMAND_PERM)) {
+ .ifPresent(permPrefix -> {
+ // Restore barrier on/off to default
+ user.putMetaData(BorderShower.BORDER_STATE_META_DATA,
+ new MetaDataValue(addon.getSettings().isShowByDefault()));
+ if (!e.getPlayer().hasPermission(permPrefix + "border.type") && !e.getPlayer().hasPermission(permPrefix + "border.bordertype")) {
// Restore default barrier type to player
MetaDataValue metaDataValue = new MetaDataValue(addon.getSettings().getType().getId());
user.putMetaData(PerPlayerBorderProxy.BORDER_BORDERTYPE_META_DATA, metaDataValue);
}
- });
+ });
// Show the border if required one tick after
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> addon.getIslands().getIslandAt(e.getPlayer().getLocation()).ifPresent(i ->
@@ -103,14 +102,25 @@ public void onPlayerRespawn(PlayerRespawnEvent e) {
show.showBorder(e.getPlayer(), i)));
}
+ private boolean isOn(Player player) {
+ // Check if border is off
+ User user = User.getInstance(player);
+ return user.getMetaData(BorderShower.BORDER_STATE_META_DATA).map(MetaDataValue::asBoolean)
+ .orElse(addon.getSettings().isShowByDefault());
+
+ }
+
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent e) {
Player player = e.getPlayer();
+ if (!isOn(player)) {
+ return;
+ }
Location to = e.getTo();
show.clearUser(User.getInstance(player));
- if (!addon.inGameWorld(to.getWorld())) {
+ if (to == null || !addon.inGameWorld(to.getWorld())) {
return;
}
@@ -146,6 +156,9 @@ public void onPlayerTeleport(PlayerTeleportEvent e) {
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerLeaveIsland(PlayerMoveEvent e) {
Player p = e.getPlayer();
+ if (!isOn(p)) {
+ return;
+ }
Location from = e.getFrom();
if (!addon.getSettings().isReturnTeleport() || !outsideCheck(e.getPlayer(), from, e.getTo())) {
return;
@@ -215,7 +228,7 @@ private boolean outsideCheck(Player player, Location from, Location to) {
return addon.getIslands().getIslandAt(to).filter(i -> !i.onIsland(to)).isPresent();
}
- /**
+ /**
* Runs a task while the player is mounting an entity and eject
* if the entity went outside the protection range
* @param event - event
diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml
index ef18ae2..bedc91a 100644
--- a/src/main/resources/addon.yml
+++ b/src/main/resources/addon.yml
@@ -14,4 +14,7 @@ permissions:
default: op
'[gamemode].border.type':
description: Player can use border type setting command
- default: true
\ No newline at end of file
+ default: true
+ '[gamemode].bordertype':
+ description: Player can use bordertype command to change the border type
+ default: false
\ No newline at end of file
diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml
index 82caaa9..7718225 100644
--- a/src/main/resources/locales/lv.yml
+++ b/src/main/resources/locales/lv.yml
@@ -4,3 +4,7 @@ border:
description: pārslēdz iespēju redzēt robežu
border-on: "&a Robeža ieslēgta."
border-off: "&a Robeža izslēgta."
+ set-type:
+ description: maina apmales veidu
+ changed: "&a Apmales veids mainīts uz &b[type]&a."
+ error-unavailable-type: "&c Šis veids nav pieejams vai neeksistē."
diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml
index ecf0f69..f2601e4 100644
--- a/src/main/resources/locales/zh-CN.yml
+++ b/src/main/resources/locales/zh-CN.yml
@@ -1,10 +1,9 @@
----
border:
toggle:
- description: 打开/关闭边框
- border-on: "&a 启用边框。"
- border-off: "&a 边框已禁用。"
+ description: 启用/禁用边界屏障
+ border-on: '&a边界屏障已启用.'
+ border-off: '&a边界屏障已禁用.'
set-type:
- description: 更改边框的类型
- changed: "&a 边框类型更改为 &b[type]&a。"
- error-unavailable-type: "&c 此类型不可用或不存在。"
+ description: 更改边界屏障类型
+ changed: '&a边界屏障已更改为: &b[type]&a.'
+ error-unavailable-type: '&c该屏障类型不可用或不存在.'
diff --git a/src/test/java/world/bentobox/border/commands/BorderTypeCommandTest.java b/src/test/java/world/bentobox/border/commands/BorderTypeCommandTest.java
index b2bbd3a..f50e1b2 100644
--- a/src/test/java/world/bentobox/border/commands/BorderTypeCommandTest.java
+++ b/src/test/java/world/bentobox/border/commands/BorderTypeCommandTest.java
@@ -23,7 +23,6 @@
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.eclipse.jdt.annotation.Nullable;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -139,14 +138,7 @@ public void setUp() throws Exception {
when(addon.getSettings()).thenReturn(settings);
- ic = new BorderTypeCommand(addon, ac);
- }
-
- /**
- * @throws java.lang.Exception
- */
- @After
- public void tearDown() throws Exception {
+ ic = new BorderTypeCommand(addon, ac, "type");
}
/**