diff --git a/pom.xml b/pom.xml
index 79dc81a..276e803 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,13 +41,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/
@@ -58,17 +54,17 @@
2.0.9
- 1.20.1-R0.1-SNAPSHOT
+ 1.21.3-R0.1-SNAPSHOT
- 1.20.1-R0.1-SNAPSHOT
- 1.24.1-SNAPSHOT
+ 1.21.3-R0.1-SNAPSHOT
+ 2.7.1-SNAPSHOT
${build.version}-SNAPSHOT
-LOCAL
- 1.3.0
+ 1.4.0
bentobox-world
https://sonarcloud.io
@@ -124,6 +120,10 @@
papermc
https://repo.papermc.io/repository/maven-public/
+
+ bentoboxworld
+ https://repo.codemc.org/repository/bentoboxworld/
+
codemc
https://repo.codemc.org/repository/maven-snapshots/
diff --git a/src/main/java/world/bentobox/parkour/Parkour.java b/src/main/java/world/bentobox/parkour/Parkour.java
index f8ba2b8..4c8e943 100644
--- a/src/main/java/world/bentobox/parkour/Parkour.java
+++ b/src/main/java/world/bentobox/parkour/Parkour.java
@@ -1,5 +1,6 @@
package world.bentobox.parkour;
+import java.util.ArrayList;
import java.util.HashMap;
import org.bukkit.Material;
@@ -91,7 +92,7 @@ public void setup() {
adminCommand = new DefaultAdminCommand(this) {
};
- parkourRunRecord = new ParkourRunRecord(new HashMap<>(), new HashMap<>());
+ parkourRunRecord = new ParkourRunRecord(new HashMap<>(), new HashMap<>(), new ArrayList<>());
registerFlag(PARKOUR_CREATIVE);
diff --git a/src/main/java/world/bentobox/parkour/ParkourRunRecord.java b/src/main/java/world/bentobox/parkour/ParkourRunRecord.java
index 82240f9..797ddac 100644
--- a/src/main/java/world/bentobox/parkour/ParkourRunRecord.java
+++ b/src/main/java/world/bentobox/parkour/ParkourRunRecord.java
@@ -1,11 +1,12 @@
package world.bentobox.parkour;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Location;
-public record ParkourRunRecord(Map checkpoints, Map timers) {
+public record ParkourRunRecord(Map checkpoints, Map timers, List currentlyTeleporting) {
/**
* Clears any current times or checkpoints
* @param uuid UUID of runner
@@ -13,6 +14,7 @@ public record ParkourRunRecord(Map checkpoints, Map
public void clear(UUID uuid) {
checkpoints.remove(uuid);
timers.remove(uuid);
+ currentlyTeleporting.remove(uuid);
}
}
diff --git a/src/main/java/world/bentobox/parkour/Settings.java b/src/main/java/world/bentobox/parkour/Settings.java
index 6d14156..43d82f8 100644
--- a/src/main/java/world/bentobox/parkour/Settings.java
+++ b/src/main/java/world/bentobox/parkour/Settings.java
@@ -14,6 +14,7 @@
import com.google.common.base.Enums;
+import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.configuration.ConfigComment;
import world.bentobox.bentobox.api.configuration.ConfigEntry;
import world.bentobox.bentobox.api.configuration.StoreAt;
@@ -138,6 +139,15 @@ public class Settings implements WorldSettings {
@ConfigEntry(path = "world.max-areas")
private int maxIslands = -1;
+ @ConfigComment("The number of concurrent areas a player can have")
+ @ConfigComment("A value of 0 will use the BentoBox config.yml default")
+ @ConfigEntry(path = "world.concurrent-area")
+ private int concurrentIslands = 0;
+
+ @ConfigComment("Disallow team members from having their own area.")
+ @ConfigEntry(path = "world.disallow-team-member-areas")
+ private boolean disallowTeamMemberIslands = true;
+
@ConfigComment("The default game mode for this world. Players will be set to this mode when they create")
@ConfigComment("a new area for example. Options are SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR")
@ConfigEntry(path = "world.default-game-mode")
@@ -148,7 +158,7 @@ public class Settings implements WorldSettings {
private Biome defaultBiome = Biome.PLAINS;
@ConfigComment("The default biome for the nether world (this may affect what mobs can spawn)")
@ConfigEntry(path = "world.default-nether-biome")
- private Biome defaultNetherBiome = Enums.getIfPresent(Biome.class, "NETHER").or(Enums.getIfPresent(Biome.class, "NETHER_WASTES").or(Biome.BADLANDS));
+ private Biome defaultNetherBiome = Biome.NETHER_WASTES;
@ConfigComment("The default biome for the end world (this may affect what mobs can spawn)")
@ConfigEntry(path = "world.default-end-biome")
private Biome defaultEndBiome = Biome.THE_END;
@@ -1721,4 +1731,36 @@ public Biome getDefaultEndBiome() {
public void setDefaultEndBiome(Biome defaultEndBiome) {
this.defaultEndBiome = defaultEndBiome;
}
+
+ /**
+ * @return the concurrentIslands
+ */
+ @Override
+ public int getConcurrentIslands() {
+ if (concurrentIslands <= 0) {
+ return BentoBox.getInstance().getSettings().getIslandNumber();
+ }
+ return concurrentIslands;
+ }
+
+ /**
+ * @param concurrentIslands the concurrentIslands to set
+ */
+ public void setConcurrentIslands(int concurrentIslands) {
+ this.concurrentIslands = concurrentIslands;
+ }
+
+ /**
+ * @return the disallowTeamMemberIslands
+ */
+ public boolean isDisallowTeamMemberIslands() {
+ return disallowTeamMemberIslands;
+ }
+
+ /**
+ * @param disallowTeamMemberIslands the disallowTeamMemberIslands to set
+ */
+ public void setDisallowTeamMemberIslands(boolean disallowTeamMemberIslands) {
+ this.disallowTeamMemberIslands = disallowTeamMemberIslands;
+ }
}
diff --git a/src/main/java/world/bentobox/parkour/commands/ClearTopCommand.java b/src/main/java/world/bentobox/parkour/commands/ClearTopCommand.java
index 4ecb899..53b2afc 100644
--- a/src/main/java/world/bentobox/parkour/commands/ClearTopCommand.java
+++ b/src/main/java/world/bentobox/parkour/commands/ClearTopCommand.java
@@ -61,7 +61,8 @@ public boolean canExecute(User user, String label, List args) {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
- user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
+ user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
+ user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Check the name of the score to clear
diff --git a/src/main/java/world/bentobox/parkour/commands/RemoveWarpCommand.java b/src/main/java/world/bentobox/parkour/commands/RemoveWarpCommand.java
index a6ba496..b72c4da 100644
--- a/src/main/java/world/bentobox/parkour/commands/RemoveWarpCommand.java
+++ b/src/main/java/world/bentobox/parkour/commands/RemoveWarpCommand.java
@@ -7,6 +7,7 @@
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.managers.RanksManager;
import world.bentobox.parkour.Parkour;
import world.bentobox.parkour.ParkourManager;
@@ -39,7 +40,7 @@ public boolean canExecute(User user, String label, List args) {
// Check rank to use command
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
- user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
+ user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
diff --git a/src/main/java/world/bentobox/parkour/commands/SetWarpCommand.java b/src/main/java/world/bentobox/parkour/commands/SetWarpCommand.java
index e2ce851..c3bb71b 100644
--- a/src/main/java/world/bentobox/parkour/commands/SetWarpCommand.java
+++ b/src/main/java/world/bentobox/parkour/commands/SetWarpCommand.java
@@ -10,6 +10,7 @@
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.managers.RanksManager;
import world.bentobox.parkour.Parkour;
import world.bentobox.parkour.ParkourManager;
@@ -46,7 +47,8 @@ public boolean canExecute(User user, String label, List args) {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
- user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
+ user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
+ user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
Optional start = ((Parkour) getAddon()).getParkourManager().getStart(island);
diff --git a/src/main/java/world/bentobox/parkour/commands/WarpCommand.java b/src/main/java/world/bentobox/parkour/commands/WarpCommand.java
index f318815..ff03b9c 100644
--- a/src/main/java/world/bentobox/parkour/commands/WarpCommand.java
+++ b/src/main/java/world/bentobox/parkour/commands/WarpCommand.java
@@ -19,8 +19,8 @@
/**
* Warps to a
- * @author tastybento
*
+ * @author tastybento
*/
public class WarpCommand extends CompositeCommand {
@@ -48,7 +48,7 @@ public boolean canExecute(User user, String label, List args) {
}
if (args.isEmpty()) {
Optional island = getIslands().getIslandAt(user.getLocation());
- if (island.isEmpty() || !((Parkour)getAddon()).inWorld(user.getWorld())) {
+ if (island.isEmpty() || !((Parkour) getAddon()).inWorld(user.getWorld())) {
user.sendMessage("parkour.errors.not-on-island");
this.showHelp(this, user);
return false;
@@ -81,18 +81,18 @@ public boolean execute(User user, String label, List args) {
// Teleport user
user.getPlayer().playSound(user.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 1F, 1F);
user.getPlayer().playSound(warpSpot, Sound.ENTITY_BAT_TAKEOFF, 1F, 1F);
- Util.teleportAsync(user.getPlayer(), warpSpot.clone().add(new Vector(0.5, 0.5, 0.5)), TeleportCause.COMMAND);
+ Util.teleportAsync(user.getPlayer(), warpSpot, TeleportCause.COMMAND);
return true;
}
@Override
public Optional> tabComplete(User user, String alias, List args) {
- ArrayList options = new ArrayList<>(((Parkour)getAddon()).getParkourManager().getWarps().keySet());
+ ArrayList options = new ArrayList<>(((Parkour) getAddon()).getParkourManager().getWarps().keySet());
if (options.size() < 10) {
return Optional.of(options);
}
// List is too long; require at least the first letter
- String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
+ String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
if (args.isEmpty()) {
return Optional.empty();
}
diff --git a/src/main/java/world/bentobox/parkour/gui/CoursesTab.java b/src/main/java/world/bentobox/parkour/gui/CoursesTab.java
index 63b5d2e..b711460 100644
--- a/src/main/java/world/bentobox/parkour/gui/CoursesTab.java
+++ b/src/main/java/world/bentobox/parkour/gui/CoursesTab.java
@@ -10,7 +10,6 @@
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
-import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
@@ -25,9 +24,9 @@
/**
* Implements a {@link Tab} that shows course rankings
+ *
* @author tastybento
* @since 1.0.0
- *
*/
public class CoursesTab implements Tab {
@@ -36,8 +35,9 @@ public class CoursesTab implements Tab {
/**
* Show a tab of settings
+ *
* @param addon - addon
- * @param user - user who is viewing the tab
+ * @param user - user who is viewing the tab
*/
public CoursesTab(Parkour addon, User user) {
super();
@@ -47,6 +47,7 @@ public CoursesTab(Parkour addon, User user) {
/**
* Get the icon for this tab
+ *
* @return panel item
*/
@Override
@@ -69,6 +70,7 @@ public String getName() {
/**
* Get all the flags as panel items
+ *
* @return list of all the panel items for this flag type
*/
@Override
@@ -78,32 +80,34 @@ public String getName() {
List heads = new ArrayList<>();
// Sort the courses by runs
addon.getParkourManager().getParkourData().stream()
- .sorted()
- .filter(hs -> Objects.nonNull(hs.getWarpSpot()))
- .forEach(hs -> {
- UUID owner = addon.getIslands().getIslandById(hs.getUniqueId()).map(Island::getOwner).orElse(null);
- if (owner != null) {
- heads.add(getHead(hs, owner));
- }
- });
+ .sorted()
+ .filter(hs -> Objects.nonNull(hs.getWarpSpot()))
+ .forEach(hs -> {
+ addon.getIslands().getIslandById(hs.getUniqueId()).ifPresent(is -> {
+ if (is.getOwner() != null) {
+ heads.add(getHead(hs, is));
+ }
+ });
+ });
return heads;
}
/**
* Get the head panel item
- * @param pd - parkour data
- * @param playerUUID - the UUID of the owner
+ *
+ * @param pd - parkour data
+ * @param is.getOwner() - the UUID of the owner
* @return PanelItem
*/
- private PanelItem getHead(ParkourData pd, UUID playerUUID) {
- final String name = addon.getPlayers().getName(playerUUID);
+ private PanelItem getHead(ParkourData pd, Island is) {
+ final String name = addon.getPlayers().getName(is.getOwner());
List description = new ArrayList<>();
if (pd.getRunCount() > 0) {
description.add(user.getTranslation("parkour.courses.head-description", "[name]", name, "[runs]", String.valueOf(pd.getRunCount())));
}
- if (addon.getIslands().inTeam(addon.getOverWorld(), playerUUID)) {
+ if (addon.getIslands().inTeam(addon.getOverWorld(), is.getOwner())) {
List memberList = new ArrayList<>();
- for (UUID members : addon.getIslands().getMembers(addon.getOverWorld(), playerUUID)) {
+ for (UUID members : is.getMemberSet()) {
memberList.add(ChatColor.AQUA + addon.getPlayers().getName(members));
}
description.addAll(memberList);
@@ -115,7 +119,7 @@ private PanelItem getHead(ParkourData pd, UUID playerUUID) {
.clickHandler((panel, user, clickType, slot) -> {
user.sendMessage("parkour.warp.warping");
// Teleport user
- Util.teleportAsync(user.getPlayer(), pd.getWarpSpot().clone().add(new Vector(0.5, 1, 0.5)), TeleportCause.COMMAND);
+ Util.teleportAsync(user.getPlayer(), pd.getWarpSpot(), TeleportCause.COMMAND);
return true;
})
.description(description);
diff --git a/src/main/java/world/bentobox/parkour/listeners/CourseRunnerListener.java b/src/main/java/world/bentobox/parkour/listeners/CourseRunnerListener.java
index 645162d..1503d3c 100644
--- a/src/main/java/world/bentobox/parkour/listeners/CourseRunnerListener.java
+++ b/src/main/java/world/bentobox/parkour/listeners/CourseRunnerListener.java
@@ -3,12 +3,15 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.UUID;
+import org.bukkit.Bukkit;
import org.bukkit.EntityEffect;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
+import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -16,8 +19,10 @@
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.util.Vector;
@@ -28,6 +33,7 @@
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;
import world.bentobox.parkour.Parkour;
import world.bentobox.parkour.ParkourRunRecord;
@@ -71,13 +77,64 @@ public void onVisitorArrive(IslandEnterEvent e) {
} else if (!parkourRunManager.timers().containsKey(e.getPlayerUUID())) {
user.notify("parkour.to-start");
}
- if (island.getFlag(addon.PARKOUR_CREATIVE) <= island.getRank(user)) {
- user.setGameMode(GameMode.CREATIVE);
- } else {
- user.setGameMode(GameMode.SURVIVAL);
+
+ // the location that the player teleports from
+ Location fromLocation = user.getLocation();
+
+ if (addon.inWorld(fromLocation)) {
+ // if the player is staying in a parkour managed world they can have their gamemode changed
+ // this occurs when their island changes, so wont risk affecting players that are currently running
+ // since exit would trigger first
+ // cross (unmanaged) world teleportation is handled in onPlayerChangeWorld
+
+ updateGamemode(island, user);
}
}
+ @EventHandler
+ public void onPlayerChangeWorld(PlayerChangedWorldEvent event) {
+ User user = User.getInstance(event.getPlayer());
+ boolean fromParkour = addon.inWorld(event.getFrom());
+ World newWorld = user.getWorld();
+ boolean toParkour = addon.inWorld(newWorld);
+
+ if (toParkour && !fromParkour) {
+ // switching from non-parkour world to parkour world
+ Bukkit.getServer().getScheduler().runTaskLater(addon.getPlugin(), () -> {
+ Location currentLocation = user.getLocation();
+
+ // world has changed in the 2 ticks delay, dont set gamemode
+ if (currentLocation.getWorld() != newWorld) {
+ return;
+ }
+
+ addon.getIslandsManager().getIslandAt(currentLocation).ifPresent(island -> updateGamemode(island, user));
+ }, 2); // 2 ticks since multiverse overrides gamemode after 1 tick
+ }
+ }
+
+
+ @EventHandler
+ public void onPlayerJoin(PlayerJoinEvent event) {
+ User user = User.getInstance(event.getPlayer());
+ World world = user.getWorld();
+ if (!addon.inWorld(world)) {
+ return;
+ }
+
+ Bukkit.getServer().getScheduler().runTaskLater(addon.getPlugin(), () -> {
+ Location currentLocation = user.getLocation();
+
+ // world has changed in the 2 ticks delay, dont set gamemode
+ if (currentLocation.getWorld() != world) {
+ return;
+ }
+
+ addon.getIslandsManager().getIslandAt(currentLocation).ifPresent(island -> updateGamemode(island, user));
+ }, 2);
+ }
+
+
@EventHandler(priority = EventPriority.NORMAL)
public void onVisitorLeave(IslandExitEvent e) {
// If the user leaves any island, end and clear the session.
@@ -91,6 +148,10 @@ public void onVisitorLeave(IslandExitEvent e) {
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerDeath(PlayerDeathEvent e) {
// Game over
+ User user = User.getInstance(e.getEntity().getUniqueId());
+ if (parkourRunManager.checkpoints().containsKey(e.getEntity().getUniqueId()) && user.isOnline()) {
+ user.notify("parkour.session-ended");
+ }
parkourRunManager.clear(e.getEntity().getUniqueId());
}
@@ -113,44 +174,58 @@ public void onVisitorFall(EntityDamageEvent e) {
player.playEffect(EntityEffect.ENTITY_POOF);
player.setVelocity(new Vector(0, 0, 0));
player.setFallDistance(0);
- player.teleport(parkourRunManager.checkpoints().get(player.getUniqueId()));
-
+ Location checkpointLocation = parkourRunManager.checkpoints().get(player.getUniqueId());
+ checkpointLocation = checkpointLocation.clone().add(0.5, 0, 0.5);
+ parkourRunManager.currentlyTeleporting().add(player.getUniqueId());
+ Util.teleportAsync(player, checkpointLocation, PlayerTeleportEvent.TeleportCause.PLUGIN)
+ .thenAccept(b -> parkourRunManager.currentlyTeleporting().remove(player.getUniqueId()));
}
@EventHandler
public void onTeleport(PlayerTeleportEvent e) {
boolean shouldStopRun = switch (e.getCause()) {
- case ENDER_PEARL, CHORUS_FRUIT, DISMOUNT, EXIT_BED -> false;
- case COMMAND, PLUGIN, NETHER_PORTAL, END_PORTAL, SPECTATE, END_GATEWAY, UNKNOWN -> true;
+ case ENDER_PEARL, CHORUS_FRUIT, DISMOUNT, EXIT_BED, NETHER_PORTAL, END_PORTAL -> false;
+ case COMMAND, PLUGIN, SPECTATE, END_GATEWAY, UNKNOWN -> true;
};
- if (shouldStopRun && parkourRunManager.timers().containsKey(e.getPlayer().getUniqueId())) {
- User user = User.getInstance(e.getPlayer().getUniqueId());
- if (parkourRunManager.checkpoints().containsKey(e.getPlayer().getUniqueId()) && user.isOnline()) {
+ UUID playerUUID = e.getPlayer().getUniqueId();
+ if (!parkourRunManager.currentlyTeleporting().contains(playerUUID) && shouldStopRun && parkourRunManager.timers().containsKey(playerUUID)) {
+ User user = User.getInstance(playerUUID);
+ if (parkourRunManager.checkpoints().containsKey(playerUUID) && user.isOnline()) {
user.notify("parkour.session-ended");
}
- parkourRunManager.clear(e.getPlayer().getUniqueId());
+ parkourRunManager.clear(playerUUID);
}
// Check world - only apply flag actions to Parkour world and only if player is not actively running the course
if (e.getTo() == null // To can sometimes be null...
|| !addon.inWorld(e.getTo())
- || parkourRunManager.timers().containsKey(e.getPlayer().getUniqueId())) {
+ || parkourRunManager.timers().containsKey(playerUUID)) {
return;
}
// Handle flag action for players who are not running
Optional fromIsland = addon.getIslands().getIslandAt(e.getFrom());
Optional toIsland = addon.getIslands().getIslandAt(e.getTo());
- if (fromIsland.isPresent() && toIsland.isPresent() && fromIsland.get().equals(toIsland.get())) {
+ boolean shouldAlterGamemode = switch (e.getCause()) {
+ case COMMAND, PLUGIN, UNKNOWN -> true;
+ default -> false;
+ };
+ if (shouldAlterGamemode && fromIsland.isPresent() && toIsland.isPresent() && fromIsland.get().equals(toIsland.get())) {
// same island teleport
- Island island = fromIsland.get();
User user = User.getInstance(e.getPlayer());
- if (island.getFlag(addon.PARKOUR_CREATIVE) <= island.getRank(user)) {
- user.setGameMode(GameMode.CREATIVE);
- } else {
- user.setGameMode(GameMode.SURVIVAL);
- }
+ updateGamemode(fromIsland.get(), user);
}
+ }
+ private void updateGamemode(Island island, User user) {
+ if (user.hasPermission("parkour.mod.bypassgamemodechange")) {
+ return;
+ }
+
+ if (island.getFlag(addon.PARKOUR_CREATIVE) <= island.getRank(user)) {
+ user.setGameMode(GameMode.CREATIVE);
+ } else {
+ user.setGameMode(GameMode.SURVIVAL);
+ }
}
@@ -205,14 +280,14 @@ public void onStartEndSet(PlayerInteractEvent e) {
return;
}
- Location l = e.getClickedBlock().getLocation();
+ Location blockLocation = e.getClickedBlock().getLocation();
User user = User.getInstance(e.getPlayer());
- addon.getIslands().getProtectedIslandAt(l).ifPresent(island -> {
+ addon.getIslands().getProtectedIslandAt(blockLocation).ifPresent(island -> {
Optional start = addon.getParkourManager().getStart(island);
Optional end = addon.getParkourManager().getEnd(island);
// Check if start and end is set
- if (start.filter(startLoc -> isLocEquals(l, startLoc)).isPresent()) {
+ if (start.filter(startLoc -> isLocEquals(blockLocation, startLoc)).isPresent()) {
// End is not set
if (end.isEmpty()) {
user.sendMessage("parkour.set-the-end");
@@ -220,29 +295,29 @@ public void onStartEndSet(PlayerInteractEvent e) {
}
// Start the race!
if (!parkourRunManager.timers().containsKey(e.getPlayer().getUniqueId())) {
- parkourStart(user, l);
+ parkourStart(user, blockLocation);
}
- } else if (end.filter(endLoc -> isLocEquals(l, endLoc)).isPresent()
+ } else if (end.filter(endLoc -> isLocEquals(blockLocation, endLoc)).isPresent()
&& parkourRunManager.timers().containsKey(e.getPlayer().getUniqueId())) {
// End the race!
- parkourEnd(user, island, l);
+ parkourEnd(user, island, blockLocation);
}
});
}
- void parkourStart(User user, Location l) {
+ void parkourStart(User user, Location blockLocation) {
user.sendMessage("parkour.start");
- user.getPlayer().playSound(l, Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1F, 1F);
+ user.getPlayer().playSound(blockLocation, Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1F, 1F);
parkourRunManager.timers().put(user.getUniqueId(), System.currentTimeMillis());
- parkourRunManager.checkpoints().put(user.getUniqueId(), user.getLocation());
+ parkourRunManager.checkpoints().put(user.getUniqueId(), blockLocation);
user.setGameMode(GameMode.SURVIVAL);
}
- void parkourEnd(User user, Island island, Location l) {
+ void parkourEnd(User user, Island island, Location blockLocation) {
long duration = (System.currentTimeMillis() - Objects.requireNonNull(parkourRunManager.timers().get(user.getUniqueId())));
user.notify("parkour.end");
- user.getPlayer().playSound(l, Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1F, 1F);
+ user.getPlayer().playSound(blockLocation, Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1F, 1F);
user.notify("parkour.you-took", TextVariables.NUMBER, getDuration(user, duration));
parkourRunManager.clear(user.getUniqueId());
@@ -279,14 +354,18 @@ public void onCheckpoint(PlayerInteractEvent e) {
|| !parkourRunManager.timers().containsKey(e.getPlayer().getUniqueId())) {
return;
}
- Location l = e.getClickedBlock().getLocation();
- User user = User.getInstance(e.getPlayer());
- Vector checkPoint = parkourRunManager.checkpoints().get(e.getPlayer().getUniqueId()).toVector();
-
- if (addon.getIslands().getProtectedIslandAt(l).isPresent() && !l.toVector().equals(checkPoint)) {
- user.notify("parkour.checkpoint");
- e.getPlayer().playSound(l, Sound.BLOCK_BELL_USE, 1F, 1F);
- parkourRunManager.checkpoints().put(user.getUniqueId(), e.getPlayer().getLocation());
+ Location newCheckpointBlockLocation = e.getClickedBlock().getLocation();
+ if (addon.getIslands().getProtectedIslandAt(newCheckpointBlockLocation).isPresent()) {
+ // pressure plate should be a checkpoint
+ Location currentCheckpointBlockLocation = parkourRunManager.checkpoints().get(e.getPlayer().getUniqueId());
+ if (!isLocEquals(newCheckpointBlockLocation, currentCheckpointBlockLocation)) {
+ // new location is different
+ User user = User.getInstance(e.getPlayer());
+ user.sendMessage("parkour.checkpoint");
+ e.getPlayer().playSound(newCheckpointBlockLocation, Sound.BLOCK_BELL_USE, 1F, 1F);
+ parkourRunManager.checkpoints().put(user.getUniqueId(), newCheckpointBlockLocation);
+ }
}
}
+
}
diff --git a/src/main/java/world/bentobox/parkour/listeners/MakeCourseListener.java b/src/main/java/world/bentobox/parkour/listeners/MakeCourseListener.java
index fbe2c99..c86cba9 100644
--- a/src/main/java/world/bentobox/parkour/listeners/MakeCourseListener.java
+++ b/src/main/java/world/bentobox/parkour/listeners/MakeCourseListener.java
@@ -50,10 +50,11 @@ public void onWarpSet(BlockPlaceEvent e) {
Optional warpSpot = addon.getParkourManager().getWarpSpot(island);
if (warpSpot.isEmpty()) {
user.notify("parkour.warp.set");
- addon.getParkourManager().setWarpSpot(island, l);
} else {
user.notify("parkour.warp.replaced");
}
+ // shift from block to player location
+ addon.getParkourManager().setWarpSpot(island, l.add(0.5,0,0.5));
}
}
diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml
index e287f7c..b2ecd55 100755
--- a/src/main/resources/addon.yml
+++ b/src/main/resources/addon.yml
@@ -1,7 +1,7 @@
name: Parkour
main: world.bentobox.parkour.Parkour
version: ${version}${build.number}
-api-version: 1.24
+api-version: 2.7.1
metrics: true
icon: "POLISHED_BLACKSTONE_PRESSURE_PLATE"
repository: "BentoBoxWorld/Parkour"
@@ -40,6 +40,9 @@ permissions:
parkour.mod.bypasslock:
description: Bypasses an course lock
default: op
+ parkour.mod.bypassgamemodechange:
+ description: Bypasses the changing of your gamemode when entering your island.
+ default: false
parkour.mod.bypassban:
description: Bypasses course ban
default: op
@@ -89,18 +92,6 @@ permissions:
parkour.mod.team.setowner:
description: Allow use of '/pkadmin team setowner' command - transfers course ownership to the player
default: op
- parkour.mod.team.add:
- description: Allow use of '/pkadmin add' command - add player to owner's team
- default: op
- parkour.mod.team.kick:
- description: Allow use of '/pkadmin kick' command - kick a player from a team
- default: op
- parkour.mod.team.disband:
- description: Allow use of '/pkadmin disband' command - disband owner's team
- default: op
- parkour.mod.team.setowner:
- description: Allow use of '/pkadmin setowner' command - transfers course ownership to the player
- default: op
parkour.admin.blueprint:
description: Allow use of '/pkadmin blueprint' command - manipulate blueprints
default: op
@@ -170,9 +161,6 @@ permissions:
parkour.admin.resets.add:
description: Allow use of '/pkadmin resets add' command - adds this player's course reset count
default: op
- parkour.admin.resets.remove:
- description: Allow use of '/pkadmin resets remove' command - reduces the player's course reset count
- default: op
parkour.admin.delete:
description: Allow use of '/pkadmin delete' command - deletes a player and regenerates their course
default: op
@@ -296,9 +284,6 @@ permissions:
parkour.island.team.coop:
description: Allow use of '/parkour team coop' command - make a player coop rank on your course
default: true
- parkour.island.team.coop:
- description: Allow use of '/parkour team uncoop' command - remove a coop rank from player
- default: true
parkour.island.team.trust:
description: Allow use of '/parkour team trust', '/parkour team untrust' command - remove trusted player rank from player
default: true
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 76abb7e..75f1ee7 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,7 +1,7 @@
name: BentoBox-Parkour
main: world.bentobox.parkour.ParkourPladdon
version: ${project.version}${build.number}
-api-version: "1.20"
+api-version: "1.21"
authors: [tastybento]
contributors: ["The BentoBoxWorld Community"]
diff --git a/src/test/java/world/bentobox/parkour/AbstractParkourTest.java b/src/test/java/world/bentobox/parkour/AbstractParkourTest.java
new file mode 100644
index 0000000..9e0d134
--- /dev/null
+++ b/src/test/java/world/bentobox/parkour/AbstractParkourTest.java
@@ -0,0 +1,178 @@
+package world.bentobox.parkour;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.beans.IntrospectionException;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Logger;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Server;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.PluginManager;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+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.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.reflect.Whitebox;
+
+import world.bentobox.bentobox.BentoBox;
+import world.bentobox.bentobox.Settings;
+import world.bentobox.bentobox.api.configuration.Config;
+import world.bentobox.bentobox.api.user.User;
+import world.bentobox.bentobox.database.AbstractDatabaseHandler;
+import world.bentobox.bentobox.database.DatabaseSetup;
+import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
+import world.bentobox.bentobox.database.objects.Island;
+import world.bentobox.bentobox.managers.AddonsManager;
+import world.bentobox.bentobox.managers.CommandsManager;
+import world.bentobox.bentobox.managers.FlagsManager;
+import world.bentobox.bentobox.managers.IslandWorldManager;
+import world.bentobox.bentobox.managers.IslandsManager;
+import world.bentobox.bentobox.managers.PlaceholdersManager;
+import world.bentobox.bentobox.managers.RanksManager;
+import world.bentobox.bentobox.util.Util;
+import world.bentobox.parkour.mocks.ServerMocks;
+
+/**
+ * @author tastybento
+ *
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ Bukkit.class, BentoBox.class, User.class, Config.class, DatabaseSetup.class, Util.class })
+public abstract class AbstractParkourTest {
+
+ @Mock
+ protected User user;
+ @Mock
+ protected IslandsManager im;
+ @Mock
+ protected Island island;
+ @Mock
+ protected BentoBox plugin;
+ @Mock
+ protected FlagsManager fm;
+ @Mock
+ protected Settings settings;
+ @Mock
+ protected PlaceholdersManager phm;
+ @Mock
+ protected IslandWorldManager iwm;
+ @Mock
+ protected Parkour addon;
+ @Mock
+ private RanksManager rm;
+ protected static AbstractDatabaseHandler