mirror of
https://github.com/md5sha256/AreaShop.git
synced 2026-11-06 17:32:43 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f04efb3e8 | ||
|
|
5872af7466 | ||
|
|
a8315e627b | ||
|
|
35361308a8 | ||
|
|
0c359cdfdf | ||
|
|
2a03e7612a | ||
|
|
aaf4e07053 | ||
|
|
fa91016613 | ||
|
|
f4bd4ee6c6 | ||
|
|
44fd7deea5 |
No files matched your search
+3
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+1
-1
@@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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>
|
||||
|
||||
+172
-131
@@ -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;
|
||||
}
|
||||
|
||||
+2
-1
@@ -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();
|
||||
|
||||
+9
@@ -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
|
||||
|
||||
+6
-12
@@ -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");
|
||||
|
||||
Reference in new issue
Block a user