Compare commits

..
10 Commits
28 changed files with 1502 additions and 993 deletions

No files matched your search

+3 -3
View File
@@ -11,7 +11,7 @@
<artifactId>areashop</artifactId>
<packaging>jar</packaging>
<name>AreaShop</name>
<version>2.6.2</version> <!-- Main plugin version -->
<version>2.7</version> <!-- Main plugin version -->
<repositories>
<repository>
@@ -25,11 +25,11 @@
</repositories>
<dependencies>
<!-- Bukkit, AreaShop aims to support 1.13.2+ -->
<!-- Bukkit, AreaShop aims to support 1.16.5+ -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.13.2-R0.1-SNAPSHOT</version>
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
@@ -29,9 +29,11 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -49,6 +51,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
public static final String groupsFile = "groups.yml";
public static final String defaultFile = "default.yml";
public static final String configFile = "config.yml";
public static final String signLogFile = "signErrors.yml";
public static final String configFileHidden = "hiddenConfig.yml";
public static final String versionFile = "versions";
// Euro tag for in the config
@@ -111,7 +114,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
public static final String tagTeleportWorld = "tpworld";
// REGEX Matcher for Versions which follow a x.x.x format.
private static final Pattern VERSION_MATCHER_3 = Pattern.compile("([0-9]+)\\.([0-9]+)\\.([0-9]+)");
private static final Pattern VERSION_MATCHER_2 = Pattern.compile("([0-9]+)\\.([0-9]+)\\.([0-9]+)");
private static final Pattern VERSION_MATCHER_2 = Pattern.compile("([0-9]+)\\.([0-9]+)");
// Statically available instance
private static AreaShop instance = null;
// General variables
@@ -129,6 +132,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
private List<String> chatprefix = null;
private boolean ready = false;
private GithubUpdateCheck githubUpdateCheck = null;
private SignErrorLogger signErrorLogger;
public static AreaShop getInstance() {
return AreaShop.instance;
@@ -419,6 +423,12 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
* Register all required tasks.
*/
private void setupTasks() {
long signLogTicks = Utils.millisToTicks(TimeUnit.MINUTES.toMillis(5));
Bukkit.getScheduler().runTaskTimerAsynchronously(this,
() -> signErrorLogger.saveIfDirty(),
signLogTicks,
signLogTicks
);
// Rent expiration timer
long expirationCheck =
Utils.millisToTicks(Utils.getDurationFromSecondsOrString("expiration.delay"));
@@ -578,7 +588,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
error("Vault plugin is not present or has not loaded correctly");
error = true;
}
signErrorLogger = new SignErrorLogger(new File(signLogFile));
// Load all data from files and check versions
fileManager = new FileManager();
managers.add(fileManager);
@@ -622,7 +632,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
Matcher matcher0 = VERSION_MATCHER_3.matcher(latestVersion);
boolean dual0 = false;
if (!matcher0.find()) {
matcher0 = VERSION_MATCHER_3.matcher(latestVersion);
matcher0 = VERSION_MATCHER_2.matcher(latestVersion);
if (!matcher0.find()) {
throw new IllegalArgumentException("Invalid Versions Detected!");
}
@@ -681,6 +691,22 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
return true;
}
final String packageName = "me.wiefferink.areashop.handlers.";
final Plugin fawe = getServer().getPluginManager().getPlugin("FastAsyncWorldEdit");
if (fawe != null && fawe.isEnabled()) {
final String version = fawe.getDescription().getVersion();
if (version.startsWith("1.15") || version.startsWith("1.16")) {
try {
this.worldEditInterface = (WorldEditInterface) Class.forName(packageName + "fawe.v1_15.FastAsyncWorldEditHandler")
.getDeclaredConstructor(AreaShopInterface.class).newInstance(this);
} catch (ReflectiveOperationException ex) {
getLogger().log(Level.SEVERE, "Failed to hook into FAWE!");
ex.printStackTrace();
}
}
if (this.worldEditInterface == null) {
info("FAWE Version: " + version + " detected, no compatible handler found. Using the WorldEdit handler instead.");
}
}
if (this.worldEditInterface == null) {
final String version = worldEdit.getDescription().getVersion();
Matcher matcher = VERSION_MATCHER_3.matcher(version);
@@ -702,23 +728,6 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
return false;
}
}
this.worldEdit = (WorldEditPlugin) worldEdit;
final Plugin fawe = getServer().getPluginManager().getPlugin("FastAsyncWorldEdit");
if (fawe != null && fawe.isEnabled()) {
final String version = fawe.getDescription().getVersion();
if (version.startsWith("1.15") || version.startsWith("1.16")) {
try {
this.worldEditInterface = (WorldEditInterface) Class.forName(packageName + "fawe.v1_15.FastAsyncWorldEditHandler")
.getDeclaredConstructor(AreaShopInterface.class).newInstance(this);
} catch (ReflectiveOperationException ex) {
getLogger().log(Level.SEVERE, "Failed to hook into FAWE!");
ex.printStackTrace();
}
}
if (this.worldEditInterface == null) {
info("FAWE Version: " + version + " detected, no compatible handler found. Using the WorldEdit handler instead.");
}
}
final String wgVersion = worldGuard.getDescription().getVersion();
Matcher matcher = VERSION_MATCHER_3.matcher(wgVersion);
if (!matcher.find()) {
@@ -773,6 +782,10 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
return worldEdit;
}
public SignErrorLogger getSignErrorLogger() {
return signErrorLogger;
}
/**
* Reload all files of the plugin and update all regions.
*
@@ -125,7 +125,7 @@ public class AddCommand extends CommandAreaShop {
TreeSet<String> namesNoPermission = new TreeSet<>();
TreeSet<String> namesAddCancelled = new TreeSet<>(); // Denied by an event listener
Utils.runAsBatches(regions.entrySet(),
Math.min(plugin.getConfig().getInt("adding.regionsPerTick"), 1),
plugin.getConfig().getInt("adding.regionsPerTick"),
(regionEntry) -> {
String regionName = regionEntry.getKey();
ProtectedRegion region = regionEntry.getValue();
@@ -6,10 +6,7 @@ import org.apache.commons.lang.StringUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Abstract class for generalising command classes.
@@ -31,11 +28,12 @@ public abstract class CommandAreaShop {
* @return true if it can execute the command, false otherwise
*/
public boolean canExecute(Command command, String[] args) {
String commandString = command.getName() + " " + StringUtils.join(args, " ");
if(commandString.length() > getCommandStart().length()) {
return commandString.toLowerCase().startsWith(getCommandStart().toLowerCase() + " ");
String commandString = command.getName() + " " + StringUtils.join(args, " ").toLowerCase();
final String start = getCommandStart().toLowerCase();
if(commandString.length() > start.length()) {
return commandString.startsWith(start + " ");
}
return commandString.toLowerCase().startsWith(getCommandStart().toLowerCase());
return commandString.startsWith(start);
}
/**
@@ -46,7 +44,7 @@ public abstract class CommandAreaShop {
* @return A collection with all the possibilities for argument to complete
*/
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
return new ArrayList<>();
return Collections.emptyList();
}
/**
@@ -89,7 +87,7 @@ public abstract class CommandAreaShop {
return false;
}
private class CommandTime {
private static class CommandTime {
public final String command;
public final long time;
@@ -51,7 +51,7 @@ public class DelCommand extends CommandAreaShop {
return;
}
List<GeneralRegion> regions = Utils.getRegionsInSelection(selection);
if(regions.isEmpty()) {
if(regions == null || regions.isEmpty()) {
plugin.message(player, "cmd-noRegionsFound");
return;
}
@@ -5,78 +5,79 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TeleportCommand extends CommandAreaShop {
@Override
public String getCommandStart() {
return "areashop tp";
}
/**
* Check if a person can teleport to the region (assuming he is not teleporting to a sign).
*
* @param person The person to check
* @param region The region to check for
* @return true if the person can teleport to it, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if (!(person instanceof Player)) {
return false;
}
Player player = (Player) person;
return player.hasPermission("areashop.teleportall")
|| region.isOwner(player) && player.hasPermission("areashop.teleport")
|| region.isAvailable() && player.hasPermission("areashop.teleportavailable")
|| region.getFriendsFeature().getFriends().contains(player.getUniqueId()) && player.hasPermission("areashop.teleportfriend");
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.teleportall") || target.hasPermission("areashop.teleport")) {
return "help-teleport";
}
return null;
}
@Override
public String getCommandStart() {
return "areashop tp";
}
/**
* Check if a person can teleport to the region (assuming he is not teleporting to a sign).
* @param person The person to check
* @param region The region to check for
* @return true if the person can teleport to it, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if(!(person instanceof Player)) {
return false;
}
Player player = (Player)person;
return player.hasPermission("areashop.teleportall")
|| region.isOwner(player) && player.hasPermission("areashop.teleport")
|| region.isAvailable() && player.hasPermission("areashop.teleportavailable")
|| region.getFriendsFeature().getFriends().contains(player.getUniqueId()) && player.hasPermission("areashop.teleportfriend");
}
@Override
public String getHelp(CommandSender target) {
if (target.hasPermission("areashop.teleportall") || target.hasPermission("areashop.teleport")) {
return "help-teleport";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.teleport") && !sender.hasPermission("areashop.teleportall") && !sender.hasPermission("areashop.teleportavailable") && !sender.hasPermission("areashop.teleportavailablesign") && !sender.hasPermission("areashop.teleportsign") && !sender.hasPermission("areashop.teleportsignall") && !sender.hasPermission("areashop.teleportfriend") && !sender.hasPermission("teleportfriendsign")) {
plugin.message(sender, "teleport-noPermission");
return;
}
if(!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
if(args.length <= 1 || args[1] == null) {
plugin.message(sender, "teleport-help");
return;
}
Player player = (Player)sender;
GeneralRegion region = plugin.getFileManager().getRegion(args[1]);
if(region == null) {
plugin.message(player, "teleport-noRentOrBuy", args[1]);
return;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!sender.hasPermission("areashop.teleport") && !sender.hasPermission("areashop.teleportall") && !sender.hasPermission("areashop.teleportavailable") && !sender.hasPermission("areashop.teleportavailablesign") && !sender.hasPermission("areashop.teleportsign") && !sender.hasPermission("areashop.teleportsignall") && !sender.hasPermission("areashop.teleportfriend") && !sender.hasPermission("teleportfriendsign")) {
plugin.message(sender, "teleport-noPermission");
return;
}
if (!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
if (args.length <= 1 || args[1] == null) {
plugin.message(sender, "teleport-help");
return;
}
Player player = (Player) sender;
GeneralRegion region = plugin.getFileManager().getRegion(args[1]);
if (region == null) {
plugin.message(player, "teleport-noRentOrBuy", args[1]);
return;
}
boolean toSign = args.length >= 3 && (
args[2].equalsIgnoreCase("sign")
|| args[2].equalsIgnoreCase("yes")
|| args[2].equalsIgnoreCase("true")
);
region.getTeleportFeature().teleportPlayer(player, toSign, true);
}
boolean toSign = args.length >= 3 && (
args[2].equalsIgnoreCase("sign")
|| args[2].equalsIgnoreCase("yes")
|| args[2].equalsIgnoreCase("true")
);
region.getTeleportFeature().teleportPlayer(player, toSign, true);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
result.addAll(plugin.getFileManager().getRegionNames());
} else if(toComplete == 3) {
result.add("sign");
}
return result;
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
if (toComplete == 2) {
return plugin.getFileManager().getRegionNames();
} else if (toComplete == 3) {
return Collections.singletonList("sign");
}
return Collections.emptyList();
}
}
@@ -92,7 +92,7 @@ public class FriendsFeature extends RegionFeature {
HashSet<String> result = new HashSet<>();
for(UUID friend : getFriends()) {
OfflinePlayer player = Bukkit.getOfflinePlayer(friend);
if(player.hasPlayedBefore() && player.getName() != null) {
if(player != null && player.getName() != null) {
result.add(player.getName());
}
}
@@ -1,600 +0,0 @@
package me.wiefferink.areashop.features;
import co.aikar.taskchain.TaskChain;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import io.papermc.lib.PaperLib;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.areashop.tools.Value;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
public class TeleportFeature extends RegionFeature {
public TeleportFeature() {
}
public TeleportFeature(GeneralRegion region) {
setRegion(region);
}
/**
* Checks if a certain location is safe to teleport to.
*
* @param location The location to check
* @return true if it is safe, otherwise false
*/
private static boolean isSafe(Location location) {
Block feet = location.getBlock();
BlockState head = PaperLib.getBlockState(feet.getRelative(BlockFace.UP), true).getState();
BlockState below = PaperLib.getBlockState(feet.getRelative(BlockFace.DOWN), true).getState();
BlockState above = PaperLib.getBlockState(head.getBlock().getRelative(BlockFace.UP), true).getState();
// Check the block at the feet and head of the player
if ((feet.getType().isSolid() && !canSpawnIn(feet.getType())) || feet.isLiquid()) {
return false;
} else if ((head.getType().isSolid() && !canSpawnIn(head.getType())) || head.getBlock().isLiquid()) {
return false;
} else if (!below.getType().isSolid() || cannotSpawnOn(below.getType()) || below.getBlock().isLiquid()) {
return false;
} else if (above.getBlock().isLiquid() || cannotSpawnBeside(above.getType())) {
return false;
}
// Get all blocks around the player (below foot level, foot level, head level and above head level)
Set<Material> around = new HashSet<>();
for (int y = 0; y <= 3; y++) {
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
// Skip blocks in the column of the player
if (x == 0 && z == 0) {
continue;
}
around.add(PaperLib.getBlockState(below.getBlock().getRelative(x, y, z), true).getState().getType());
}
}
}
// Check the blocks around the player
for (Material material : around) {
if (cannotSpawnBeside(material)) {
return false;
}
}
return true;
}
/**
* Check if a player can spawn in here.
*
* @param material Material to check (assumed that this is at the feet or head level)
* @return true when it is safe to spawn inside, otherwise false
*/
private static boolean canSpawnIn(Material material) {
String name = material.name();
return name.contains("DOOR")
|| name.contains("SIGN")
|| name.contains("PLATE") // Redstone plates
|| name.equals("DRAGON_EGG");
}
/**
* Check if a player can spawn on here.
*
* @param material Material to check (assumed that this is below the feet)
* @return true when it is safe to spawn on top of, otherwise false
*/
private static boolean cannotSpawnOn(Material material) {
String name = material.name();
return name.equals("CACTUS")
|| name.contains("PISTON")
|| name.contains("SIGN")
|| name.contains("DOOR")
|| name.contains("PLATE")
|| name.contains("REDSTONE_LAMP")
|| name.contains("FENCE")
|| name.contains("GLASS_PANE") || name.contains("THIN_GLASS")
|| name.equals("DRAGON_EGG")
|| name.contains("MAGMA");
}
/**
* Check if a player can spawn next to it.
*
* @param material Material to check (assumed that this is somewhere around the player)
* @return true when it is safe to spawn next to, otherwise false
*/
private static boolean cannotSpawnBeside(Material material) {
String name = material.name();
return name.contains("LAVA")
|| name.contains("CACTUS")
|| name.equals("FIRE")
|| name.contains("MAGMA");
}
/**
* Get the teleportlocation set for this region.
*
* @return The teleport location, or null if not set
*/
public Location getTeleportLocation() {
return Utils.configToLocation(getRegion().getConfigurationSectionSetting("general.teleportLocation"));
}
/**
* Check if the region has a teleportLocation specified.
*
* @return true if the region has a teleportlocation, false otherwise
*/
public boolean hasTeleportLocation() {
return getRegion().getConfigurationSectionSetting("general.teleportLocation") != null;
}
/**
* Set the teleport location of this region.
*
* @param location The location to set as teleport location
*/
public void setTeleport(Location location) {
if (location == null) {
getRegion().setSetting("general.teleportLocation", null);
} else {
getRegion().setSetting("general.teleportLocation", Utils.locationToConfig(location, true));
}
}
/**
* Teleport a player to the region or sign.
*
* @param player Player that should be teleported
* @param toSign true to teleport to the first sign of the region, false for teleporting to the region itself
* @param checkRestrictions Set to true if teleport permissions should be checked, false otherwise, also toggles cross-world check
* @return true if the teleport succeeded, otherwise false
*/
public CompletableFuture<Boolean> teleportPlayer(Player player, boolean toSign, boolean checkRestrictions) {
// Check basics
if (getRegion().getWorld() == null) {
getRegion().message(player, "general-noWorld");
return CompletableFuture.completedFuture(false);
}
if (getRegion().getRegion() == null) {
getRegion().message(player, "general-noRegion");
return CompletableFuture.completedFuture(false);
}
if (checkRestrictions) {
// Check correct world
if (!getRegion().getBooleanSetting("general.teleportCrossWorld") && !player.getWorld().equals(getRegion().getWorld())) {
getRegion().message(player, "teleport-wrongWorld", player.getWorld().getName());
return CompletableFuture.completedFuture(false);
}
boolean owner = player.getUniqueId().equals(getRegion().getOwner());
boolean friend = getRegion().getFriendsFeature().getFriends().contains(player.getUniqueId());
boolean available = getRegion().isAvailable();
// Teleport to sign instead if they dont have permission for teleporting to region
if ((!toSign && owner && !player.hasPermission("areashop.teleport") && player.hasPermission("areashop.teleportsign")
|| !toSign && !owner && !friend && !player.hasPermission("areashop.teleportall") && player.hasPermission("areashop.teleportsignall")
|| !toSign && !owner && friend && !player.hasPermission("areashop.teleportfriend") && player.hasPermission("areashop.teleportfriendsign")
|| !toSign && !owner && !friend && available && !player.hasPermission("areashop.teleportavailable") && player.hasPermission("areashop.teleportavailablesign"))) {
getRegion().message(player, "teleport-changedToSign");
toSign = true;
}
// Check permissions
if (owner && !available && !player.hasPermission("areashop.teleport") && !toSign) {
getRegion().message(player, "teleport-noPermission");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportall") && !toSign && !friend) {
getRegion().message(player, "teleport-noPermissionOther");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportfriend") && !toSign && friend) {
getRegion().message(player, "teleport-noPermissionFriend");
return CompletableFuture.completedFuture(false);
} else if (available && !player.hasPermission("areashop.teleportavailable") && !toSign) {
getRegion().message(player, "teleport-noPermissionAvailable");
return CompletableFuture.completedFuture(false);
} else if (owner && !available && !player.hasPermission("areashop.teleportsign") && toSign) {
getRegion().message(player, "teleport-noPermissionSign");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportsignall") && toSign && !friend) {
getRegion().message(player, "teleport-noPermissionOtherSign");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportfriendsign") && toSign && friend) {
getRegion().message(player, "teleport-noPermissionFriendSign");
return CompletableFuture.completedFuture(false);
} else if (available && !player.hasPermission("areashop.teleportavailablesign") && toSign) {
getRegion().message(player, "teleport-noPermissionAvailableSign");
return CompletableFuture.completedFuture(false);
}
}
// Get the starting location
Value<Boolean> toSignRef = new Value<>(toSign);
Location startLocation = getStartLocation(player, toSignRef);
toSign = toSignRef.get();
boolean insideRegion;
if (toSign) {
insideRegion = getRegion().getBooleanSetting("general.teleportToSignIntoRegion");
} else {
insideRegion = getRegion().getBooleanSetting("general.teleportIntoRegion");
}
// Check locations starting from startLocation and then a cube that increases
// radius around that (until no block in the region is found at all cube sides)
ProtectedRegion worldguardRegion = getRegion().getRegion();
boolean blocksInRegion = worldguardRegion.contains(startLocation.getBlockX(), startLocation.getBlockY(), startLocation.getBlockZ());
if (!blocksInRegion && insideRegion) {
getRegion().message(player, "teleport-blocked");
return CompletableFuture.completedFuture(false);
}
// Tracking of which sides to continue the search
final World world = getRegion().getWorld();
BlockVector3 min = worldguardRegion.getMinimumPoint();
BlockVector3 max = worldguardRegion.getMaximumPoint();
final Collection<int[]> collection = new HashSet<>();
for (int x = min.getX(); x < max.getX(); x += 16) {
for (int z = min.getZ(); z < max.getZ(); z += 16) {
collection.add(new int[]{x, z});
}
}
final CompletableFuture<Boolean> future = new CompletableFuture<>();
final boolean finalToSign = toSign;
TaskChain<?> chain = Utils.newChain();
if (collection.size() < 64) {
chain = chain.future(Utils.runAsBatches(collection, 16, (arr) -> PaperLib.getChunkAtAsync(world, arr[0], arr[1]), false));
}
chain.delay(3).syncLast((unused) -> {
boolean blocksInRegionCopy = blocksInRegion;
Location safeLocation = startLocation;
// Tries limit tracking
int radius = 1;
int checked = 1;
int maxTries = plugin.getConfig().getInt("maximumTries");
// Tracking of which sides to continue the search
boolean done = isSafe(safeLocation);
boolean northDone = false, eastDone = false, southDone = false, westDone = false, topDone = false, bottomDone = false;
boolean continueThisDirection;
while ((blocksInRegionCopy || !insideRegion) && !done) {
blocksInRegionCopy = false;
// North side
continueThisDirection = false;
for (int x = -radius + 1; x <= radius && !done && !northDone; x++) {
for (int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(x, y, -radius);
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
northDone = northDone || !continueThisDirection;
// East side
continueThisDirection = false;
for (int z = -radius + 1; z <= radius && !done && !eastDone; z++) {
for (int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(radius, y, z);
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
eastDone = eastDone || !continueThisDirection;
// South side
continueThisDirection = false;
for (int x = radius - 1; x >= -radius && !done && !southDone; x--) {
for (int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(x, y, radius);
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
southDone = southDone || !continueThisDirection;
// West side
continueThisDirection = false;
for (int z = radius - 1; z >= -radius && !done && !westDone; z--) {
for (int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(-radius, y, z);
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
westDone = westDone || !continueThisDirection;
// Top side
continueThisDirection = false;
// Middle block of the top
if ((startLocation.getBlockY() + radius) > 256) {
topDone = true;
}
if (!done && !topDone) {
safeLocation = startLocation.clone().add(0, radius, 0);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
for (int r = 1; r <= radius && !done && !topDone; r++) {
// North
for (int x = -r + 1; x <= r && !done; x++) {
safeLocation = startLocation.clone().add(x, radius, -r);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// East
for (int z = -r + 1; z <= r && !done; z++) {
safeLocation = startLocation.clone().add(r, radius, z);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// South side
for (int x = r - 1; x >= -r && !done; x--) {
safeLocation = startLocation.clone().add(x, radius, r);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// West side
for (int z = r - 1; z >= -r && !done; z--) {
safeLocation = startLocation.clone().add(-r, radius, z);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
topDone = topDone || !continueThisDirection;
// Bottom side
continueThisDirection = false;
// Middle block of the bottom
if (startLocation.getBlockY() - radius < 0) {
bottomDone = true;
}
if (!done && !bottomDone) {
safeLocation = startLocation.clone().add(0, -radius, 0);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
for (int r = 1; r <= radius && !done && !bottomDone; r++) {
// North
for (int x = -r + 1; x <= r && !done; x++) {
safeLocation = startLocation.clone().add(x, -radius, -r);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// East
for (int z = -r + 1; z <= r && !done; z++) {
safeLocation = startLocation.clone().add(r, -radius, z);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// South side
for (int x = r - 1; x >= -r && !done; x--) {
safeLocation = startLocation.clone().add(x, -radius, r);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// West side
for (int z = r - 1; z >= -r && !done; z--) {
safeLocation = startLocation.clone().add(-r, -radius, z);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
bottomDone = bottomDone || !continueThisDirection;
// Increase cube radius
radius++;
}
if (done && isSafe(safeLocation)) {
if (finalToSign) {
getRegion().message(player, "teleport-successSign");
// Let the player look at the sign
Vector playerVector = safeLocation.toVector();
playerVector.setY(playerVector.getY() + player.getEyeHeight(true));
Vector signVector = getRegion().getSignsFeature().getSigns().get(0).getLocation().toVector().add(new Vector(0.5, 0.5, 0.5));
Vector direction = playerVector.clone().subtract(signVector).normalize();
safeLocation.setYaw(180 - (float) Math.toDegrees(Math.atan2(direction.getX(), direction.getZ())));
safeLocation.setPitch(90 - (float) Math.toDegrees(Math.acos(direction.getY())));
} else {
getRegion().message(player, "teleport-success");
}
AreaShop.debug("Found location: " + safeLocation.toString() + " Tries: " + checked);
PaperLib.teleportAsync(player, safeLocation).thenAccept(future::complete);
} else {
getRegion().message(player, "teleport-noSafe", checked, maxTries);
AreaShop.debug("No location found, checked " + checked + " spots of max " + maxTries);
future.complete(false);
}
}).execute();
return future;
}
/**
* Teleport a player to the region or sign when he has permissions for it.
*
* @param player Player that should be teleported
* @param toSign true to teleport to the first sign of the region, false for teleporting to the region itself
* @return true if the teleport succeeded, otherwise false
*/
public CompletableFuture<Boolean> teleportPlayer(Player player, boolean toSign) {
return teleportPlayer(player, toSign, true);
}
/**
* Teleport a player to the region when he has permissions for it.
*
* @param player Player that should be teleported
* @return true if the teleport succeeded, otherwise false
*/
public CompletableFuture<Boolean> teleportPlayer(Player player) {
return teleportPlayer(player, false, true);
}
/**
* Get the start location of a safe teleport search.
*
* @param player The player to get it for
* @param toSign true to try teleporting to the first sign, false for teleporting to the region
* @return The start location
*/
private Location getStartLocation(Player player, Value<Boolean> toSign) {
Location startLocation = null;
ProtectedRegion worldguardRegion = getRegion().getRegion();
// Try to get sign location
List<RegionSign> signs = getRegion().getSignsFeature().getSigns();
boolean signAvailable = !signs.isEmpty();
if (toSign.get()) {
if (signAvailable) {
// Use the location 1 below the sign to prevent weird spawing above the sign
startLocation = signs.get(0).getLocation(); //.subtract(0.0, 1.0, 0.0);
startLocation.setPitch(player.getLocation().getPitch());
startLocation.setYaw(player.getLocation().getYaw());
// Move player x blocks away from the sign
double distance = getRegion().getDoubleSetting("general.teleportSignDistance");
if (distance > 0) {
BlockFace facing = getRegion().getSignsFeature().getSigns().get(0).getFacing();
Vector facingVector = new Vector(facing.getModX(), facing.getModY(), facing.getModZ())
.normalize()
.multiply(distance);
startLocation.setX(startLocation.getBlockX() + 0.5);
startLocation.setZ(startLocation.getBlockZ() + 0.5);
startLocation.add(facingVector);
}
} else {
// No sign available
getRegion().message(player, "teleport-changedToNoSign");
toSign.set(false);
}
}
// Use teleportation location that is set for the region
if (startLocation == null && hasTeleportLocation()) {
startLocation = getTeleportLocation();
}
// Calculate a default location
if (startLocation == null) {
// Set to block in the middle, y configured in the config
Vector regionMin = AreaShop.getInstance().getWorldGuardHandler().getMinimumPoint(worldguardRegion);
Vector regionMax = AreaShop.getInstance().getWorldGuardHandler().getMaximumPoint(worldguardRegion);
Vector middle = regionMin.clone().midpoint(regionMax);
String configSetting = getRegion().getStringSetting("general.teleportLocationY");
if ("bottom".equalsIgnoreCase(configSetting)) {
middle = middle.setY(regionMin.getBlockY());
} else if ("top".equalsIgnoreCase(configSetting)) {
middle = middle.setY(regionMax.getBlockY());
} else if ("middle".equalsIgnoreCase(configSetting)) {
middle = middle.setY(middle.getBlockY());
} else {
try {
int vertical = Integer.parseInt(configSetting);
middle = middle.setY(vertical);
} catch (NumberFormatException e) {
AreaShop.warn("Could not parse general.teleportLocationY: '" + configSetting + "'");
}
}
startLocation = new Location(getRegion().getWorld(), middle.getX(), middle.getY(), middle.getZ(), player.getLocation().getYaw(), player.getLocation().getPitch());
}
// Set location in the center of the block
startLocation.setX(startLocation.getBlockX() + 0.5);
startLocation.setZ(startLocation.getBlockZ() + 0.5);
return startLocation;
}
}
@@ -255,7 +255,7 @@ public class WorldGuardRegionFlagsFeature extends RegionFeature {
result = result.replace("&1", "&B");
result = result.replace("&d", "&p");
result = result.replace("&5", "&P");
//result = result.replace("&0", "&0");
result = result.replace("&0", "&0");
result = result.replace("&8", "&1");
result = result.replace("&7", "&2");
result = result.replace("&f", "&w");
@@ -4,6 +4,7 @@ import com.google.common.base.Objects;
import io.papermc.lib.PaperLib;
import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.managers.SignErrorLogger;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.Utils;
@@ -11,6 +12,7 @@ import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
@@ -109,7 +111,7 @@ public class RegionSign {
/**
* Get the material of the sign as saved in the config.
*
* @return Material of the sign, usually {@link Material#WALL_SIGN}, {@link Material#SIGN}, or one of the other wood types (different result for 1.13-), Material.AIR if none.
* @return Material of the sign, usually or one of the other wood types (different result for 1.13-), Material.AIR if none.
*/
public Material getMaterial() {
String name = getRegion().getConfig().getString("general.signs." + key + ".signType");
@@ -123,6 +125,7 @@ public class RegionSign {
* @return true if the update was successful, otherwise false
*/
public boolean update() {
// Ignore updates of signs in chunks that are not loaded
Location signLocation = getLocation();
if (signLocation == null
@@ -131,20 +134,23 @@ public class RegionSign {
return false;
}
if (getRegion().isDeleted()) {
final GeneralRegion region = getRegion();
if (region.isDeleted()) {
return false;
}
YamlConfiguration regionConfig = getRegion().getConfig();
YamlConfiguration regionConfig = region.getConfig();
ConfigurationSection signConfig = getProfile();
Block block = signLocation.getBlock();
if (signConfig == null || !signConfig.isSet(getRegion().getState().getValue())) {
final String value = region.getState().getValue();
if (signConfig == null || !signConfig.isSet(value)) {
block.setType(Material.AIR);
return true;
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue());
assert stateConfig != null;
ConfigurationSection stateConfig = signConfig.getConfigurationSection(value);
// Get the lines
String[] signLines = new String[4];
boolean signEmpty = true;
@@ -156,51 +162,52 @@ public class RegionSign {
block.setType(Material.AIR);
return true;
}
final BlockStateSnapshotResult snapshotResult = PaperLib.getBlockState(block, true);
final BlockState blockState = snapshotResult.getState();
final BlockStateSnapshotResult snapshot = PaperLib.getBlockState(block, false);
final BlockState blockState = snapshot.getState();
final BlockData blockData = blockState.getBlockData();
final SignErrorLogger errorLogger = AreaShop.getInstance().getSignErrorLogger();
Material signType = getMaterial();
// Place the sign back (with proper rotation and type) after it has been hidden or (indirectly) destroyed
if (!Materials.isSign(block.getType())) {
Material signType = getMaterial();
if (signType.name().contains("SIGN")) {
final BlockData data = blockState.getBlockData();
// Don't do physics here, we first need to update the direction
blockState.setType(signType);
if (data instanceof WallSign) {
((WallSign) data).setFacing(getFacing());
} else if (data instanceof Sign) {
((org.bukkit.block.data.type.Sign) data).setRotation(getFacing());
}
blockState.setBlockData(data);
blockState.update(false, false);
// Check if the sign has popped
if (!Materials.isSign(block.getType())) {
AreaShop.warn("Setting sign", key, "of region", getRegion().getName(), "failed, could not set sign block back");
return false;
}
} else {
AreaShop.warn("Setting sign", key, "of region", getRegion().getName(), "failed, RegionSign material was: " + signType.name());
if (!Tag.SIGNS.isTagged(blockState.getType())) {
// Don't do physics here, we first need to update the direction
blockState.setType(signType);
if (blockData instanceof WallSign) {
((WallSign) blockData).setFacing(getFacing());
} else if (blockData instanceof Sign) {
((org.bukkit.block.data.type.Sign) blockData).setRotation(getFacing());
}
blockState.setBlockData(blockData);
blockState.update(false, true);
// Check if the sign has popped
if (!Materials.isSign(block.getType())) {
final String message = String.format("Setting sign of $1%s of region $2%s failed, could not set sign block back", key, region.getName());
errorLogger.submitWarning(message);
return false;
}
} else {
final String message = "Setting sign $1%s of region $2%s failed, RegionSign material was: $3%s";
errorLogger.submitWarning(String.format(message, key, region.getName(), signType.name()));
}
// Save current rotation and type
if (!regionConfig.isString("general.signs." + key + ".signType")) {
getRegion().setSetting("general.signs." + key + ".signType", block.getType().name());
region.setSetting("general.signs." + key + ".signType", blockState.getType().name());
}
if (!regionConfig.isString("general.signs." + key + ".facing")) {
final BlockFace rotation;
if (Materials.isSign(block.getType())) {
BlockData data = blockState.getBlockData();
if (data instanceof org.bukkit.block.data.type.Sign) {
rotation = ((org.bukkit.block.data.type.Sign) block.getBlockData()).getRotation();
} else if (data instanceof WallSign) {
rotation = ((WallSign) data).getFacing();
if (blockData instanceof org.bukkit.block.data.type.Sign) {
rotation = ((org.bukkit.block.data.type.Sign) blockData).getRotation();
} else if (blockData instanceof WallSign) {
rotation = ((WallSign) blockData).getFacing();
} else {
rotation = null;
}
} else {
rotation = null;
}
getRegion().setSetting("general.signs." + key + ".facing", rotation == null ? null : rotation.toString());
region.setSetting("general.signs." + key + ".facing", rotation == null ? null : rotation.toString());
}
// Apply replacements and color and then set it on the sign
@@ -214,7 +221,9 @@ public class RegionSign {
signLines[i] = Utils.applyColors(signLines[i]);
signState.setLine(i, signLines[i]);
}
return signState.update(false, false);
// BlockState#update *should* return true here.
blockState.update(false, false);
return true;
}
/**
@@ -255,7 +264,8 @@ public class RegionSign {
if (signConfig == null) {
return false;
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue().toLowerCase());
final GeneralRegion region = getRegion();
ConfigurationSection stateConfig = signConfig.getConfigurationSection(region.getState().getValue().toLowerCase());
// Run player commands if specified
List<String> playerCommands = new ArrayList<>();
@@ -263,14 +273,14 @@ public class RegionSign {
// TODO move variable checking code to InteractiveMessenger?
playerCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName()));
}
getRegion().runCommands(clicker, playerCommands);
region.runCommands(clicker, playerCommands);
// Run console commands if specified
List<String> consoleCommands = new ArrayList<>();
for (String command : stateConfig.getStringList(clickType.getValue() + "Console")) {
consoleCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName()));
}
getRegion().runCommands(Bukkit.getConsoleSender(), consoleCommands);
region.runCommands(Bukkit.getConsoleSender(), consoleCommands);
return !playerCommands.isEmpty() || !consoleCommands.isEmpty();
}
@@ -7,6 +7,7 @@ import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.events.notify.UpdateRegionEvent;
import me.wiefferink.areashop.features.RegionFeature;
import me.wiefferink.areashop.managers.FileManager;
import me.wiefferink.areashop.managers.SignErrorLogger;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
@@ -22,7 +23,6 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Sign;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -56,12 +56,14 @@ public class SignsFeature extends RegionFeature {
signs = new HashMap<>();
// Setup current signs
ConfigurationSection signSection = region.getConfig().getConfigurationSection("general.signs");
SignErrorLogger errorLogger = AreaShop.getInstance().getSignErrorLogger();
if (signSection != null) {
for (String signKey : signSection.getKeys(false)) {
RegionSign sign = new RegionSign(this, signKey);
Location location = sign.getLocation();
if (location == null) {
AreaShop.warn("Sign with key " + signKey + " of region " + region.getName() + " does not have a proper location");
final String message = "Sign with key $1%s of region $2%s does not have a proper location";
errorLogger.submitWarning(String.format(message, signKey, region.getName()));
continue;
}
signs.put(sign.getStringLocation(), sign);
@@ -598,7 +600,7 @@ public class SignsFeature extends RegionFeature {
* @return A List with all sign locations
*/
public List<Location> getSignLocations() {
List<Location> result = new ArrayList<>(signs.size());
List<Location> result = new ArrayList<>();
for (RegionSign sign : signs.values()) {
result.add(sign.getLocation());
}
@@ -615,19 +617,19 @@ public class SignsFeature extends RegionFeature {
*/
public void addSign(Location location, Material signType, BlockFace facing, String profile) {
int i = 0;
final YamlConfiguration configuration = getRegion().getConfig();
while (configuration.isSet("general.signs." + i)) {
while (getRegion().getConfig().isSet("general.signs." + i)) {
i++;
}
String signPath = "general.signs." + i + ".";
getRegion().setSetting(signPath + "location", Utils.locationToConfig(location));
getRegion().setSetting(signPath + "facing", facing != null ? facing.name() : null);
getRegion().setSetting(signPath + "signType", signType != null ? signType.name() : null);
final GeneralRegion region = getRegion();
region.setSetting(signPath + "location", Utils.locationToConfig(location));
region.setSetting(signPath + "facing", facing != null ? facing.name() : null);
region.setSetting(signPath + "signType", signType != null ? signType.name() : null);
if (profile != null && !profile.isEmpty()) {
getRegion().setSetting(signPath + "profile", profile);
region.setSetting(signPath + "profile", profile);
}
// Add to the map
RegionSign sign = new RegionSign(this, i + "");
RegionSign sign = new RegionSign(this, String.valueOf(i));
signs.put(sign.getStringLocation(), sign);
allSigns.put(sign.getStringLocation(), sign);
signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>())
@@ -0,0 +1,269 @@
package me.wiefferink.areashop.features.teleport;
import co.aikar.taskchain.TaskChain;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import io.papermc.lib.PaperLib;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.RegionFeature;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.areashop.tools.Value;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
public class TeleportFeature extends RegionFeature {
public TeleportFeature() {
}
public TeleportFeature(GeneralRegion region) {
setRegion(region);
}
/**
* Checks if a certain location is safe to teleport to.
*
* @param location The location to check
* @return true if it is safe, otherwise false
*/
static boolean isSafe(Location location) {
Block feet = location.getBlock();
Block head = feet.getRelative(BlockFace.UP);
Block below = feet.getRelative(BlockFace.DOWN);
Block above = head.getRelative(BlockFace.UP);
// Check the block at the feet and head of the player
if ((feet.getType().isSolid() && !canSpawnIn(feet.getType())) || feet.isLiquid()) {
return false;
} else if ((head.getType().isSolid() && !canSpawnIn(head.getType())) || head.isLiquid()) {
return false;
} else if (!below.getType().isSolid() || cannotSpawnOn(below.getType()) || below.isLiquid()) {
return false;
} else if (above.isLiquid() || cannotSpawnBeside(above.getType())) {
return false;
}
// Get all blocks around the player (below foot level, foot level, head level and above head level)
Set<Material> around = new HashSet<>();
for (int y = 0; y <= 3; y++) {
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
// Skip blocks in the column of the player
if (x == 0 && z == 0) {
continue;
}
around.add(below.getRelative(x, y, z).getType());
}
}
}
// Check the blocks around the player
for (Material material : around) {
if (cannotSpawnBeside(material)) {
return false;
}
}
return true;
}
/**
* Check if a player can spawn in here.
*
* @param material Material to check (assumed that this is at the feet or head level)
* @return true when it is safe to spawn inside, otherwise false
*/
private static boolean canSpawnIn(Material material) {
String name = material.name();
return name.contains("DOOR")
|| name.contains("SIGN")
|| name.contains("PLATE") // Redstone plates
|| name.equals("DRAGON_EGG");
}
/**
* Check if a player can spawn on here.
*
* @param material Material to check (assumed that this is below the feet)
* @return true when it is safe to spawn on top of, otherwise false
*/
private static boolean cannotSpawnOn(Material material) {
String name = material.name();
return name.equals("CACTUS")
|| name.contains("PISTON")
|| name.contains("SIGN")
|| name.contains("DOOR")
|| name.contains("PLATE")
|| name.contains("REDSTONE_LAMP")
|| name.contains("FENCE")
|| name.contains("GLASS_PANE") || name.contains("THIN_GLASS")
|| name.equals("DRAGON_EGG")
|| name.contains("MAGMA");
}
/**
* Check if a player can spawn next to it.
*
* @param material Material to check (assumed that this is somewhere around the player)
* @return true when it is safe to spawn next to, otherwise false
*/
private static boolean cannotSpawnBeside(Material material) {
String name = material.name();
return name.contains("LAVA")
|| name.contains("CACTUS")
|| name.equals("FIRE")
|| name.contains("MAGMA");
}
/**
* Get the teleportlocation set for this region.
*
* @return The teleport location, or null if not set
*/
public Location getTeleportLocation() {
return Utils.configToLocation(getRegion().getConfigurationSectionSetting("general.teleportLocation"));
}
/**
* Check if the region has a teleportLocation specified.
*
* @return true if the region has a teleportlocation, false otherwise
*/
public boolean hasTeleportLocation() {
return getRegion().getConfigurationSectionSetting("general.teleportLocation") != null;
}
/**
* Set the teleport location of this region.
*
* @param location The location to set as teleport location
*/
public void setTeleport(Location location) {
if (location == null) {
getRegion().setSetting("general.teleportLocation", null);
} else {
getRegion().setSetting("general.teleportLocation", Utils.locationToConfig(location, true));
}
}
/**
* Teleport a player to the region or sign.
*
* @param player Player that should be teleported
* @param toSign true to teleport to the first sign of the region, false for teleporting to the region itself
* @param checkRestrictions Set to true if teleport permissions should be checked, false otherwise, also toggles cross-world check
* @return true if the teleport succeeded, otherwise false
*/
public CompletableFuture<Boolean> teleportPlayer(Player player, boolean toSign, boolean checkRestrictions) {
return new TeleportJob(this, plugin).executeTeleport(player, toSign, checkRestrictions);
}
/**
* Teleport a player to the region or sign when he has permissions for it.
*
* @param player Player that should be teleported
* @param toSign true to teleport to the first sign of the region, false for teleporting to the region itself
* @return true if the teleport succeeded, otherwise false
*/
public CompletableFuture<Boolean> teleportPlayer(Player player, boolean toSign) {
return teleportPlayer(player, toSign, true);
}
/**
* Teleport a player to the region when he has permissions for it.
*
* @param player Player that should be teleported
* @return true if the teleport succeeded, otherwise false
*/
public CompletableFuture<Boolean> teleportPlayer(Player player) {
return teleportPlayer(player, false, true);
}
/**
* Get the start location of a safe teleport search.
*
* @param player The player to get it for
* @param toSign true to try teleporting to the first sign, false for teleporting to the region
* @return The start location
*/
Location getStartLocation(Player player, Value<Boolean> toSign) {
Location startLocation = null;
ProtectedRegion worldguardRegion = getRegion().getRegion();
// Try to get sign location
List<RegionSign> signs = getRegion().getSignsFeature().getSigns();
boolean signAvailable = !signs.isEmpty();
if (toSign.get()) {
if (signAvailable) {
// Use the location 1 below the sign to prevent weird spawing above the sign
startLocation = signs.get(0).getLocation(); //.subtract(0.0, 1.0, 0.0);
startLocation.setPitch(player.getLocation().getPitch());
startLocation.setYaw(player.getLocation().getYaw());
// Move player x blocks away from the sign
double distance = getRegion().getDoubleSetting("general.teleportSignDistance");
if (distance > 0) {
BlockFace facing = getRegion().getSignsFeature().getSigns().get(0).getFacing();
Vector facingVector = new Vector(facing.getModX(), facing.getModY(), facing.getModZ())
.normalize()
.multiply(distance);
startLocation.setX(startLocation.getBlockX() + 0.5);
startLocation.setZ(startLocation.getBlockZ() + 0.5);
startLocation.add(facingVector);
}
} else {
// No sign available
getRegion().message(player, "teleport-changedToNoSign");
toSign.set(false);
}
}
// Use teleportation location that is set for the region
if (startLocation == null && hasTeleportLocation()) {
startLocation = getTeleportLocation();
}
// Calculate a default location
if (startLocation == null) {
// Set to block in the middle, y configured in the config
Vector regionMin = AreaShop.getInstance().getWorldGuardHandler().getMinimumPoint(worldguardRegion);
Vector regionMax = AreaShop.getInstance().getWorldGuardHandler().getMaximumPoint(worldguardRegion);
Vector middle = regionMin.clone().midpoint(regionMax);
String configSetting = getRegion().getStringSetting("general.teleportLocationY");
if ("bottom".equalsIgnoreCase(configSetting)) {
middle = middle.setY(regionMin.getBlockY());
} else if ("top".equalsIgnoreCase(configSetting)) {
middle = middle.setY(regionMax.getBlockY());
} else if ("middle".equalsIgnoreCase(configSetting)) {
middle = middle.setY(middle.getBlockY());
} else {
try {
int vertical = Integer.parseInt(configSetting);
middle = middle.setY(vertical);
} catch (NumberFormatException e) {
AreaShop.warn("Could not parse general.teleportLocationY: '" + configSetting + "'");
}
}
startLocation = new Location(getRegion().getWorld(), middle.getX(), middle.getY(), middle.getZ(), player.getLocation().getYaw(), player.getLocation().getPitch());
}
// Set location in the center of the block
startLocation.setX(startLocation.getBlockX() + 0.5);
startLocation.setZ(startLocation.getBlockZ() + 0.5);
return startLocation;
}
}
@@ -0,0 +1,639 @@
package me.wiefferink.areashop.features.teleport;
import co.aikar.taskchain.TaskChain;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import io.papermc.lib.PaperLib;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.areashop.tools.Value;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
class TeleportJob {
private final AreaShop plugin;
private final GeneralRegion region;
private final TeleportFeature feature;
private BukkitTask notifierTask;
private boolean running;
private int checked;
private boolean done;
private boolean blocksInRegionCopy;
private boolean northDone = false, eastDone = false, southDone = false, westDone = false, topDone = false, bottomDone = false;
private boolean continueThisDirection;
private int radius = 1;
public TeleportJob(final TeleportFeature region, final AreaShop plugin) {
this.feature = region;
this.region = region.getRegion();
this.plugin = plugin;
}
private static Function<double[], Boolean> generateContainsFunction(final ProtectedRegion worldguardRegion) {
final BlockVector3 min = worldguardRegion.getMinimumPoint(), max = worldguardRegion.getMaximumPoint();
if (worldguardRegion instanceof GlobalProtectedRegion) {
return (unused) -> true;
} else if (worldguardRegion instanceof ProtectedCuboidRegion) {
return coords -> {
final double x = coords[0];
final double y = coords[1];
final double z = coords[2];
return x >= min.getBlockX() && x < max.getBlockX() + 1
&& y >= min.getBlockY() && y < max.getBlockY() + 1
&& z >= min.getBlockZ() && z < max.getBlockZ() + 1;
};
} else if (worldguardRegion instanceof ProtectedPolygonalRegion) {
final int minY = min.getY(), maxY = max.getY();
final List<BlockVector2> points = worldguardRegion.getPoints();
return coords -> {
int targetX = (int) Math.round(coords[0]); // Width
int targetY = (int) Math.round(coords[1]); // Height
int targetZ = (int) Math.round(coords[2]); // Depth
if (targetY < minY || targetY > maxY) {
return false;
}
//Quick and dirty check.
if (targetX < min.getBlockX() || targetX > max.getBlockX() || targetZ < min.getBlockZ() || targetZ > max.getBlockZ()) {
return false;
}
boolean inside = false;
int npoints = points.size();
int xNew, zNew;
int xOld, zOld;
int x1, z1;
int x2, z2;
long crossproduct;
int i;
xOld = points.get(npoints - 1).getBlockX();
zOld = points.get(npoints - 1).getBlockZ();
for (i = 0; i < npoints; i++) {
xNew = points.get(i).getBlockX();
zNew = points.get(i).getBlockZ();
//Check for corner
if (xNew == targetX && zNew == targetZ) {
return true;
}
if (xNew > xOld) {
x1 = xOld;
x2 = xNew;
z1 = zOld;
z2 = zNew;
} else {
x1 = xNew;
x2 = xOld;
z1 = zNew;
z2 = zOld;
}
if (x1 <= targetX && targetX <= x2) {
crossproduct = ((long) targetZ - (long) z1) * (long) (x2 - x1)
- ((long) z2 - (long) z1) * (long) (targetX - x1);
if (crossproduct == 0) {
if ((z1 <= targetZ) == (targetZ <= z2)) return true; // on edge
} else if (crossproduct < 0 && (x1 != targetX)) {
inside = !inside;
}
}
xOld = xNew;
zOld = zNew;
}
return inside;
};
} else {
throw new UnsupportedOperationException("Unknown region type: " + worldguardRegion.getClass().getCanonicalName());
}
}
public boolean isRunning() {
return running;
}
public CompletableFuture<Boolean> executeTeleport(final Player player, boolean toSign, boolean checkRestrictions) {
if (running) {
throw new IllegalStateException("Task is already running!");
}
running = true;
// Check basics
if (feature == null) {
region.message(player, "general-noRegion");
return CompletableFuture.completedFuture(false);
}
if (region.getWorld() == null) {
region.message(player, "general-noWorld");
return CompletableFuture.completedFuture(false);
}
if (checkRestrictions) {
// Check correct world
if (!region.getBooleanSetting("general.teleportCrossWorld") && !player.getWorld().equals(region.getWorld())) {
region.message(player, "teleport-wrongWorld", player.getWorld().getName());
return CompletableFuture.completedFuture(false);
}
boolean owner = player.getUniqueId().equals(region.getOwner());
boolean friend = region.getFriendsFeature().getFriends().contains(player.getUniqueId());
boolean available = region.isAvailable();
// Teleport to sign instead if they dont have permission for teleporting to region
if ((!toSign && owner && !player.hasPermission("areashop.teleport") && player.hasPermission("areashop.teleportsign")
|| !toSign && !owner && !friend && !player.hasPermission("areashop.teleportall") && player.hasPermission("areashop.teleportsignall")
|| !toSign && !owner && friend && !player.hasPermission("areashop.teleportfriend") && player.hasPermission("areashop.teleportfriendsign")
|| !toSign && !owner && !friend && available && !player.hasPermission("areashop.teleportavailable") && player.hasPermission("areashop.teleportavailablesign"))) {
region.message(player, "teleport-changedToSign");
toSign = true;
}
// Check permissions
if (owner && !available && !player.hasPermission("areashop.teleport") && !toSign) {
region.message(player, "teleport-noPermission");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportall") && !toSign && !friend) {
region.message(player, "teleport-noPermissionOther");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportfriend") && !toSign && friend) {
region.message(player, "teleport-noPermissionFriend");
return CompletableFuture.completedFuture(false);
} else if (available && !player.hasPermission("areashop.teleportavailable") && !toSign) {
region.message(player, "teleport-noPermissionAvailable");
return CompletableFuture.completedFuture(false);
} else if (owner && !available && !player.hasPermission("areashop.teleportsign") && toSign) {
region.message(player, "teleport-noPermissionSign");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportsignall") && toSign && !friend) {
region.message(player, "teleport-noPermissionOtherSign");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportfriendsign") && toSign && friend) {
region.message(player, "teleport-noPermissionFriendSign");
return CompletableFuture.completedFuture(false);
} else if (available && !player.hasPermission("areashop.teleportavailablesign") && toSign) {
region.message(player, "teleport-noPermissionAvailableSign");
return CompletableFuture.completedFuture(false);
}
}
// Get the starting location
Value<Boolean> toSignRef = new Value<>(toSign);
Location startLocation = feature.getStartLocation(player, toSignRef);
toSign = toSignRef.get();
boolean insideRegion;
if (toSign) {
insideRegion = region.getBooleanSetting("general.teleportToSignIntoRegion");
} else {
insideRegion = region.getBooleanSetting("general.teleportIntoRegion");
}
// Check locations starting from startLocation and then a cube that increases
// radius around that (until no block in the region is found at all cube sides)
ProtectedRegion worldguardRegion = region.getRegion();
final Function<double[], Boolean> regionContains = generateContainsFunction(worldguardRegion);
boolean blocksInRegion = worldguardRegion.contains(startLocation.getBlockX(), startLocation.getBlockY(), startLocation.getBlockZ());
if (!blocksInRegion && insideRegion) {
region.message(player, "teleport-blocked");
return CompletableFuture.completedFuture(false);
}
// Tracking of which sides to continue the search
final World world = region.getWorld();
BlockVector3 min = worldguardRegion.getMinimumPoint();
BlockVector3 max = worldguardRegion.getMaximumPoint();
final Collection<Chunk> collection = ConcurrentHashMap.newKeySet();
final List<CompletableFuture<Object>> futures = new ArrayList<>();
final Collection<int[]> coords = new HashSet<>();
for (int x = min.getX(); x < max.getX(); x += 16) {
for (int z = min.getZ(); z < max.getZ(); z += 16) {
coords.add(new int[]{x, z});
}
}
final CompletableFuture<?> schedulingFuture;
if (coords.size() < 128) {
schedulingFuture = Utils.runAsBatches(coords, 32,
coord -> futures.add(PaperLib.getChunkAtAsync(world, coord[0], coord[1]).thenApply(chunk -> {
chunk.setForceLoaded(true);
collection.add(chunk);
return chunk;
})), false);
} else {
schedulingFuture = null;
}
final CompletableFuture<Boolean> future = new CompletableFuture<>();
final boolean finalToSign = toSign;
final Runnable task = () -> {
blocksInRegionCopy = blocksInRegion;
int safeY = startLocation.getBlockY();
int safeX = startLocation.getBlockX();
int safeZ = startLocation.getBlockZ();
final Location temp = new Location(world, 0, 0, 0);
// Tries limit tracking
int maxTries = plugin.getConfig().getInt("maximumTries");
// Tracking of which sides to continue the search
try {
this.done = Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(startLocation)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
Utils.newChain().async(() -> {
while ((blocksInRegionCopy || !insideRegion) && !done) {
blocksInRegionCopy = false;
// North side
continueThisDirection = false;
temp.setX(-radius + 1);
temp.setY(-radius + 1);
temp.setZ(-radius);
for (int x = -radius + 1; x <= radius && !done && !northDone; x++) {
for (int y = -radius + 1; y < radius && !done; y++) {
if (y + safeY > 256 || y + safeY < 0) {
continue;
}
if (!insideRegion || regionContains.apply(new double[]{x + safeX, y + safeY, safeZ - radius})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(0, 1, 0);
}
temp.add(1, 0, 0);
}
northDone = northDone || !continueThisDirection;
}
}).abortIf((var0) -> done).async(() -> {
// South side
continueThisDirection = false;
temp.setX(radius - 1);
temp.setY(-radius + 1);
temp.setZ(-radius);
for (int x = radius - 1; x >= -radius && !done && !southDone; x--) {
for (int y = -radius + 1; y < radius && !done; y++) {
if (y + safeY > 256 || y + safeY < 0) {
continue;
}
if (!insideRegion || regionContains.apply(new double[]{safeX + x, safeY + y, safeZ - radius})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(0, 1, 0);
}
temp.add(-1, 0, 0);
}
southDone = southDone || !continueThisDirection;
}).abortIf((var0) -> done).async(() -> {
// East side
continueThisDirection = false;
temp.setX(-radius + 1);
temp.setY(-radius + 1);
temp.setZ(0);
for (int z = -radius + 1; z <= radius && !done && !eastDone; z++) {
for (int y = -radius + 1; y < radius && !done; y++) {
if (y + safeY > 256 || y + safeY < 0) {
continue;
}
if (!insideRegion || regionContains.apply(new double[]{safeX + radius, y + safeY, z + safeZ})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(0, 1, 0);
}
temp.add(0, 0, 1);
}
eastDone = eastDone || !continueThisDirection;
}).abortIf((var0) -> done).async(() -> {
// West side
continueThisDirection = false;
temp.setX(-radius);
temp.setY(-radius);
temp.setZ(radius - 1);
for (int z = radius - 1; z >= -radius && !done && !westDone; z--) {
for (int y = -radius + 1; y < radius && !done; y++) {
if (y + safeY > 256 || y + safeY < 0) {
continue;
}
if (!insideRegion || regionContains.apply(new double[]{safeX - radius, safeY + y, safeZ + z})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(0, 1, 0);
}
temp.add(0, 0, -1);
}
westDone = westDone || !continueThisDirection;
}).abortIf((var0) -> done).async(() -> {
// Top side
continueThisDirection = false;
// Middle block of the top
if ((safeY + radius) > 256) {
topDone = true;
}
if (!done && !topDone) {
temp.setX(0);
temp.setY(radius);
temp.setZ(0);
if (!insideRegion || regionContains.apply(new double[]{safeX, safeY + radius, safeZ})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
temp.setY(radius);
for (int r = 1; r <= radius && !done && !topDone; r++) {
temp.setX(-r + 1);
temp.setZ(-r);
for (int x = -r + 1; x <= r && !done; x++) {
if (!insideRegion || regionContains.apply(new double[]{safeX + x, safeY + radius, safeZ - r})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(1, 0, 0);
}
// East
temp.setX(r);
temp.setZ(-r + 1);
for (int z = -r + 1; z <= r && !done; z++) {
if (!insideRegion || regionContains.apply(new double[]{safeX + r, safeY + radius, safeZ + z})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(0, 0, z);
}
// South side
temp.setX(r - 1);
temp.setZ(r);
for (int x = r - 1; x >= -r && !done; x--) {
if (!insideRegion || regionContains.apply(new double[]{safeX + x, safeY + radius, safeZ + r})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(-1, 0, 0);
}
// West side
temp.setX(-r);
temp.setZ(r - 1);
for (int z = r - 1; z >= -r && !done; z--) {
if (!insideRegion || regionContains.apply(new double[]{safeX - r, safeY + radius, safeZ + z})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(0, 0, -1);
}
}
topDone = topDone || !continueThisDirection;
}).abortIf(var0 -> done).async(() -> {
// Bottom side
continueThisDirection = false;
// Middle block of the bottom
if (safeY - radius < 0) {
bottomDone = true;
}
if (!done && !bottomDone) {
temp.setX(0);
temp.setY(-radius);
temp.setZ(0);
if (!insideRegion || regionContains.apply(new double[]{safeX, safeY - radius, safeZ})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
temp.setY(-radius);
for (int r = 1; r <= radius && !done && !bottomDone; r++) {
// North
temp.setX(-r + 1);
temp.setZ(-r);
for (int x = -r + 1; x <= r && !done; x++) {
if (!insideRegion || regionContains.apply(new double[]{safeX + x, safeY - radius, safeZ - r})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(1, 0, 0);
}
// East
temp.setX(0);
temp.setZ(-r + 1);
for (int z = -r + 1; z <= r && !done; z++) {
if (!insideRegion || regionContains.apply(new double[]{safeX + r, safeY - radius, safeZ + z})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(0, 0, 1);
}
// South side
temp.setX(r - 1);
temp.setZ(r);
for (int x = r - 1; x >= -r && !done; x--) {
if (!insideRegion || regionContains.apply(new double[]{safeX + x, safeY - radius, safeZ + r})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(-1, 0, 0);
}
// West side
temp.setX(-r);
temp.setZ(r - 1);
for (int z = r - 1; z >= -r && !done; z--) {
if (!insideRegion || regionContains.apply(new double[]{safeX - r, safeY - radius, safeZ + z})) {
checked++;
try {
this.done = checked > maxTries || Bukkit.getScheduler().callSyncMethod(plugin, () -> TeleportFeature.isSafe(temp)).get(5, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException ex) {
ex.printStackTrace();
this.done = false;
}
blocksInRegionCopy = true;
continueThisDirection = true;
}
temp.add(0, 0, -1);
}
}
bottomDone = bottomDone || !continueThisDirection;
// Increase cube radius
radius++;
startLocation.add(temp);
}).execute(() -> Bukkit.getScheduler().runTask(plugin, () -> {
if (done && TeleportFeature.isSafe(startLocation)) {
if (finalToSign) {
region.message(player, "teleport-successSign");
// Let the player look at the sign
Vector playerVector = startLocation.toVector();
playerVector.setY(playerVector.getY() + player.getEyeHeight(true));
Vector signVector = region.getSignsFeature().getSigns().get(0).getLocation().toVector().add(new Vector(0.5, 0.5, 0.5));
Vector direction = playerVector.clone().subtract(signVector).normalize();
startLocation.setYaw(180 - (float) Math.toDegrees(Math.atan2(direction.getX(), direction.getZ())));
startLocation.setPitch(90 - (float) Math.toDegrees(Math.acos(direction.getY())));
} else {
region.message(player, "teleport-success");
}
AreaShop.debug("Found location: " + startLocation.toString() + " Tries: " + checked);
PaperLib.teleportAsync(player, startLocation).thenAccept(future::complete);
} else {
region.message(player, "teleport-noSafe", checked, maxTries);
AreaShop.debug("No location found, checked " + checked + " spots of max " + maxTries);
future.complete(false);
}
}));
};
// Start notifying the player, the one issue is that checked/max is 0 is if chunk loading takes up >= ~3s
doNotifications(player.getUniqueId());
TaskChain<?> chain = Utils.newChain();
if (schedulingFuture != null) {
// If there aren't too many chunks, pre-load them. Max will take 10 ticks ~ 500-600ms
chain = chain.asyncFirstFuture(() -> schedulingFuture).asyncFutures((unused) -> futures);
}
// Run the calculation task async
chain.async(task::run).execute();
// Don't keep chunks force-loaded once the teleportation has been completed.
return future.thenApply((unused0) -> {
for (Chunk chunk : collection) {
chunk.setForceLoaded(false); // Should be fine to call async(?)
}
running = false;
return unused0;
});
}
/**
* Get the task which notifies player of teleport progress.
*
* @return Returns the task, may be null.
*/
public BukkitTask getNotifierTask() {
return notifierTask;
}
private void doNotifications(final UUID player0) {
final int maxTries = plugin.getConfig().getInt("maximumTries");
this.notifierTask = new BukkitRunnable() {
@Override
public void run() {
final Player player = Bukkit.getPlayer(player0);
if (TeleportJob.this.done || player == null) {
this.cancel();
TeleportJob.this.notifierTask = null;
return;
}
region.message(player, "teleport-calculation-progress", "%done%", String.valueOf(checked), "%max%", String.valueOf(maxTries));
}
}.runTaskTimer(plugin, Utils.millisToTicks(3000), Utils.millisToTicks(3000));
}
}
@@ -39,19 +39,15 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.*;
public class CommandManager extends Manager implements CommandExecutor, TabCompleter {
private final ArrayList<CommandAreaShop> commands;
private final List<CommandAreaShop> commands = new ArrayList<>();
/**
* Constructor.
*/
public CommandManager() {
commands = new ArrayList<>();
commands.add(new HelpCommand());
commands.add(new RentCommand());
commands.add(new UnrentCommand());
@@ -96,7 +92,8 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
* @return The list with AreaShop commands
*/
public List<CommandAreaShop> getCommands() {
return commands;
// Return a cloned array
return new ArrayList<>(commands);
}
/**
@@ -109,7 +106,7 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
return;
}
// Add all messages to a list
ArrayList<String> messages = new ArrayList<>();
List<String> messages = new ArrayList<>(commands.size());
plugin.message(target, "help-header");
plugin.message(target, "help-alias");
for(CommandAreaShop command : commands) {
@@ -132,18 +129,21 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
}
// Redirect `/as info player <player>` to `/as me <player>`
if(args.length == 3 && "info".equals(args[0]) && "player".equals(args[1])) {
if(args.length == 3 && "info".equalsIgnoreCase(args[0]) && "player".equalsIgnoreCase(args[1])) {
args[0] = "me";
args[1] = args[2];
}
// Execute command
boolean executed = false;
for(int i = 0; i < commands.size() && !executed; i++) {
if(commands.get(i).canExecute(command, args)) {
commands.get(i).execute(sender, args);
for (final CommandAreaShop cmd : commands) {
if (cmd.canExecute(command, args)) {
cmd.execute(sender, args);
executed = true;
// Can't have two commands with same label
break;
}
}
// Show help
@@ -165,10 +165,10 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
List<String> result = new ArrayList<>();
if(!sender.hasPermission("areashop.tabcomplete")) {
return result;
return Collections.emptyList();
}
List<String> result = new ArrayList<>();
int toCompleteNumber = args.length;
String toCompletePrefix = args[args.length - 1].toLowerCase();
//AreaShop.debug("toCompleteNumber=" + toCompleteNumber + ", toCompletePrefix=" + toCompletePrefix + ", length=" + toCompletePrefix.length());
@@ -184,6 +184,7 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
for(CommandAreaShop c : commands) {
if(c.canExecute(command, args)) {
result = c.getTabCompleteList(toCompleteNumber, start, sender);
break;
}
}
}
@@ -195,8 +196,7 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
set.add(suggestion);
}
}
result.clear();
result.addAll(set);
return new ArrayList<>(set);
}
//AreaShop.debug("Tabcomplete #" + toCompleteNumber + ", prefix="+ toCompletePrefix + ", result=" + result.toString());
return result;
@@ -5,7 +5,7 @@ import me.wiefferink.areashop.features.CommandsFeature;
import me.wiefferink.areashop.features.DebugFeature;
import me.wiefferink.areashop.features.FriendsFeature;
import me.wiefferink.areashop.features.RegionFeature;
import me.wiefferink.areashop.features.TeleportFeature;
import me.wiefferink.areashop.features.teleport.TeleportFeature;
import me.wiefferink.areashop.features.WorldGuardRegionFlagsFeature;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.regions.GeneralRegion;
@@ -57,7 +57,7 @@ public class FeatureManager extends Manager {
for(Class<? extends RegionFeature> clazz : featureClasses) {
try {
regionFeatureConstructors.put(clazz, clazz.getConstructor(GeneralRegion.class));
} catch(NoSuchMethodException | IllegalArgumentException e) {
} catch(NoSuchMethodException | IllegalArgumentException ignored) {
// The feature does not have a region specific part
}
}
@@ -71,7 +71,7 @@ public class FeatureManager extends Manager {
}
/**
* Instanciate a feature for a certain region.
* Instantiate a feature for a certain region.
* @param region The region to create a feature for
* @param featureClazz The class of the feature to create
* @return The feature class
@@ -30,16 +30,15 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class FileManager extends Manager {
private final Set<String> worldRegionsRequireSaving;
private final Map<String, BuyRegion> buyRegions = new HashMap<>();
private final Map<String, RentRegion> rentRegions = new HashMap<>();
private final Map<String, GeneralRegion> allRegions = new HashMap<>();
private final Map<String, BuyRegion> buyRegions;
private final Map<String, RentRegion> rentRegions;
private final String regionsPath;
private final HashMap<String, RegionGroup> groups;
private final Map<String, RegionGroup> groups;
private final String configPath;
private YamlConfiguration config = null;
private final String groupsPath;
@@ -48,7 +47,7 @@ public class FileManager extends Manager {
private YamlConfiguration defaultConfig = null;
private YamlConfiguration defaultConfigFallback = null;
private boolean saveGroupsRequired = false;
private HashMap<String, Integer> versions = null;
private Map<String, Integer> versions = null;
private final String versionPath;
private final String schemFolder;
@@ -56,6 +55,8 @@ public class FileManager extends Manager {
* Constructor, initialize variabeles.
*/
public FileManager() {
buyRegions = new HashMap<>();
rentRegions = new HashMap<>();
regionsPath = plugin.getDataFolder() + File.separator + AreaShop.regionsFolder;
configPath = plugin.getDataFolder() + File.separator + "config.yml";
groups = new HashMap<>();
@@ -151,7 +152,8 @@ public class FileManager extends Manager {
* @return The region if found, otherwise null
*/
public GeneralRegion getRegion(String name) {
return allRegions.get(name.toLowerCase());
GeneralRegion region = buyRegions.get(name.toLowerCase());
return region == null ? rentRegions.get(name.toLowerCase()) : region;
}
/**
@@ -198,9 +200,10 @@ public class FileManager extends Manager {
* @return List of all regions (it is safe to modify the list)
*/
public List<GeneralRegion> getRegions() {
final List<GeneralRegion> list = new ArrayList<>(allRegions.size());
list.addAll(allRegions.values());
return list;
List<GeneralRegion> regions = new ArrayList<>(buyRegions.size() + rentRegions.size());
regions.addAll(buyRegions.values());
regions.addAll(rentRegions.values());
return regions;
}
/**
@@ -209,11 +212,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getBuyNames() {
final List<String> list = new ArrayList<>(buyRegions.size());
for (GeneralRegion region : buyRegions.values()) {
list.add(region.getName());
}
return list;
return new ArrayList<>(buyRegions.keySet());
}
/**
@@ -222,11 +221,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getRentNames() {
final List<String> list = new ArrayList<>(rentRegions.size());
for (GeneralRegion region : rentRegions.values()) {
list.add(region.getName());
}
return list;
return new ArrayList<>(rentRegions.keySet());
}
/**
@@ -235,10 +230,8 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getRegionNames() {
final List<String> list = new ArrayList<>(allRegions.size());
for (GeneralRegion region : rentRegions.values()) {
list.add(region.getName());
}
final List<String> list = getRentNames();
list.addAll(getBuyNames());
return list;
}
@@ -248,11 +241,11 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getGroupNames() {
final List<String> names = new ArrayList<>(groups.size());
for (RegionGroup group : groups.values()) {
names.add(group.getName());
final List<String> list = new ArrayList<>(groups.size());
for (final RegionGroup group : groups.values()) {
list.add(group.getName());
}
return names;
return list;
}
/**
@@ -288,16 +281,11 @@ public class FileManager extends Manager {
if (event.isCancelled()) {
return event;
}
final String name = region.getName().toLowerCase();
// Forcefully remove regions from cache.
buyRegions.remove(name);
rentRegions.remove(name);
if (region instanceof BuyRegion) {
buyRegions.put(region.getName().toLowerCase(), (BuyRegion) region);
} else if (region instanceof RentRegion) {
rentRegions.put(region.getName().toLowerCase(), (RentRegion) region);
}
allRegions.put(region.getName().toLowerCase(), region);
Bukkit.getPluginManager().callEvent(new AddedRegionEvent(region));
return event;
}
@@ -411,9 +399,9 @@ public class FileManager extends Manager {
}
region.resetRegionFlags();
rentRegions.remove(region.getLowerCaseName());
buyRegions.remove(region.getLowerCaseName());
allRegions.remove(region.getLowerCaseName());
rentRegions.remove(region.getLowerCaseName());
// Remove file
File file = new File(plugin.getDataFolder() + File.separator + AreaShop.regionsFolder + File.separator + region.getLowerCaseName() + ".yml");
if (file.exists()) {
@@ -561,12 +549,12 @@ public class FileManager extends Manager {
this.saveWorldGuardRegions();
final AtomicInteger processed = new AtomicInteger();
for (GeneralRegion region : allRegions.values()) {
for (GeneralRegion region : getRegions()) {
if (region.isSaveRequired()) {
region.saveNowAsync().thenAccept((unused) -> processed.getAndIncrement());
}
}
final int finalCount = allRegions.size();
final int finalCount = getRegions().size();
final CompletableFuture<Boolean> future = new CompletableFuture<>();
new BukkitRunnable() {
@Override
@@ -588,7 +576,7 @@ public class FileManager extends Manager {
if (isSaveGroupsRequired()) {
saveGroupsNow();
}
for (GeneralRegion region : allRegions.values()) {
for (GeneralRegion region : getRegions()) {
if (region.isSaveRequired()) {
region.saveNow();
}
@@ -841,6 +829,7 @@ public class FileManager extends Manager {
* @return true if succeeded, otherwise false
*/
public boolean loadGroupsFile() {
boolean result = true;
File groupFile = new File(groupsPath);
if (groupFile.exists() && groupFile.isFile()) {
try (
@@ -858,16 +847,15 @@ public class FileManager extends Manager {
RegionGroup group = new RegionGroup(plugin, groupName);
groups.put(groupName, group);
}
return true;
return result;
}
/**
* Load all region files.
*/
public void loadRegionFiles() {
rentRegions.clear();
buyRegions.clear();
allRegions.clear();
rentRegions.clear();
final File file = new File(regionsPath);
if (!file.exists()) {
if (!file.mkdirs()) {
@@ -962,7 +950,7 @@ public class FileManager extends Manager {
boolean noWorldRegions = !noWorld.isEmpty();
while (!noWorld.isEmpty()) {
List<GeneralRegion> toDisplay = new ArrayList<>(noWorld.size());
List<GeneralRegion> toDisplay = new ArrayList<>();
String missingWorld = noWorld.get(0).getWorldName();
toDisplay.add(noWorld.get(0));
for (int i = 1; i < noWorld.size(); i++) {
@@ -982,7 +970,7 @@ public class FileManager extends Manager {
}
if (!incorrectDuration.isEmpty()) {
List<String> incorrectDurationNames = new ArrayList<>(incorrectDuration.size());
List<String> incorrectDurationNames = new ArrayList<>();
for (GeneralRegion region : incorrectDuration) {
incorrectDurationNames.add(region.getName());
}
@@ -1290,7 +1278,7 @@ public class FileManager extends Manager {
// Add 'general.lastActive' to rented/bought regions (initialize at current time)
if (fileStatus == null || fileStatus < 3) {
for (GeneralRegion region : allRegions.values()) {
for (GeneralRegion region : getRegions()) {
region.updateLastActiveTime();
}
// Update versions file to 3
@@ -0,0 +1,79 @@
package me.wiefferink.areashop.managers;
import me.wiefferink.areashop.AreaShop;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicBoolean;
public class SignErrorLogger {
public static final String PREFIX_WARN = "[Warning] [Sign Feature] ";
public static final DateFormat FORMAT = new SimpleDateFormat("MM/dd/yy HH:mm:ss");
private final List<String> cache = new LinkedList<>();
private final AtomicBoolean needsSaving = new AtomicBoolean(false);
private final File file;
public SignErrorLogger(File file) {
this.file = Objects.requireNonNull(file);
}
public void clearCache() {
synchronized (this.cache) {
this.cache.clear();
}
}
public SignErrorLogger submitWarning(String message) {
final String actual = FORMAT.format(Date.from(Instant.now())) + PREFIX_WARN + message;
synchronized (this.cache) {
this.cache.add(actual);
}
return this;
}
public void queueSave() {
this.needsSaving.set(true);
}
public void saveIfDirty() {
if (this.needsSaving.get()) {
save();
}
}
public synchronized void save() {
AreaShop.debugTask("[Sign Feature] Dumping sign errors to disk...");
final String[] copy;
synchronized (this.cache) {
copy = cache.toArray(new String[0]);
}
final StringJoiner joiner = new StringJoiner(System.lineSeparator());
for (String s : copy) {
joiner.add(s);
}
try (OutputStream os = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
writer.write(joiner.toString());
} catch (IOException ex) {
ex.printStackTrace();
AreaShop.error("Failed to update the sign error log!");
} finally {
AreaShop.debugTask("[Sign Feature] Error dump complete.");
}
}
}
@@ -42,7 +42,7 @@ public class SignLinkerManager extends Manager implements Listener {
@Override
public void shutdown() {
for(UUID uuid : signLinkers.keySet()) {
exitSignLinkMode(Bukkit.getPlayer(uuid));
exitSignLinkMode(uuid);
}
}
@@ -74,6 +74,18 @@ public class SignLinkerManager extends Manager implements Listener {
plugin.message(player, "linksigns-stopped");
}
private void exitSignLinkMode(UUID player) {
signLinkers.remove(player);
if(eventsRegistered && signLinkers.isEmpty()) {
eventsRegistered = false;
HandlerList.unregisterAll(this);
}
final Player online = Bukkit.getPlayer(player);
if (online != null) {
plugin.message(player, "linksigns-stopped");
}
}
/**
* Check if the player is in sign linking mode.
* @param player The player to check
@@ -551,10 +551,11 @@ public class BuyRegion extends GeneralRegion {
return false;
}
long lastPlayed = getLastActiveTime();
long now = System.currentTimeMillis();
//AreaShop.debug("currentTime=" + Calendar.getInstance().getTimeInMillis() + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (Calendar.getInstance().getTimeInMillis()-player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
if(Calendar.getInstance().getTimeInMillis() > (lastPlayed + inactiveSetting)) {
if(now > (lastPlayed + inactiveSetting)) {
AreaShop.info("Region " + getName() + " unrented because of inactivity for player " + getPlayerName());
AreaShop.debug("currentTime=" + Calendar.getInstance().getTimeInMillis() + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (Calendar.getInstance().getTimeInMillis() - player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
AreaShop.debug("currentTime=" + now + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (now - player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
return this.sell(true, null);
}
return false;
@@ -8,7 +8,7 @@ import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.events.notify.UpdateRegionEvent;
import me.wiefferink.areashop.features.FriendsFeature;
import me.wiefferink.areashop.features.RegionFeature;
import me.wiefferink.areashop.features.TeleportFeature;
import me.wiefferink.areashop.features.teleport.TeleportFeature;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.interfaces.GeneralRegionInterface;
import me.wiefferink.areashop.managers.FileManager;
@@ -189,12 +189,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
* @return The feature (either just instantiated or cached)
*/
public <T extends RegionFeature> T getFeature(Class<T> clazz) {
RegionFeature result = features.get(clazz);
if(result == null) {
result = plugin.getFeatureManager().getRegionFeature(this, clazz);
features.put(clazz, result);
}
return clazz.cast(result);
return clazz.cast(features.computeIfAbsent(clazz, (unused) -> plugin.getFeatureManager().getRegionFeature(this, clazz)));
}
/**
@@ -450,7 +445,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
if(landlordName != null && !landlordName.isEmpty()) {
@SuppressWarnings("deprecation")
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(landlordName);
if(offlinePlayer != null) {
if(offlinePlayer.hasPlayedBefore()) {
return offlinePlayer.getUniqueId();
}
}
@@ -463,7 +458,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*/
public String getLandlordName() {
String result = Utils.toName(getLandlord());
if(result == null || result.isEmpty()) {
if(result.isEmpty()) {
result = config.getString("general.landlordName");
if(result == null || result.isEmpty()) {
result = null;
@@ -482,7 +477,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
setSetting("general.landlord", landlord.toString());
}
String properName = Utils.toName(landlord);
if(properName == null) {
if(properName.isEmpty()) {
properName = name;
}
setSetting("general.landlordName", properName);
@@ -592,8 +587,11 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*/
public List<String> getGroupNames() {
List<String> result = new ArrayList<>();
for(RegionGroup group : getGroups()) {
result.add(group.getName());
// Inline #getGroups
for(RegionGroup group : plugin.getFileManager().getGroups()) {
if(group.isMember(this)) {
result.add(group.getName());
}
}
return result;
}
@@ -781,6 +779,36 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return true;
}
/**
* Save all blocks in a region for restoring later.
* @param fileName The name of the file to save to (extension and folder will be added)
* @return true if the region has been saved properly, otherwise false
*/
public CompletableFuture<Boolean> saveRegionBlocksAsync(String fileName) {
// Check if the region is correct
ProtectedRegion region = getRegion();
if(region == null) {
AreaShop.debug("Region '" + getName() + "' does not exist in WorldGuard, save failed");
return CompletableFuture.completedFuture(false);
}
// The path to save the schematic
File saveFile = new File(plugin.getFileManager().getSchematicFolder() + File.separator + fileName);
// Create parent directories
File parent = saveFile.getParentFile();
if(parent != null && !parent.exists()) {
if(!parent.mkdirs()) {
AreaShop.warn("Did not save region " + getName() + ", schematic directory could not be created: " + saveFile.getAbsolutePath());
return CompletableFuture.completedFuture(false);
}
}
return plugin.getWorldEditHandler().saveRegionBlocksAsync(saveFile, this).thenApply(result -> {
if(result) {
AreaShop.debug("Saved schematic for region " + getName());
}
return result;
});
}
/**
* Restore all blocks in a region for restoring later.
* @param fileName The name of the file to save to (extension and folder will be added)
@@ -803,6 +831,29 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return result;
}
/**
* Restore all blocks in a region for restoring later.
* @param fileName The name of the file to save to (extension and folder will be added)
* @return true if the region has been restored properly, otherwise false
*/
public CompletableFuture<Boolean> restoreRegionBlocksAsync(String fileName) {
if(getRegion() == null) {
AreaShop.debug("Region '" + getName() + "' does not exist in WorldGuard, restore failed");
return CompletableFuture.completedFuture(false);
}
// The path to save the schematic
File restoreFile = new File(plugin.getFileManager().getSchematicFolder() + File.separator + fileName);
return plugin.getWorldEditHandler().restoreRegionBlocksAsync(restoreFile, this).thenApply(result -> {
if(result) {
AreaShop.debug("Restored schematic for region " + getName());
// Workaround for signs inside the region in combination with async restore of plugins like AsyncWorldEdit and FastAsyncWorldEdit
Bukkit.getScheduler().runTaskLater(plugin, getSignsFeature()::update, 10);
}
return result;
});
}
/**
* Reset all flags of the region.
*/
@@ -1318,7 +1369,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
/**
* Class to store the result of a limits check.
*/
public class LimitResult {
public static class LimitResult {
private final boolean actionAllowed;
private final LimitType limitingFactor;
private final int maximum;
@@ -1433,8 +1484,8 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
public boolean matchesLimitGroup(String group) {
List<String> worlds = plugin.getConfig().getStringList("limitGroups." + group + ".worlds");
List<String> groups = plugin.getConfig().getStringList("limitGroups." + group + ".groups");
if((worlds == null || worlds.isEmpty() || worlds.contains(getWorldName()))) {
if(groups == null || groups.isEmpty()) {
if(worlds.isEmpty() || worlds.contains(getWorldName())) {
if(groups.isEmpty()) {
return true;
} else {
boolean inGroups = false;
@@ -1468,12 +1519,12 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
// Save the region if needed
if(save != null && !save.isEmpty()) {
save = Message.fromString(save).replacements(this).getSingle();
saveRegionBlocks(save);
saveRegionBlocksAsync(save);
}
// Restore the region if needed
if(restore != null && !restore.isEmpty()) {
restore = Message.fromString(restore).replacements(this).getSingle();
restoreRegionBlocks(restore);
restoreRegionBlocksAsync(restore);
}
}
@@ -721,10 +721,11 @@ public class RentRegion extends GeneralRegion {
return false;
}
long lastPlayed = getLastActiveTime();
final long now = System.currentTimeMillis();
//AreaShop.debug("currentTime=" + Calendar.getInstance().getTimeInMillis() + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (Calendar.getInstance().getTimeInMillis()-player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
if(Calendar.getInstance().getTimeInMillis() > (lastPlayed + inactiveSetting)) {
if(now > (lastPlayed + inactiveSetting)) {
AreaShop.info("Region " + getName() + " unrented because of inactivity for player " + getPlayerName());
AreaShop.debug("currentTime=" + Calendar.getInstance().getTimeInMillis() + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (Calendar.getInstance().getTimeInMillis() - player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
AreaShop.debug("currentTime=" + now + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (now - player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
return this.unRent(true, null);
}
return false;
@@ -734,7 +734,6 @@ public class Utils {
* @param input The input to check
* @return true if the input is numeric, otherwise false
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
public static boolean isNumeric(String input) {
try {
Integer.parseInt(input);
@@ -750,7 +749,6 @@ public class Utils {
* @param input The input
* @return true if the input is a double, otherwise false
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
public static boolean isDouble(String input) {
try {
Double.parseDouble(input);
@@ -828,15 +826,13 @@ public class Utils {
* @return the name of the player
*/
public static String toName(UUID uuid) {
if (uuid == null) {
return "";
} else {
if (uuid != null) {
String name = Bukkit.getOfflinePlayer(uuid).getName();
if (name != null) {
return name;
}
return "";
}
return "";
}
/**
@@ -865,20 +861,23 @@ public class Utils {
public static <E> CompletableFuture<?> runAsBatches(final Collection<E> objects, final int batchSize, final Consumer<E> task, final boolean async) {
if (batchSize < 1) {
throw new IllegalArgumentException("Invalid batch size!");
throw new IllegalArgumentException("Invalid batch size: " + batchSize);
}
final Queue<E> cloned = new LinkedList<>(objects);
final List<E> cloned = new ArrayList<>(objects);
final CompletableFuture<Object> future = new CompletableFuture<>();
BukkitRunnable runnable = new BukkitRunnable() {
int lastIndex;
@Override
public void run() {
if (cloned.isEmpty()) {
future.complete(true);
if (lastIndex >= cloned.size() - 1) {
cancel();
future.complete(true);
return;
}
for (int i = 0; i < batchSize && !cloned.isEmpty(); i++) {
task.accept(cloned.remove());
int index = Math.min(lastIndex + batchSize, cloned.size() - 1);
for (; lastIndex < index; lastIndex++) {
task.accept(cloned.get(lastIndex));
}
}
};
+1
View File
@@ -343,6 +343,7 @@ destroy-successRent: "%lang:region% successfully deregistered as rental region."
destroy-successBuy: "%lang:region% successfully deregistered as buy region."
teleport-help: "/as tp <region> [sign]."
teleport-calculation-progress: "Teleport in progress, finding safe location %done%/%max% scans complete."
teleport-noRentOrBuy: "The specified region is not registered: %0%."
teleport-noPermission: "You don't have permission to teleport to your region."
teleport-noPermissionOther: "You don't have permission to teleport to regions you do not own."
+2 -2
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>areashop-fawe-1-15</artifactId>
<packaging>jar</packaging>
<name>AreaShop FastAsyncWorldEdit 1.15+</name>
<name>AreaShop FastAsyncWorldEdit 1.14+</name>
<version>latest</version>
<parent>
@@ -24,7 +24,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.15.2-R0.1-SNAPSHOT</version>
<version>1.14.4-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
<optional>true</optional>
@@ -3,13 +3,14 @@ package me.wiefferink.areashop.handlers.fawe.v1_15;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RelightMode;
import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard;
import com.google.common.annotations.Beta;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.*;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
@@ -22,6 +23,7 @@ import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.io.Closer;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import com.sk89q.worldguard.protection.regions.RegionType;
import me.wiefferink.areashop.interfaces.AreaShopInterface;
@@ -30,8 +32,10 @@ import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.entity.Player;
import org.bukkit.util.BoundingBox;
import java.io.*;
import java.util.concurrent.CompletableFuture;
import java.util.zip.GZIPInputStream;
// Future: this class could use the schematic save/paste api of FAWE: https://github.com/boy0001/FastAsyncWorldedit/wiki/Pasting-a-schematic
@@ -42,28 +46,38 @@ public class FastAsyncWorldEditHandler extends WorldEditInterface {
super(pluginInterface);
}
private Mask newMask(final ProtectedRegion region) {
return new Mask() {
@Override
public boolean test(BlockVector3 vector) {
return region.contains(vector);
}
private static class RegionBoundMask implements Mask {
@Override
public Mask2D toMask2D() {
return null;
}
private final BoundingBox boundingBox;
@Override
public Mask copy() {
return this;
}
};
public RegionBoundMask(final ProtectedRegion region) {
final BlockVector3 min = region.getMinimumPoint(), max = region.getMaximumPoint();
this.boundingBox = new BoundingBox(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ());
}
public RegionBoundMask(final BoundingBox boundingBox) {
this.boundingBox = boundingBox.clone();
}
@Override
public boolean test(BlockVector3 vector) {
return boundingBox.contains(vector.getX(), vector.getY(), vector.getZ());
}
@Override
public Mask2D toMask2D() {
return null;
}
@Override
public Mask copy() {
return new RegionBoundMask(boundingBox);
}
}
@Override
public boolean restoreRegionBlocks(File rawFile, GeneralRegionInterface regionInterface) {
// TODO implement using the FastAsyncWorldEdit api to paste async
@Beta
public CompletableFuture<Boolean> restoreRegionBlocksAsync(File rawFile, GeneralRegionInterface regionInterface) {
File file = null;
ClipboardFormat format = null;
for (ClipboardFormat formatOption : BuiltInClipboardFormat.values()) {
@@ -77,11 +91,11 @@ public class FastAsyncWorldEditHandler extends WorldEditInterface {
}
if (file == null) {
pluginInterface.getLogger().info("Did not restore region " + regionInterface.getName()
+ ", schematic file does not exist: " + rawFile.getAbsolutePath());
return false;
+ ", schematic file does not exist: " + rawFile.getAbsolutePath());
return CompletableFuture.completedFuture(false);
}
pluginInterface.debugI("Trying to restore region", regionInterface.getName(), " from file",
file.getAbsolutePath(), "with format", format.getName());
file.getAbsolutePath(), "with format", format.getName());
com.sk89q.worldedit.world.World world = null;
if (regionInterface.getName() != null) {
@@ -89,90 +103,105 @@ public class FastAsyncWorldEditHandler extends WorldEditInterface {
}
if (world == null) {
pluginInterface.getLogger().info(
"Did not restore region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return false;
"Did not restore region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return CompletableFuture.completedFuture(false);
}
final CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
final FaweLimit limit = new FaweLimit();
limit.MAX_CHANGES = pluginInterface.getConfig().getInt("maximumBlocks");
EditSession editSession = FaweAPI.getEditSessionBuilder(world).limit(limit).build();
EditSession editSession = FaweAPI.getEditSessionBuilder(world).limit(limit).relightMode(RelightMode.OPTIMAL).build();
editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE);
ProtectedRegion region = regionInterface.getRegion();
final RegionType regionType = region.getType();
// Get the origin and size of the region
BlockVector3 origin = BlockVector3
final BlockVector3 origin = BlockVector3
.at(region.getMinimumPoint().getBlockX(), region.getMinimumPoint().getBlockY(),
region.getMinimumPoint().getBlockZ());
final RegionBoundMask mask = new RegionBoundMask(region);
final File finalFile = file;
final String regionName = regionInterface.getName();
final double regionWidth = regionInterface.getWidth(), regionHeight = regionInterface.getHeight(), regionDepth = regionInterface.getDepth();
final LocalSession session =
new LocalSession(pluginInterface.getWorldEdit().getLocalConfiguration());
FaweAPI.getTaskManager().async(() -> {
// Read the schematic and paste it into the world
try (Closer closer = Closer.create()) {
FileInputStream fis = closer.register(new FileInputStream(finalFile));
BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(bis));
ClipboardReader reader = new FastSchematicReader(nbtStream);
// Read the schematic and paste it into the world
try (Closer closer = Closer.create(); FileInputStream fis = closer.register(new FileInputStream(file));
BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(bis));
ClipboardReader reader = new FastSchematicReader(nbtStream)) {
LocalSession session =
new LocalSession(pluginInterface.getWorldEdit().getLocalConfiguration());
Clipboard clipboard = reader.read();
if (clipboard.getDimensions().getY() != regionInterface.getHeight()
|| clipboard.getDimensions().getX() != regionInterface.getWidth()
|| clipboard.getDimensions().getZ() != regionInterface.getDepth()) {
pluginInterface.getLogger().warning(
"Size of the region " + regionInterface.getName()
+ " is not the same as the schematic to restore!");
pluginInterface.debugI(
"schematic|region, x:" + clipboard.getDimensions().getX() + "|"
+ regionInterface.getWidth() + ", y:" + clipboard.getDimensions().getY()
+ "|" + regionInterface.getHeight() + ", z:" + clipboard.getDimensions()
.getZ() + "|" + regionInterface.getDepth());
}
clipboard.setOrigin(clipboard.getMinimumPoint());
ClipboardHolder clipboardHolder = new ClipboardHolder(clipboard);
session.setBlockChangeLimit(pluginInterface.getConfig().getInt("maximumBlocks"));
session.setClipboard(clipboardHolder);
// Build operation
BlockTransformExtent extent = new BlockTransformExtent(clipboard);
ForwardExtentCopy copy =
new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(),
editSession, origin);
copy.setTransform(clipboardHolder.getTransform());
// Mask to region (for polygon and other weird shaped regions)
// TODO make this more efficient (especially for polygon regions)
if (region.getType() != RegionType.CUBOID) {
copy.setSourceMask(newMask(region));
Clipboard clipboard = reader.read();
if (clipboard.getDimensions().getY() != regionHeight
|| clipboard.getDimensions().getX() != regionWidth
|| clipboard.getDimensions().getZ() != regionDepth) {
pluginInterface.getLogger().warning(
"Size of the region " + regionName
+ " is not the same as the schematic to restore!");
pluginInterface.debugI(
"schematic|region, x:" + clipboard.getDimensions().getX() + "|"
+ regionWidth + ", y:" + clipboard.getDimensions().getY()
+ "|" + regionHeight + ", z:" + clipboard.getDimensions()
.getZ() + "|" + regionDepth);
}
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
pluginInterface.getLogger().warning(
"exceeded the block limit while restoring schematic of " + regionInterface.getName()
+ ", limit in exception: " + e.getBlockLimit() + ", limit passed by AreaShop: "
+ pluginInterface.getConfig().getInt("maximumBlocks"));
return false;
} catch (IOException e) {
pluginInterface.getLogger().warning(
"An error occured while restoring schematic of " + regionInterface.getName()
+ ", enable debug to see the complete stacktrace");
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
return false;
} catch (Exception e) {
pluginInterface.getLogger()
.warning("crashed during restore of " + regionInterface.getName());
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
return false;
}
editSession.flushQueue();
return true;
clipboard.setOrigin(clipboard.getMinimumPoint());
ClipboardHolder clipboardHolder = new ClipboardHolder(clipboard);
session.setBlockChangeLimit(limit.MAX_CHANGES);
session.setClipboard(clipboardHolder);
// Build operation
BlockTransformExtent extent = new BlockTransformExtent(clipboard);
ForwardExtentCopy copy =
new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(),
editSession, origin);
copy.setTransform(clipboardHolder.getTransform());
// Mask to region (for polygon and other weird shaped regions)
// TODO make this more efficient (especially for polygon regions)
if (regionType != RegionType.CUBOID) {
copy.setSourceMask(mask);
}
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
pluginInterface.getLogger().warning(
"exceeded the block limit while restoring schematic of " + regionInterface.getName()
+ ", limit in exception: " + e.getBlockLimit() + ", limit passed by AreaShop: "
+ pluginInterface.getConfig().getInt("maximumBlocks"));
CompletableFuture.completedFuture(false);
} catch (IOException e) {
pluginInterface.getLogger().warning(
"An error occured while restoring schematic of " + regionInterface.getName()
+ ", enable debug to see the complete stacktrace");
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
CompletableFuture.completedFuture(false);
} catch (Exception e) {
pluginInterface.getLogger()
.warning("crashed during restore of " + regionInterface.getName());
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
CompletableFuture.completedFuture(false);
}
editSession.flushQueue();
CompletableFuture.completedFuture(true);
});
return completableFuture;
}
@Override public boolean saveRegionBlocks(File file, GeneralRegionInterface regionInterface) {
@Override
public boolean restoreRegionBlocks(File rawFile, GeneralRegionInterface regionInterface) {
return restoreRegionBlocksAsync(rawFile, regionInterface).join();
}
@Override
@Beta
public CompletableFuture<Boolean> saveRegionBlocksAsync(File file, GeneralRegionInterface regionInterface) {
// TODO implement using the FastAsyncWorldEdit api to save async
ClipboardFormat format = BuiltInClipboardFormat.MINECRAFT_STRUCTURE;
// TODO allow selecting FAWE format in the config? (when enabled you cannot go back to vanilla WorldEdit easily)
file = new File(file.getAbsolutePath() + "." + format.getPrimaryFileExtension());
pluginInterface.debugI("Trying to save region", regionInterface.getName(), " to file",
file.getAbsolutePath(), "with format", format.getName());
file.getAbsolutePath(), "with format", format.getName());
com.sk89q.worldedit.world.World world = null;
if (regionInterface.getWorld() != null) {
world = BukkitAdapter.adapt(regionInterface.getWorld());
@@ -181,60 +210,72 @@ public class FastAsyncWorldEditHandler extends WorldEditInterface {
pluginInterface.getLogger().warning(
"Did not save region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return false;
return CompletableFuture.completedFuture(false);
}
final FaweLimit limit = new FaweLimit();
limit.MAX_CHANGES = pluginInterface.getConfig().getInt("maximumBlocks");
EditSession editSession = FaweAPI.getEditSessionBuilder(world).limit(limit).relightMode(RelightMode.OPTIMAL).build();
EditSession editSession = FaweAPI.getEditSessionBuilder(world).relightMode(RelightMode.OPTIMAL).limit(limit).build();
// Create a clipboard
CuboidRegion selection =
new CuboidRegion(world, regionInterface.getRegion().getMinimumPoint(),
regionInterface.getRegion().getMaximumPoint());
BlockArrayClipboard clipboard = new BlockArrayClipboard(selection);
clipboard.setOrigin(regionInterface.getRegion().getMinimumPoint());
ForwardExtentCopy copy = new ForwardExtentCopy(editSession,
new CuboidRegion(world, regionInterface.getRegion().getMinimumPoint(),
regionInterface.getRegion().getMaximumPoint()), clipboard,
regionInterface.getRegion().getMinimumPoint());
try {
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
pluginInterface.getLogger().warning(
"Exceeded the block limit while saving schematic of " + regionInterface.getName()
+ ", limit in exception: " + e.getBlockLimit() + ", limit passed by AreaShop: "
+ pluginInterface.getConfig().getInt("maximumBlocks"));
return false;
}
final ProtectedRegion region = regionInterface.getRegion();
final BlockVector3 min = region.getMinimumPoint(), max = region.getMaximumPoint();
final String regionName = regionInterface.getName();
final World finalWorld = world;
final CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
final File finalFile = file;
FaweAPI.getTaskManager().async(() -> {
// Create a clipboard
CuboidRegion selection =
new CuboidRegion(finalWorld, min, max);
Clipboard clipboard = new MemoryOptimizedClipboard(selection);
clipboard.setOrigin(min);
ForwardExtentCopy copy = new ForwardExtentCopy(editSession,
selection.clone(), clipboard,
min);
try {
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
pluginInterface.getLogger().warning(
"Exceeded the block limit while saving schematic of " + regionName
+ ", limit in exception: " + e.getBlockLimit() + ", limit passed by AreaShop: "
+ pluginInterface.getConfig().getInt("maximumBlocks"));
completableFuture.complete(false);
}
try (Closer closer = Closer.create();
FileOutputStream fos = closer.register(new FileOutputStream(file));
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos))) {
ClipboardWriter writer = closer.register(format.getWriter(bos));
writer.write(clipboard);
} catch (IOException e) {
pluginInterface.getLogger().warning(
"An error occured while saving schematic of " + regionInterface.getName()
+ ", enable debug to see the complete stacktrace");
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
return false;
} catch (Exception e) {
pluginInterface.getLogger()
.warning("Crashed during save of " + regionInterface.getName());
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
return false;
}
return true;
try (Closer closer = Closer.create()) {
FileOutputStream fos = closer.register(new FileOutputStream(finalFile));
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
ClipboardWriter writer = closer.register(format.getWriter(bos));
writer.write(clipboard);
} catch (IOException e) {
pluginInterface.getLogger().warning(
"An error occured while saving schematic of " + regionName
+ ", enable debug to see the complete stacktrace");
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
completableFuture.complete(false);
} catch (Exception e) {
pluginInterface.getLogger()
.warning("Crashed during save of " + regionName);
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
completableFuture.complete(false);
}
completableFuture.complete(true);
});
return completableFuture;
}
@Override public WorldEditSelection getPlayerSelection(Player player) {
@Override
public boolean saveRegionBlocks(File file, GeneralRegionInterface regionInterface) {
return saveRegionBlocksAsync(file, regionInterface).join();
}
@Override
public WorldEditSelection getPlayerSelection(Player player) {
try {
Region region = pluginInterface.getWorldEdit().getSession(player)
.getSelection(BukkitAdapter.adapt(player.getWorld()));
.getSelection(BukkitAdapter.adapt(player.getWorld()));
return new WorldEditSelection(player.getWorld(),
BukkitAdapter.adapt(player.getWorld(), region.getMinimumPoint()),
BukkitAdapter.adapt(player.getWorld(), region.getMaximumPoint()));
BukkitAdapter.adapt(player.getWorld(), region.getMinimumPoint()),
BukkitAdapter.adapt(player.getWorld(), region.getMaximumPoint()));
} catch (IncompleteRegionException e) {
return null;
}
@@ -3,10 +3,11 @@ package me.wiefferink.areashop.interfaces;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import java.util.logging.Logger;
public interface AreaShopInterface {
public interface AreaShopInterface extends Plugin {
void debugI(Object... message);
YamlConfiguration getConfig();
@@ -3,6 +3,7 @@ package me.wiefferink.areashop.interfaces;
import org.bukkit.entity.Player;
import java.io.File;
import java.util.concurrent.CompletableFuture;
public abstract class WorldEditInterface {
protected final AreaShopInterface pluginInterface;
@@ -20,6 +21,10 @@ public abstract class WorldEditInterface {
*/
public abstract boolean restoreRegionBlocks(File file, GeneralRegionInterface regionInterface);
public CompletableFuture<Boolean> restoreRegionBlocksAsync(File file, GeneralRegionInterface regionInterface) {
return CompletableFuture.completedFuture(restoreRegionBlocks(file, regionInterface));
}
/**
* Different way to save blocks per implementation, newer ones support entities as well.
* Why: the schematic api has changed between WorldEdit 5 and 6, and the schematic format changed between 6 and 7
@@ -29,6 +34,10 @@ public abstract class WorldEditInterface {
*/
public abstract boolean saveRegionBlocks(File file, GeneralRegionInterface regionInterface);
public CompletableFuture<Boolean> saveRegionBlocksAsync(File file, GeneralRegionInterface regionInterface) {
return CompletableFuture.completedFuture(saveRegionBlocks(file, regionInterface));
}
/**
* Get the selection of the player.
* Why: the underlying WorldEdit selection class has changed from interface <-> class
@@ -46,14 +46,7 @@ public class WorldEditHandler extends WorldEditInterface {
@Override
public WorldEditSelection getPlayerSelection(Player player) {
try {
final LocalSession session = pluginInterface.getWorldEdit().getSession(player);
if (session == null) {
return null;
}
Region region = session.getSelection(BukkitAdapter.adapt(player.getWorld()));
if (region == null) {
return null;
}
Region region = pluginInterface.getWorldEdit().getSession(player).getSelection(BukkitAdapter.adapt(player.getWorld()));
return new WorldEditSelection(
player.getWorld(),
BukkitAdapter.adapt(player.getWorld(), region.getMinimumPoint()),
@@ -99,10 +92,10 @@ public class WorldEditHandler extends WorldEditInterface {
BlockVector3 origin = BlockVector3.at(region.getMinimumPoint().getBlockX(), region.getMinimumPoint().getBlockY(), region.getMinimumPoint().getBlockZ());
// Read the schematic and paste it into the world
try(Closer closer = Closer.create();
try(Closer closer = Closer.create()) {
FileInputStream fis = closer.register(new FileInputStream(file));
BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
ClipboardReader reader = format.getReader(bis)) {
ClipboardReader reader = format.getReader(bis);
//WorldData worldData = world.getWorldData();
LocalSession session = new LocalSession(pluginInterface.getWorldEdit().getLocalConfiguration());
@@ -195,9 +188,10 @@ public class WorldEditHandler extends WorldEditInterface {
return false;
}
try(Closer closer = Closer.create(); FileOutputStream fos = closer.register(new FileOutputStream(file));
try(Closer closer = Closer.create()) {
FileOutputStream fos = closer.register(new FileOutputStream(file));
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
ClipboardWriter writer = closer.register(format.getWriter(bos));) {
ClipboardWriter writer = closer.register(format.getWriter(bos));
writer.write(clipboard);
} catch(IOException e) {
pluginInterface.getLogger().warning("An error occured while saving schematic of " + regionInterface.getName() + ", enable debug to see the complete stacktrace");