* Remove unused imports

* Implements PaperLib within bentobox

* Added PaperLib methods to Util for addons to call

* Uses PaperLib for island deletion

* Point to https://github.com/BentoBoxWorld/MVdWPlaceholderAPI in POM
This commit is contained in:
tastybento 2019-11-13 15:10:33 -08:00 committed by GitHub
parent e2fd5555cf
commit c9b0c74d08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 392 additions and 181 deletions

181
pom.xml
View File

@ -41,7 +41,7 @@
<ciManagement> <ciManagement>
<system>jenkins</system> <system>jenkins</system>
<url>http://ci.codemc.org/job/BentoBoxWorld/job/BentoBox</url> <url>https://ci.codemc.org/job/BentoBoxWorld/job/BentoBox</url>
</ciManagement> </ciManagement>
<issueManagement> <issueManagement>
@ -87,8 +87,9 @@
<!-- Profiles will allow to automatically change build version. --> <!-- Profiles will allow to automatically change build version. -->
<profiles> <profiles>
<profile> <profile>
<!-- ci profile is activated if exist environment variable BUILD_NUMBER. --> <!-- ci profile is activated if exist environment variable BUILD_NUMBER. -->
<!-- It replaces ${build.number} that is currently '-LOCAL' with correct build number from JENKINS machine. --> <!-- It replaces ${build.number} that is currently '-LOCAL' with
correct build number from JENKINS machine. -->
<id>ci</id> <id>ci</id>
<activation> <activation>
<property> <property>
@ -101,10 +102,14 @@
</properties> </properties>
</profile> </profile>
<profile> <profile>
<!-- Master profile is activated if exist environment variable GIT_BRANCH and its value is origin/master. --> <!-- Master profile is activated if exist environment variable
<!-- It will replace 'revision' with '${build.version}' so it removes '-SNAPSHOT' string at the end. --> GIT_BRANCH and its value is origin/master. -->
<!-- Also, as this is release build, build number can be set to empty string. --> <!-- It will replace 'revision' with '${build.version}' so it
<!-- This profile will be used only if exist environment variable GIT_BRANCH with value origin/master. --> removes '-SNAPSHOT' string at the end. -->
<!-- Also, as this is release build, build number can be set
to empty string. -->
<!-- This profile will be used only if exist environment variable
GIT_BRANCH with value origin/master. -->
<id>master</id> <id>master</id>
<activation> <activation>
<property> <property>
@ -115,7 +120,7 @@
<properties> <properties>
<!-- Override only if necessary --> <!-- Override only if necessary -->
<revision>${build.version}</revision> <revision>${build.version}</revision>
<!-- Empties build number variable.--> <!-- Empties build number variable. -->
<build.number></build.number> <build.number></build.number>
</properties> </properties>
</profile> </profile>
@ -130,7 +135,7 @@
<plugin> <plugin>
<groupId>org.sonarsource.scanner.maven</groupId> <groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId> <artifactId>sonar-maven-plugin</artifactId>
<!-- was <version>3.4.1.1168</version> --> <!-- was <version>3.4.1.1168</version> -->
<version>3.6.0.1398</version> <version>3.6.0.1398</version>
<executions> <executions>
<execution> <execution>
@ -161,78 +166,81 @@
</repository> </repository>
<repository> <repository>
<id>placeholderapi-repo</id> <id>placeholderapi-repo</id>
<url>http://repo.extendedclip.com/content/repositories/placeholderapi/</url> <url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository> </repository>
<repository> <repository>
<id>dynmap-repo</id> <id>dynmap-repo</id>
<url>http://repo.mikeprimm.com/</url> <url>https://repo.mikeprimm.com/</url>
</repository> </repository>
<repository> <repository>
<id>worldedit-repo</id> <id>worldedit-repo</id>
<url>http://maven.sk89q.com/repo/</url> <url>https://maven.sk89q.com/repo/</url>
</repository>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository> </repository>
</repositories> </repositories>
<dependencies> <dependencies>
<!-- Spigot API --> <!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
<!-- Metrics -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>${bstats.version}</version>
</dependency>
<!-- Mockito (Unit testing) -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>${mongodb.version}</version>
</dependency>
<!-- Vault: as their maven repo is down, we need to get it from jitpack -->
<!-- See https://github.com/MilkBowl/VaultAPI/issues/69 -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>${vault.version}</version>
<scope>provided</scope>
</dependency>
<!-- Placeholders -->
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>${placeholderapi.version}</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<!-- We have to use a forked version of this api <groupId>org.spigotmc</groupId>
because Maxim had the brilliant idea to make one <artifactId>spigot-api</artifactId>
of the dependencies rely on an illegal artifact <version>${spigot.version}</version>
thus making its repository unusable. --> <scope>provided</scope>
<groupId>com.github.Prouser123-forks</groupId> </dependency>
<!-- Metrics -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>${bstats.version}</version>
</dependency>
<!-- Mockito (Unit testing) -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>${mongodb.version}</version>
</dependency>
<!-- Vault: as their maven repo is down, we need to get it from jitpack -->
<!-- See https://github.com/MilkBowl/VaultAPI/issues/69 -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>${vault.version}</version>
<scope>provided</scope>
</dependency>
<!-- Placeholders -->
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>${placeholderapi.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<!-- We have to use a forked version of this api because Maxim
had the brilliant idea to make one of the dependencies rely on an illegal
artifact thus making its repository unusable. -->
<groupId>com.github.BentoBoxWorld</groupId>
<artifactId>MVdWPlaceholderAPI</artifactId> <artifactId>MVdWPlaceholderAPI</artifactId>
<version>${mvdwplaceholderapi.version}</version> <version>${mvdwplaceholderapi.version}</version>
</dependency> </dependency>
@ -255,26 +263,35 @@
<artifactId>GitHubWebAPI4Java</artifactId> <artifactId>GitHubWebAPI4Java</artifactId>
<version>${githubapi.version}</version> <version>${githubapi.version}</version>
</dependency> </dependency>
<!-- Static analysis --> <!-- Static analysis -->
<!-- We are using Eclipse's annotations. <!-- We are using Eclipse's annotations. If you're using IDEA, update
If you're using IDEA, update your project settings to take these your project settings to take these into account for in real time static
into account for in real time static analysis --> analysis -->
<dependency> <dependency>
<groupId>org.eclipse.jdt</groupId> <groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.annotation</artifactId> <artifactId>org.eclipse.jdt.annotation</artifactId>
<version>2.2.200</version> <version>2.2.200</version>
</dependency> </dependency>
<!-- PaperLib -->
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.2</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<!-- By default ${revision} is ${build.version}-SNAPSHOT --> <!-- By default ${revision} is ${build.version}-SNAPSHOT -->
<!-- If GIT_BRANCH variable is set to origin/master, then it will be only ${build.version}. --> <!-- If GIT_BRANCH variable is set to origin/master, then it will
be only ${build.version}. -->
<!-- By default ${build.number} is -LOCAL. --> <!-- By default ${build.number} is -LOCAL. -->
<!-- If the BUILD_NUMBER variable is set, then it will be -b[number]. --> <!-- If the BUILD_NUMBER variable is set, then it will be -b[number]. -->
<!-- If GIT_BRANCH variable is set to origin/master, then it will be the empty string. --> <!-- If GIT_BRANCH variable is set to origin/master, then it will
be the empty string. -->
<finalName>${project.name}-${revision}${build.number}</finalName> <finalName>${project.name}-${revision}${build.number}</finalName>
<defaultGoal>clean package</defaultGoal> <defaultGoal>clean package</defaultGoal>
<resources> <resources>
<resource> <resource>
@ -350,6 +367,10 @@
<pattern>io.github.TheBusyBiscuit.GitHubWebAPI4Java</pattern> <pattern>io.github.TheBusyBiscuit.GitHubWebAPI4Java</pattern>
<shadedPattern>world.bentobox.bentobox.api.github</shadedPattern> <shadedPattern>world.bentobox.bentobox.api.github</shadedPattern>
</relocation> </relocation>
<relocation>
<pattern>io.papermc.lib</pattern>
<shadedPattern>world.bentobox.bentobox.paperlib</shadedPattern> <!-- Replace this -->
</relocation>
</relocations> </relocations>
<artifactSet> <artifactSet>
<excludes> <excludes>
@ -384,8 +405,8 @@
<configuration> <configuration>
<append>true</append> <append>true</append>
<excludes> <excludes>
<!-- This is required to prevent Jacoco from adding synthetic fields <!-- This is required to prevent Jacoco from adding
to a JavaBean class (causes errors in testing) --> synthetic fields to a JavaBean class (causes errors in testing) -->
<exclude>**/*Names*</exclude> <exclude>**/*Names*</exclude>
</excludes> </excludes>
</configuration> </configuration>

View File

@ -3,7 +3,6 @@ package world.bentobox.bentobox.listeners;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -15,7 +14,6 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
@ -57,32 +55,32 @@ public class JoinLeaveListener implements Listener {
players.addPlayer(playerUUID); players.addPlayer(playerUUID);
plugin.getIWM().getOverWorlds().stream() plugin.getIWM().getOverWorlds().stream()
.filter(w -> plugin.getIWM().isCreateIslandOnFirstLoginEnabled(w)) .filter(w -> plugin.getIWM().isCreateIslandOnFirstLoginEnabled(w))
.forEach(w -> { .forEach(w -> {
// Even if that'd be extremely unlikely, it's better to check if the player doesn't have an already. // Even if that'd be extremely unlikely, it's better to check if the player doesn't have an already.
if (!(plugin.getIslands().hasIsland(w, user) || plugin.getIslands().inTeam(w, user.getUniqueId()))) { if (!(plugin.getIslands().hasIsland(w, user) || plugin.getIslands().inTeam(w, user.getUniqueId()))) {
int delay = plugin.getIWM().getCreateIslandOnFirstLoginDelay(w); int delay = plugin.getIWM().getCreateIslandOnFirstLoginDelay(w);
user.sendMessage("commands.island.create.on-first-login", user.sendMessage("commands.island.create.on-first-login",
TextVariables.NUMBER, String.valueOf(delay)); TextVariables.NUMBER, String.valueOf(delay));
Runnable createIsland = () -> { Runnable createIsland = () -> {
// should only execute if: // should only execute if:
// - abort on logout is false // - abort on logout is false
// - abort on logout is true && user is online // - abort on logout is true && user is online
if (!plugin.getIWM().isCreateIslandOnFirstLoginAbortOnLogout(w) || user.isOnline()){ if (!plugin.getIWM().isCreateIslandOnFirstLoginAbortOnLogout(w) || user.isOnline()){
plugin.getIWM().getAddon(w).ifPresent(addon -> addon.getPlayerCommand() plugin.getIWM().getAddon(w).ifPresent(addon -> addon.getPlayerCommand()
.map(command -> command.getSubCommand("create").orElse(null)) .map(command -> command.getSubCommand("create").orElse(null))
.ifPresent(command -> command.execute(user, "create", Collections.singletonList(BlueprintsManager.DEFAULT_BUNDLE_NAME)))); .ifPresent(command -> command.execute(user, "create", Collections.singletonList(BlueprintsManager.DEFAULT_BUNDLE_NAME))));
}
};
if (delay <= 0) {
createIsland.run();
} else {
plugin.getServer().getScheduler().runTaskLater(plugin, createIsland, delay * 20L);
}
} }
}); };
if (delay <= 0) {
createIsland.run();
} else {
plugin.getServer().getScheduler().runTaskLater(plugin, createIsland, delay * 20L);
}
}
});
} }
// Make sure the player is loaded into the cache (doesn't impact performance) // Make sure the player is loaded into the cache (doesn't impact performance)
@ -90,8 +88,8 @@ public class JoinLeaveListener implements Listener {
// Reset island resets if required // Reset island resets if required
plugin.getIWM().getOverWorlds().stream() plugin.getIWM().getOverWorlds().stream()
.filter(w -> event.getPlayer().getLastPlayed() < plugin.getIWM().getResetEpoch(w)) .filter(w -> event.getPlayer().getLastPlayed() < plugin.getIWM().getResetEpoch(w))
.forEach(w -> players.setResets(w, playerUUID, 0)); .forEach(w -> players.setResets(w, playerUUID, 0));
// Automated island ownership transfer // Automated island ownership transfer
if (plugin.getSettings().isEnableAutoOwnershipTransfer()) { if (plugin.getSettings().isEnableAutoOwnershipTransfer()) {
@ -157,53 +155,53 @@ public class JoinLeaveListener implements Listener {
private void runAutomatedOwnershipTransfer(User user) { private void runAutomatedOwnershipTransfer(User user) {
plugin.getIWM().getOverWorlds().stream() plugin.getIWM().getOverWorlds().stream()
.filter(world -> plugin.getIslands().hasIsland(world, user) && !plugin.getIslands().isOwner(world, user.getUniqueId())) .filter(world -> plugin.getIslands().hasIsland(world, user) && !plugin.getIslands().isOwner(world, user.getUniqueId()))
.forEach(world -> { .forEach(world -> {
Island island = plugin.getIslands().getIsland(world, user); Island island = plugin.getIslands().getIsland(world, user);
OfflinePlayer owner = Bukkit.getOfflinePlayer(island.getOwner()); OfflinePlayer owner = Bukkit.getOfflinePlayer(island.getOwner());
// Converting the setting (in days) to milliseconds. // Converting the setting (in days) to milliseconds.
long inactivityThreshold = plugin.getSettings().getAutoOwnershipTransferInactivityThreshold() * 24 * 60 * 60 * 1000L; long inactivityThreshold = plugin.getSettings().getAutoOwnershipTransferInactivityThreshold() * 24 * 60 * 60 * 1000L;
long timestamp = System.currentTimeMillis() - inactivityThreshold; long timestamp = System.currentTimeMillis() - inactivityThreshold;
// We make sure the current owner is inactive. // We make sure the current owner is inactive.
if (owner.getLastPlayed() != 0 && owner.getLastPlayed() < timestamp) { if (owner.getLastPlayed() != 0 && owner.getLastPlayed() < timestamp) {
// The current owner is inactive // The current owner is inactive
// Now, let's run through all of the island members (except the player who's just joined) and see who's active. // Now, let's run through all of the island members (except the player who's just joined) and see who's active.
// Sadly, this will make us calculate the owner inactivity again... :( // Sadly, this will make us calculate the owner inactivity again... :(
List<UUID> candidates = Arrays.asList((UUID[]) island.getMemberSet().stream() List<UUID> candidates = Arrays.asList((UUID[]) island.getMemberSet().stream()
.filter(uuid -> !user.getUniqueId().equals(uuid)) .filter(uuid -> !user.getUniqueId().equals(uuid))
.filter(uuid -> Bukkit.getOfflinePlayer(uuid).getLastPlayed() != 0 .filter(uuid -> Bukkit.getOfflinePlayer(uuid).getLastPlayed() != 0
&& Bukkit.getOfflinePlayer(uuid).getLastPlayed() < timestamp) && Bukkit.getOfflinePlayer(uuid).getLastPlayed() < timestamp)
.toArray()); .toArray());
if (!candidates.isEmpty() && !plugin.getSettings().isAutoOwnershipTransferIgnoreRanks()) { if (!candidates.isEmpty() && !plugin.getSettings().isAutoOwnershipTransferIgnoreRanks()) {
// Ranks are not ignored, our candidates can only have the highest rank // Ranks are not ignored, our candidates can only have the highest rank
// TODO Complete this section // TODO Complete this section
} }
} }
}); });
} }
private void updateIslandRange(User user) { private void updateIslandRange(User user) {
plugin.getIWM().getOverWorlds().stream() plugin.getIWM().getOverWorlds().stream()
.filter(world -> plugin.getIslands().isOwner(world, user.getUniqueId())) .filter(world -> plugin.getIslands().isOwner(world, user.getUniqueId()))
.forEach(world -> { .forEach(world -> {
Island island = plugin.getIslands().getIsland(world, user); Island island = plugin.getIslands().getIsland(world, user);
if (island != null) { if (island != null) {
// Check if new owner has a different range permission than the island size // Check if new owner has a different range permission than the island size
int range = user.getPermissionValue(plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range", island.getProtectionRange()); int range = user.getPermissionValue(plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range", island.getProtectionRange());
// Range can go up or down // Range can go up or down
if (range != island.getProtectionRange()) { if (range != island.getProtectionRange()) {
user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range)); user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range));
plugin.log("Island protection range changed from " + island.getProtectionRange() + " to " plugin.log("Island protection range changed from " + island.getProtectionRange() + " to "
+ range + " for " + user.getName() + " due to permission."); + range + " for " + user.getName() + " due to permission.");
} }
island.setProtectionRange(range); island.setProtectionRange(range);
} }
}); });
} }
@EventHandler(priority = EventPriority.NORMAL) @EventHandler(priority = EventPriority.NORMAL)
@ -218,9 +216,9 @@ public class JoinLeaveListener implements Listener {
// No, there are no more players online on this island // No, there are no more players online on this island
// Tell players they are being removed // Tell players they are being removed
island.getMembers().entrySet().stream() island.getMembers().entrySet().stream()
.filter(e -> e.getValue() == RanksManager.COOP_RANK) .filter(e -> e.getValue() == RanksManager.COOP_RANK)
.forEach(e -> User.getInstance(e.getKey()) .forEach(e -> User.getInstance(e.getKey())
.sendMessage("commands.island.team.uncoop.all-members-logged-off", TextVariables.NAME, plugin.getPlayers().getName(island.getOwner()))); .sendMessage("commands.island.team.uncoop.all-members-logged-off", TextVariables.NAME, plugin.getPlayers().getName(island.getOwner())));
// Remove any coop players on this island // Remove any coop players on this island
island.removeRank(RanksManager.COOP_RANK); island.removeRank(RanksManager.COOP_RANK);
} }

View File

@ -12,6 +12,7 @@ import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.vehicle.VehicleMoveEvent; import org.bukkit.event.vehicle.VehicleMoveEvent;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import io.papermc.lib.PaperLib;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.flags.FlagListener; import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
@ -155,7 +156,7 @@ public class LockAndBanListener extends FlagListener {
} else { } else {
// There's nothing much we can do. // There's nothing much we can do.
// We'll try to teleport him to the spawn... // We'll try to teleport him to the spawn...
player.teleport(player.getWorld().getSpawnLocation()); PaperLib.teleportAsync(player, player.getWorld().getSpawnLocation());
// Switch him back to the default gamemode. He may die, sorry :( // Switch him back to the default gamemode. He may die, sorry :(
player.setGameMode(getIWM().getDefaultGameMode(player.getWorld())); player.setGameMode(getIWM().getDefaultGameMode(player.getWorld()));

View File

@ -20,6 +20,7 @@ import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.TreeSpecies; import org.bukkit.TreeSpecies;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.attribute.Attribute;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -36,6 +37,7 @@ import org.eclipse.jdt.annotation.Nullable;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.papermc.lib.PaperLib;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.IslandBaseEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent;
@ -664,27 +666,26 @@ public class IslandsManager {
.build(); .build();
return; return;
} }
if (!home.getChunk().isLoaded()) {
home.getChunk().load();
}
player.teleport(home);
// Add home // Add home
if (plugin.getPlayers().getHomeLocations(world, player.getUniqueId()).isEmpty()) { if (plugin.getPlayers().getHomeLocations(world, player.getUniqueId()).isEmpty()) {
plugin.getPlayers().setHomeLocation(player.getUniqueId(), home); plugin.getPlayers().setHomeLocation(player.getUniqueId(), home);
} }
if (number == 1) { user.sendMessage("commands.island.go.teleport");
user.sendMessage("commands.island.go.teleport"); PaperLib.teleportAsync(player, home).thenAccept(b -> teleported(world, user, number, newIsland));
} else { }
private void teleported(World world, User user, int number, boolean newIsland) {
if (number > 1) {
user.sendMessage("commands.island.go.teleported", TextVariables.NUMBER, String.valueOf(number)); user.sendMessage("commands.island.go.teleported", TextVariables.NUMBER, String.valueOf(number));
} }
// If this is a new island, then run commands and do resets // If this is a new island, then run commands and do resets
if (newIsland) { if (newIsland) {
// Execute commands // Execute commands
plugin.getIWM().getOnJoinCommands(world).forEach(command -> { plugin.getIWM().getOnJoinCommands(world).forEach(command -> {
command = command.replace("[player]", player.getName()); command = command.replace("[player]", user.getName());
if (command.startsWith("[SUDO]")) { if (command.startsWith("[SUDO]")) {
// Execute the command by the player // Execute the command by the player
player.performCommand(command.substring(6)); user.performCommand(command.substring(6));
} else { } else {
// Otherwise execute as the server console // Otherwise execute as the server console
plugin.getServer().dispatchCommand(Bukkit.getConsoleSender(), command); plugin.getServer().dispatchCommand(Bukkit.getConsoleSender(), command);
@ -704,7 +705,7 @@ public class IslandsManager {
// Reset the health // Reset the health
if (plugin.getIWM().isOnJoinResetHealth(world)) { if (plugin.getIWM().isOnJoinResetHealth(world)) {
user.getPlayer().setHealth(20.0D); user.getPlayer().setHealth(user.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getDefaultValue());
} }
// Reset the hunger // Reset the hunger
@ -789,7 +790,7 @@ public class IslandsManager {
this.spawn.put(spawn.getWorld(), spawn); this.spawn.put(spawn.getWorld(), spawn);
spawn.setSpawn(true); spawn.setSpawn(true);
} }
/** /**
* Clears the spawn island for this world * Clears the spawn island for this world
* @param world - world * @param world - world
@ -986,7 +987,7 @@ public class IslandsManager {
// Move player to spawn // Move player to spawn
if (spawn.containsKey(w)) { if (spawn.containsKey(w)) {
// go to island spawn // go to island spawn
p.teleport(spawn.get(w).getSpawnPoint(w.getEnvironment())); PaperLib.teleportAsync(p, spawn.get(w).getSpawnPoint(w.getEnvironment()));
} }
} }
}); });

View File

@ -9,7 +9,6 @@ import org.bukkit.ChatColor;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.panels.builders.PanelBuilder;

View File

@ -5,6 +5,8 @@ import java.util.Random;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
@ -12,6 +14,7 @@ import org.bukkit.generator.ChunkGenerator.ChunkData;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import io.papermc.lib.PaperLib;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent;
@ -25,13 +28,11 @@ import world.bentobox.bentobox.database.objects.IslandDeletion;
*/ */
public class DeleteIslandChunks { public class DeleteIslandChunks {
/**
* This is how many chunks per world will be done in one tick.
*/
private int chunkX; private int chunkX;
private int chunkZ; private int chunkZ;
private BukkitTask task; private BukkitTask task;
private IslandDeletion di; private IslandDeletion di;
private boolean inDelete;
public DeleteIslandChunks(BentoBox plugin, IslandDeletion di) { public DeleteIslandChunks(BentoBox plugin, IslandDeletion di) {
// Fire event // Fire event
@ -42,16 +43,19 @@ public class DeleteIslandChunks {
this.di = di; this.di = di;
// Run through all chunks of the islands and regenerate them. // Run through all chunks of the islands and regenerate them.
task = Bukkit.getScheduler().runTaskTimer(plugin, () -> { task = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
if (inDelete) return;
inDelete = true;
for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) { for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) {
plugin.getIWM().getAddon(di.getWorld()).ifPresent(gm -> { plugin.getIWM().getAddon(di.getWorld()).ifPresent(gm -> {
Chunk chunk = di.getWorld().getChunkAt(chunkX, chunkZ); // Overworld
regenerateChunk(gm, chunk); processChunk(gm, di.getWorld(), chunkX, chunkZ);
// Nether
if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) { if (plugin.getIWM().isNetherGenerate(di.getWorld()) && plugin.getIWM().isNetherIslands(di.getWorld())) {
regenerateChunk(gm, plugin.getIWM().getNetherWorld(di.getWorld()).getChunkAt(chunkX, chunkZ)); processChunk(gm, plugin.getIWM().getNetherWorld(di.getWorld()), chunkX, chunkZ);
} }
// End
if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) { if (plugin.getIWM().isEndGenerate(di.getWorld()) && plugin.getIWM().isEndIslands(di.getWorld())) {
regenerateChunk(gm, plugin.getIWM().getEndWorld(di.getWorld()).getChunkAt(chunkX, chunkZ)); processChunk(gm, plugin.getIWM().getEndWorld(di.getWorld()), chunkX, chunkZ);
} }
chunkZ++; chunkZ++;
if (chunkZ > di.getMaxZChunk()) { if (chunkZ > di.getMaxZChunk()) {
@ -66,9 +70,16 @@ public class DeleteIslandChunks {
} }
}); });
} }
inDelete = false;
}, 0L, 1L); }, 0L, 1L);
} }
private void processChunk(GameModeAddon gm, World world, int x, int z) {
if (PaperLib.isChunkGenerated(world, x, z)) {
PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk -> regenerateChunk(gm, chunk));
}
}
private void regenerateChunk(GameModeAddon gm, Chunk chunk) { private void regenerateChunk(GameModeAddon gm, Chunk chunk) {
boolean isLoaded = chunk.isLoaded(); boolean isLoaded = chunk.isLoaded();
// Clear all inventories // Clear all inventories
@ -88,6 +99,9 @@ public class DeleteIslandChunks {
if (di.inBounds(baseX + x, baseZ + z)) { if (di.inBounds(baseX + x, baseZ + z)) {
chunk.getBlock(x, 0, z).setBiome(grid.getBiome(x, z)); chunk.getBlock(x, 0, z).setBiome(grid.getBiome(x, z));
for (int y = 0; y < chunk.getWorld().getMaxHeight(); y++) { for (int y = 0; y < chunk.getWorld().getMaxHeight(); y++) {
// Note: setting block to air before setting it to something else stops a bug in the server
// where it reports a "
chunk.getBlock(x, y, z).setType(Material.AIR, false);
chunk.getBlock(x, y, z).setBlockData(cd.getBlockData(x, y, z), false); chunk.getBlock(x, y, z).setBlockData(cd.getBlockData(x, y, z), false);
} }
} }

View File

@ -6,14 +6,19 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.entity.Animals; import org.bukkit.entity.Animals;
import org.bukkit.entity.Bat; import org.bukkit.entity.Bat;
@ -28,10 +33,13 @@ import org.bukkit.entity.Shulker;
import org.bukkit.entity.Slime; import org.bukkit.entity.Slime;
import org.bukkit.entity.Snowman; import org.bukkit.entity.Snowman;
import org.bukkit.entity.WaterMob; import org.bukkit.entity.WaterMob;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import io.papermc.lib.PaperLib;
import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
@ -351,4 +359,164 @@ public class Util {
return entity instanceof Animals || entity instanceof IronGolem || entity instanceof Snowman || return entity instanceof Animals || entity instanceof IronGolem || entity instanceof Snowman ||
entity instanceof WaterMob && !(entity instanceof PufferFish) || entity instanceof Bat; entity instanceof WaterMob && !(entity instanceof PufferFish) || entity instanceof Bat;
} }
/*
* PaperLib methods for addons to call
*/
/**
* Teleports an Entity to the target location, loading the chunk asynchronously first if needed.
* @param entity The Entity to teleport
* @param location The Location to Teleport to
* @return Future that completes with the result of the teleport
*/
@Nonnull
public static CompletableFuture<Boolean> teleportAsync(@Nonnull Entity entity, @Nonnull Location location) {
return PaperLib.teleportAsync(entity, location);
}
/**
* Teleports an Entity to the target location, loading the chunk asynchronously first if needed.
* @param entity The Entity to teleport
* @param location The Location to Teleport to
* @param cause The cause for the teleportation
* @return Future that completes with the result of the teleport
*/
@Nonnull
public static CompletableFuture<Boolean> teleportAsync(@Nonnull Entity entity, @Nonnull Location location, TeleportCause cause) {
return PaperLib.teleportAsync(entity, location, cause);
}
/**
* Gets the chunk at the target location, loading it asynchronously if needed.
* @param loc Location to get chunk for
* @return Future that completes with the chunk
*/
@Nonnull
public static CompletableFuture<Chunk> getChunkAtAsync(@Nonnull Location loc) {
return getChunkAtAsync(loc.getWorld(), loc.getBlockX() >> 4, loc.getBlockZ() >> 4, true);
}
/**
* Gets the chunk at the target location, loading it asynchronously if needed.
* @param loc Location to get chunk for
* @param gen Should the chunk generate or not. Only respected on some MC versions, 1.13 for CB, 1.12 for Paper
* @return Future that completes with the chunk, or null if the chunk did not exists and generation was not requested.
*/
@Nonnull
public static CompletableFuture<Chunk> getChunkAtAsync(@Nonnull Location loc, boolean gen) {
return getChunkAtAsync(loc.getWorld(), loc.getBlockX() >> 4, loc.getBlockZ() >> 4, gen);
}
/**
* Gets the chunk at the target location, loading it asynchronously if needed.
* @param world World to load chunk for
* @param x X coordinate of the chunk to load
* @param z Z coordinate of the chunk to load
* @return Future that completes with the chunk
*/
@Nonnull
public static CompletableFuture<Chunk> getChunkAtAsync(@Nonnull World world, int x, int z) {
return getChunkAtAsync(world, x, z, true);
}
/**
* Gets the chunk at the target location, loading it asynchronously if needed.
* @param world World to load chunk for
* @param x X coordinate of the chunk to load
* @param z Z coordinate of the chunk to load
* @param gen Should the chunk generate or not. Only respected on some MC versions, 1.13 for CB, 1.12 for Paper
* @return Future that completes with the chunk, or null if the chunk did not exists and generation was not requested.
*/
@Nonnull
public static CompletableFuture<Chunk> getChunkAtAsync(@Nonnull World world, int x, int z, boolean gen) {
return PaperLib.getChunkAtAsync(world, x, z, gen);
}
/**
* Checks if the chunk has been generated or not. Only works on Paper 1.12+ or any 1.13.1+ version
* @param loc Location to check if the chunk is generated
* @return If the chunk is generated or not
*/
public static boolean isChunkGenerated(@Nonnull Location loc) {
return isChunkGenerated(loc.getWorld(), loc.getBlockX() >> 4, loc.getBlockZ() >> 4);
}
/**
* Checks if the chunk has been generated or not. Only works on Paper 1.12+ or any 1.13.1+ version
* @param world World to check for
* @param x X coordinate of the chunk to check
* @param z Z coordinate of the chunk to checl
* @return If the chunk is generated or not
*/
public static boolean isChunkGenerated(@Nonnull World world, int x, int z) {
return PaperLib.isChunkGenerated(world, x, z);
}
/**
* Get's a BlockState, optionally not using a snapshot
* @param block The block to get a State of
* @param useSnapshot Whether or not to use a snapshot when supported
* @return The BlockState
*/
@Nonnull
public static BlockStateSnapshotResult getBlockState(@Nonnull Block block, boolean useSnapshot) {
return PaperLib.getBlockState(block, useSnapshot);
}
/**
* Detects if the current MC version is at least the following version.
*
* Assumes 0 patch version.
*
* @param minor Min Minor Version
* @return Meets the version requested
*/
public static boolean isVersion(int minor) {
return PaperLib.isVersion(minor);
}
/**
* Detects if the current MC version is at least the following version.
* @param minor Min Minor Version
* @param patch Min Patch Version
* @return Meets the version requested
*/
public static boolean isVersion(int minor, int patch) {
return PaperLib.isVersion(minor, patch);
}
/**
* Gets the current Minecraft Minor version. IE: 1.13.1 returns 13
* @return The Minor Version
*/
public static int getMinecraftVersion() {
return PaperLib.getMinecraftVersion();
}
/**
* Gets the current Minecraft Patch version. IE: 1.13.1 returns 1
* @return The Patch Version
*/
public static int getMinecraftPatchVersion() {
return PaperLib.getMinecraftPatchVersion();
}
/**
* Check if the server has access to the Spigot API
* @return True for Spigot <em>and</em> Paper environments
*/
public static boolean isSpigot() {
return PaperLib.isSpigot();
}
/**
* Check if the server has access to the Paper API
* @return True for Paper environments
*/
public static boolean isPaper() {
return PaperLib.isPaper();
}
} }

View File

@ -17,6 +17,7 @@ import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import io.papermc.lib.PaperLib;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
@ -226,16 +227,13 @@ public class SafeSpotTeleport {
*/ */
private void teleportEntity(final Location loc) { private void teleportEntity(final Location loc) {
task.cancel(); task.cancel();
if (!portal && entity instanceof Player && homeNumber > 0) {
// Set home if so marked
plugin.getPlayers().setHomeLocation(User.getInstance(entity), loc, homeNumber);
}
Vector velocity = entity.getVelocity();
// Return to main thread and teleport the player // Return to main thread and teleport the player
Bukkit.getScheduler().runTask(plugin, () -> { Bukkit.getScheduler().runTask(plugin, () -> PaperLib.teleportAsync(entity, loc).thenAccept(b -> entity.setVelocity(velocity)));
if (!portal && entity instanceof Player && homeNumber > 0) {
// Set home if so marked
plugin.getPlayers().setHomeLocation(User.getInstance(entity), loc, homeNumber);
}
Vector velocity = entity.getVelocity();
entity.teleport(loc);
entity.setVelocity(velocity);
});
} }
/** /**

View File

@ -66,6 +66,9 @@ import org.powermock.reflect.Whitebox;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.ImmutableSet.Builder;
import io.papermc.lib.PaperLib;
import io.papermc.lib.environments.CraftBukkitEnvironment;
import io.papermc.lib.environments.Environment;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.Settings;
import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.configuration.WorldSettings;
@ -131,6 +134,8 @@ public class IslandsManagerTest {
private Material sign; private Material sign;
private Material wallSign; private Material wallSign;
private Environment env;
/** /**
* @throws java.lang.Exception * @throws java.lang.Exception
@ -188,6 +193,8 @@ public class IslandsManagerTest {
BukkitScheduler sch = mock(BukkitScheduler.class); BukkitScheduler sch = mock(BukkitScheduler.class);
PowerMockito.mockStatic(Bukkit.class); PowerMockito.mockStatic(Bukkit.class);
when(Bukkit.getScheduler()).thenReturn(sch); when(Bukkit.getScheduler()).thenReturn(sch);
// version
when(Bukkit.getVersion()).thenReturn("Paper version git-Paper-225 (MC: 1.14.4) (Implementing API version 1.14.4-R0.1-SNAPSHOT)");
// Standard location // Standard location
manager = new IslandsManager(plugin); manager = new IslandsManager(plugin);
@ -295,6 +302,10 @@ public class IslandsManagerTest {
if (wallSign == null) { if (wallSign == null) {
wallSign = Material.getMaterial("OAK_WALL_SIGN"); wallSign = Material.getMaterial("OAK_WALL_SIGN");
} }
// PaperLib
env = new CraftBukkitEnvironment();
PaperLib.setCustomEnvironment(env);
} }
@After @After
@ -716,7 +727,7 @@ public class IslandsManagerTest {
when(pm.getHomeLocation(any(), any(User.class), eq(0))).thenReturn(null); when(pm.getHomeLocation(any(), any(User.class), eq(0))).thenReturn(null);
when(pm.getHomeLocation(any(), any(User.class), eq(1))).thenReturn(location); when(pm.getHomeLocation(any(), any(User.class), eq(1))).thenReturn(location);
im.homeTeleport(world, player, 0); im.homeTeleport(world, player, 0);
verify(player).teleport(location); verify(player).teleport(eq(location), any());
} }