Compare commits

..
6 Commits
Author SHA1 Message Date
Andrew Wong f6d1f3389c Bug fixes 2020-08-22 01:38:28 +02:00
Andrew Wong 834ea5d994 Minor bugfixes 2020-08-22 01:14:24 +02:00
Andrew Wong f89d917b08 Remove guice as a dependency 2020-08-19 23:17:04 +02:00
Andrew Wong c2b12119e8 Bump b-stats tp 1.6, rewrite the Analytics class 2020-08-19 18:44:54 +02:00
Andrew Wong 8c3b2fe8cc Fix the repo for FAWE, code-base overhaul for the regions package.
Change some "hot" code which would call Bukkit#getOfflinePlayer sync.
Utilise inheritance better; Introduce the TransactionalRegion class.
2020-08-18 22:42:44 +02:00
Andrew Wong 2a18ec75b0 Squash History. Update to v2.7 2020-08-17 22:55:53 +02:00
45 changed files with 5085 additions and 5305 deletions

No files matched your search

+20 -5
View File
@@ -11,7 +11,7 @@
<artifactId>areashop</artifactId>
<packaging>jar</packaging>
<name>AreaShop</name>
<version>2.7</version> <!-- Main plugin version -->
<version>2.7.1-SNAPSHOT</version> <!-- Main plugin version -->
<repositories>
<repository>
@@ -25,11 +25,11 @@
</repositories>
<dependencies>
<!-- Bukkit, AreaShop aims to support 1.16.5+ -->
<!-- Bukkit, AreaShop aims to support 1.13.2+ -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.16.5-R0.1-SNAPSHOT</version>
<version>1.13.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
@@ -47,10 +47,19 @@
<version>3.7.2</version>
</dependency>
<!-- JetBrains Annotations -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>19.0.0</version>
<scope>provided</scope>
</dependency>
<!-- Paper Lib -->
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
</dependency>
<!-- WorldEdit/WorldGuard -->
@@ -112,7 +121,7 @@
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>1.1</version>
<version>1.6</version>
<scope>compile</scope>
</dependency>
@@ -263,10 +272,16 @@
<pattern>org.bstats</pattern>
<shadedPattern>me.wiefferink.areashop.shaded.bstats</shadedPattern>
</relocation>
<!-- Relocate PaperLib -->
<relocation>
<pattern>io.papermc</pattern>
<shadedPattern>me.wiefferink.areashop.shaded.paperlib</shadedPattern>
</relocation>
<!-- Relocate Google Guice & Guice-Assisted-Inject -->
<relocation>
<pattern>com.google.inject</pattern>
<shadedPattern>me.wiefferink.areashop.shaded.inject</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
@@ -4,6 +4,7 @@ import co.aikar.taskchain.BukkitTaskChainFactory;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.managers.RegionManager;
import me.wiefferink.areashop.commands.TestCommand;
import me.wiefferink.areashop.interfaces.AreaShopInterface;
import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
@@ -29,11 +30,9 @@ 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;
@@ -51,7 +50,6 @@ 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
@@ -114,7 +112,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]+)");
private static final Pattern VERSION_MATCHER_2 = Pattern.compile("([0-9]+)\\.([0-9]+)\\.([0-9]+)");
// Statically available instance
private static AreaShop instance = null;
// General variables
@@ -132,7 +130,6 @@ 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;
@@ -342,7 +339,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
public Economy getEconomy() {
RegisteredServiceProvider<Economy> economy = getServer().getServicesManager()
.getRegistration(net.milkbowl.vault.economy.Economy.class);
if (economy == null || economy.getProvider() == null) {
if (economy == null) {
error(
"There is no economy provider to support Vault, make sure you installed an economy plugin");
return null;
@@ -423,12 +420,6 @@ 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"));
@@ -580,6 +571,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
public void onEnable() {
AreaShop.instance = this;
Utils.factory = BukkitTaskChainFactory.create(this);
getCommand("test").setExecutor(new TestCommand());
managers = new HashSet<>();
boolean error = !loadHooks();
@@ -588,7 +580,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);
@@ -632,7 +624,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_2.matcher(latestVersion);
matcher0 = VERSION_MATCHER_3.matcher(latestVersion);
if (!matcher0.find()) {
throw new IllegalArgumentException("Invalid Versions Detected!");
}
@@ -782,10 +774,6 @@ 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.
*
@@ -6,7 +6,10 @@ import org.apache.commons.lang.StringUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Abstract class for generalising command classes.
@@ -28,12 +31,11 @@ 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, " ").toLowerCase();
final String start = getCommandStart().toLowerCase();
if(commandString.length() > start.length()) {
return commandString.startsWith(start + " ");
String commandString = command.getName() + " " + StringUtils.join(args, " ");
if(commandString.length() > getCommandStart().length()) {
return commandString.toLowerCase().startsWith(getCommandStart().toLowerCase() + " ");
}
return commandString.startsWith(start);
return commandString.toLowerCase().startsWith(getCommandStart().toLowerCase());
}
/**
@@ -44,7 +46,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 Collections.emptyList();
return new ArrayList<>();
}
/**
@@ -112,7 +112,7 @@ public class FindCommand extends CommandAreaShop {
// Find rental regions
else {
List<RentRegion> regions = plugin.getFileManager().getRents();
List<RentRegion> results = new ArrayList<>();
List<RentRegion> results = new ArrayList<>(regions.size());
for(RentRegion region : regions) {
if(!region.isRented()
&& ((region.getPrice() <= balance && !maxPriceSet) || (region.getPrice() <= maxPrice && maxPriceSet))
@@ -259,7 +259,7 @@ public class ImportJob {
if(owner != null) {
@SuppressWarnings("deprecation")
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(owner);
if(offlinePlayer != null) {
if(offlinePlayer.hasPlayedBefore()) {
existing.add(offlinePlayer.getUniqueId());
}
}
@@ -338,9 +338,7 @@ public class ImportJob {
// Determine unit and add that to the price
String unitSuffix = "";
if("region".equalsIgnoreCase(unit)) {
// add nothing
} else if("m3".equalsIgnoreCase(unit)) {
if("m3".equalsIgnoreCase(unit)) {
unitSuffix = "*%volume%";
} else { // m2 or nothing (in case not set, we should actually look in parent files to correctly set this...)
unitSuffix = "*(%volume%/%height%)"; // This is better than width*depth because of polygon regions
@@ -10,11 +10,7 @@ import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
// TODO fix '/as info' help message hitting replacement limit (improve depth tracking?)
// TODO add info about autoExtend
@@ -62,7 +58,7 @@ public class InfoCommand extends CommandAreaShop {
} else {
// First sort by type, then by name
regions.sort((one, two) -> {
int typeCompare = getTypeOrder(two).compareTo(getTypeOrder(one));
int typeCompare = Integer.compare(getTypeOrder(two), getTypeOrder(one));
if(typeCompare != 0) {
return typeCompare;
} else {
@@ -86,20 +82,22 @@ public class InfoCommand extends CommandAreaShop {
for(int i = (page - 1) * itemsPerPage; i < page * itemsPerPage && i < regions.size(); i++) {
String state;
GeneralRegion region = regions.get(i);
if(region.getType() == GeneralRegion.RegionType.RENT) {
if(region.getOwner() == null) {
if(region instanceof RentRegion) {
if(region.isAvailable()) {
state = "Forrent";
} else {
state = "Rented";
}
} else {
if(region.getOwner() == null) {
} else if (region instanceof BuyRegion){
if(region.isAvailable()) {
state = "Forsale";
} else if(!((BuyRegion)region).isInResellingMode()) {
state = "Sold";
} else {
state = "Reselling";
}
} else {
state = region.getState().getValue();
}
plugin.messageNoPrefix(sender, "info-entry" + state, region);
linesPrinted++;
@@ -137,15 +135,15 @@ public class InfoCommand extends CommandAreaShop {
* @param region The region to get the order for
* @return An integer for sorting by type
*/
private Integer getTypeOrder(GeneralRegion region) {
private int getTypeOrder(GeneralRegion region) {
if(region.getType() == GeneralRegion.RegionType.RENT) {
if(region.getOwner() == null) {
if(region.isAvailable()) {
return 1;
} else {
return 2;
}
} else {
if(region.getOwner() == null) {
if(region.isAvailable()) {
return 3;
} else if(!((BuyRegion)region).isInResellingMode()) {
return 4;
@@ -230,205 +228,201 @@ public class InfoCommand extends CommandAreaShop {
// Region info
else if(args[1].equalsIgnoreCase("region")) {
if(args.length > 1) {
RentRegion rent = null;
BuyRegion buy = null;
if(args.length > 2) {
rent = plugin.getFileManager().getRent(args[2]);
buy = plugin.getFileManager().getBuy(args[2]);
} else {
if(sender instanceof Player) {
// get the region by location
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
if(regions.get(0) instanceof RentRegion) {
rent = (RentRegion)regions.get(0);
} else if(regions.get(0) instanceof BuyRegion) {
buy = (BuyRegion)regions.get(0);
}
}
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
RentRegion rent = null;
BuyRegion buy = null;
if(args.length > 2) {
rent = plugin.getFileManager().getRent(args[2]);
buy = plugin.getFileManager().getBuy(args[2]);
} else {
if(sender instanceof Player) {
// get the region by location
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
if(regions.get(0) instanceof RentRegion) {
rent = (RentRegion)regions.get(0);
} else if(regions.get(0) instanceof BuyRegion) {
buy = (BuyRegion)regions.get(0);
}
}
}
if(rent == null && buy == null) {
plugin.message(sender, "info-regionNotExisting", args[2]);
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
if(rent != null) {
plugin.message(sender, "info-regionHeaderRent", rent);
if(rent.isRented()) {
plugin.messageNoPrefix(sender, "info-regionRented", rent);
plugin.messageNoPrefix(sender, "info-regionExtending", rent);
// Money back
if(UnrentCommand.canUse(sender, rent)) {
plugin.messageNoPrefix(sender, "info-regionMoneyBackRentClick", rent);
} else {
plugin.messageNoPrefix(sender, "info-regionMoneyBackRent", rent);
}
// Friends
if(!rent.getFriendsFeature().getFriendNames().isEmpty()) {
String messagePart = "info-friend";
if(DelfriendCommand.canUse(sender, rent)) {
messagePart = "info-friendRemove";
}
plugin.messageNoPrefix(sender, "info-regionFriends", rent, Utils.combinedMessage(rent.getFriendsFeature().getFriendNames(), messagePart));
}
if(rent == null && buy == null) {
plugin.message(sender, "info-regionNotExisting", args[2]);
return;
}
if(rent != null) {
plugin.message(sender, "info-regionHeaderRent", rent);
if(rent.isRented()) {
plugin.messageNoPrefix(sender, "info-regionRented", rent);
plugin.messageNoPrefix(sender, "info-regionExtending", rent);
// Money back
if(UnrentCommand.canUse(sender, rent)) {
plugin.messageNoPrefix(sender, "info-regionMoneyBackRentClick", rent);
} else {
plugin.messageNoPrefix(sender, "info-regionCanBeRented", rent);
plugin.messageNoPrefix(sender, "info-regionMoneyBackRent", rent);
}
if(rent.getLandlordName() != null) {
plugin.messageNoPrefix(sender, "info-regionLandlord", rent);
}
// Maximum extends
if(rent.getMaxExtends() != -1) {
if(rent.getMaxExtends() == 0) {
plugin.messageNoPrefix(sender, "info-regionNoExtending", rent);
} else if(rent.isRented()) {
plugin.messageNoPrefix(sender, "info-regionExtendsLeft", rent);
} else {
plugin.messageNoPrefix(sender, "info-regionMaxExtends", rent);
// Friends
if(!rent.getFriendsFeature().getFriendNames().isEmpty()) {
String messagePart = "info-friend";
if(DelfriendCommand.canUse(sender, rent)) {
messagePart = "info-friendRemove";
}
plugin.messageNoPrefix(sender, "info-regionFriends", rent, Utils.combinedMessage(rent.getFriendsFeature().getFriendNames(), messagePart));
}
// If maxExtends is zero it does not make sense to show this message
if(rent.getMaxRentTime() != -1 && rent.getMaxExtends() != 0) {
plugin.messageNoPrefix(sender, "info-regionMaxRentTime", rent);
}
if(rent.getInactiveTimeUntilUnrent() != -1) {
plugin.messageNoPrefix(sender, "info-regionInactiveUnrent", rent);
}
// Teleport
Message tp = Message.fromKey("info-prefix");
boolean foundSomething = false;
if(TeleportCommand.canUse(sender, rent)) {
foundSomething = true;
tp.append(Message.fromKey("info-regionTeleport").replacements(rent));
}
if(SetteleportCommand.canUse(sender, rent)) {
if(foundSomething) {
tp.append(", ");
}
foundSomething = true;
tp.append(Message.fromKey("info-setRegionTeleport").replacements(rent));
}
if(foundSomething) {
tp.append(".");
tp.send(sender);
}
// Signs
List<String> signLocations = new ArrayList<>();
for(Location location : rent.getSignsFeature().getSignLocations()) {
signLocations.add(Message.fromKey("info-regionSignLocation").replacements(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()).getPlain());
}
if(!signLocations.isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionSigns", Utils.createCommaSeparatedList(signLocations));
}
// Groups
if(sender.hasPermission("areashop.groupinfo") && !rent.getGroupNames().isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionGroups", Utils.createCommaSeparatedList(rent.getGroupNames()));
}
// Restoring
if(rent.isRestoreEnabled()) {
plugin.messageNoPrefix(sender, "info-regionRestoringRent", rent);
}
// Restrictions
if(!rent.isRented()) {
if(rent.restrictedToRegion()) {
plugin.messageNoPrefix(sender, "info-regionRestrictedRegionRent", rent);
} else if(rent.restrictedToWorld()) {
plugin.messageNoPrefix(sender, "info-regionRestrictedWorldRent", rent);
}
}
plugin.messageNoPrefix(sender, "info-regionFooterRent", rent);
} else if(buy != null) {
plugin.message(sender, "info-regionHeaderBuy", buy);
if(buy.isSold()) {
if(buy.isInResellingMode()) {
plugin.messageNoPrefix(sender, "info-regionReselling", buy);
plugin.messageNoPrefix(sender, "info-regionReselPrice", buy);
} else {
plugin.messageNoPrefix(sender, "info-regionBought", buy);
}
// Money back
if(!buy.getBooleanSetting("buy.sellDisabled")) {
if (SellCommand.canUse(sender, buy)) {
plugin.messageNoPrefix(sender, "info-regionMoneyBackBuyClick", buy);
} else {
plugin.messageNoPrefix(sender, "info-regionMoneyBackBuy", buy);
}
}
// Friends
if(!buy.getFriendsFeature().getFriendNames().isEmpty()) {
String messagePart = "info-friend";
if(DelfriendCommand.canUse(sender, buy)) {
messagePart = "info-friendRemove";
}
plugin.messageNoPrefix(sender, "info-regionFriends", buy, Utils.combinedMessage(buy.getFriendsFeature().getFriendNames(), messagePart));
}
} else {
plugin.messageNoPrefix(sender, "info-regionCanBeBought", buy);
}
if(buy.getLandlord() != null) {
plugin.messageNoPrefix(sender, "info-regionLandlord", buy);
}
if(buy.getInactiveTimeUntilSell() != -1) {
plugin.messageNoPrefix(sender, "info-regionInactiveSell", buy);
}
// Teleport
Message tp = Message.fromKey("info-prefix");
boolean foundSomething = false;
if(TeleportCommand.canUse(sender, buy)) {
foundSomething = true;
tp.append(Message.fromKey("info-regionTeleport").replacements(buy));
}
if(SetteleportCommand.canUse(sender, buy)) {
if(foundSomething) {
tp.append(", ");
}
foundSomething = true;
tp.append(Message.fromKey("info-setRegionTeleport").replacements(buy));
}
if(foundSomething) {
tp.append(".");
tp.send(sender);
}
// Signs
List<String> signLocations = new ArrayList<>();
for(Location location : buy.getSignsFeature().getSignLocations()) {
signLocations.add(Message.fromKey("info-regionSignLocation").replacements(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()).getPlain());
}
if(!signLocations.isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionSigns", Utils.createCommaSeparatedList(signLocations));
}
// Groups
if(sender.hasPermission("areashop.groupinfo") && !buy.getGroupNames().isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionGroups", Utils.createCommaSeparatedList(buy.getGroupNames()));
}
// Restoring
if(buy.isRestoreEnabled()) {
plugin.messageNoPrefix(sender, "info-regionRestoringBuy", buy);
}
// Restrictions
if(!buy.isSold()) {
if(buy.restrictedToRegion()) {
plugin.messageNoPrefix(sender, "info-regionRestrictedRegionBuy", buy);
} else if(buy.restrictedToWorld()) {
plugin.messageNoPrefix(sender, "info-regionRestrictedWorldBuy", buy);
}
}
plugin.messageNoPrefix(sender, "info-regionFooterBuy", buy);
} else {
plugin.messageNoPrefix(sender, "info-regionCanBeRented", rent);
}
} else {
plugin.message(sender, "info-regionHelp");
if(rent.getLandlordName() != null) {
plugin.messageNoPrefix(sender, "info-regionLandlord", rent);
}
// Maximum extends
if(rent.getMaxExtends() != -1) {
if(rent.getMaxExtends() == 0) {
plugin.messageNoPrefix(sender, "info-regionNoExtending", rent);
} else if(rent.isRented()) {
plugin.messageNoPrefix(sender, "info-regionExtendsLeft", rent);
} else {
plugin.messageNoPrefix(sender, "info-regionMaxExtends", rent);
}
}
// If maxExtends is zero it does not make sense to show this message
if(rent.getMaxRentTime() != -1 && rent.getMaxExtends() != 0) {
plugin.messageNoPrefix(sender, "info-regionMaxRentTime", rent);
}
if(rent.getInactivityThresholdMillis() != -1) {
plugin.messageNoPrefix(sender, "info-regionInactiveUnrent", rent);
}
// Teleport
Message tp = Message.fromKey("info-prefix");
boolean foundSomething = false;
if(TeleportCommand.canUse(sender, rent)) {
foundSomething = true;
tp.append(Message.fromKey("info-regionTeleport").replacements(rent));
}
if(SetteleportCommand.canUse(sender, rent)) {
if(foundSomething) {
tp.append(", ");
}
foundSomething = true;
tp.append(Message.fromKey("info-setRegionTeleport").replacements(rent));
}
if(foundSomething) {
tp.append(".");
tp.send(sender);
}
// Signs
List<String> signLocations = new ArrayList<>();
for(Location location : rent.getSignsFeature().getSignLocations()) {
signLocations.add(Message.fromKey("info-regionSignLocation").replacements(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()).getPlain());
}
if(!signLocations.isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionSigns", Utils.createCommaSeparatedList(signLocations));
}
// Groups
if(sender.hasPermission("areashop.groupinfo") && !rent.getGroupNames().isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionGroups", Utils.createCommaSeparatedList(rent.getGroupNames()));
}
// Restoring
if(rent.isRestoreEnabled()) {
plugin.messageNoPrefix(sender, "info-regionRestoringRent", rent);
}
// Restrictions
if(!rent.isRented()) {
if(rent.restrictedToRegion()) {
plugin.messageNoPrefix(sender, "info-regionRestrictedRegionRent", rent);
} else if(rent.restrictedToWorld()) {
plugin.messageNoPrefix(sender, "info-regionRestrictedWorldRent", rent);
}
}
plugin.messageNoPrefix(sender, "info-regionFooterRent", rent);
} else if(buy != null) {
plugin.message(sender, "info-regionHeaderBuy", buy);
if(buy.isSold()) {
if(buy.isInResellingMode()) {
plugin.messageNoPrefix(sender, "info-regionReselling", buy);
plugin.messageNoPrefix(sender, "info-regionReselPrice", buy);
} else {
plugin.messageNoPrefix(sender, "info-regionBought", buy);
}
// Money back
if(!buy.getBooleanSetting("buy.sellDisabled")) {
if (SellCommand.canUse(sender, buy)) {
plugin.messageNoPrefix(sender, "info-regionMoneyBackBuyClick", buy);
} else {
plugin.messageNoPrefix(sender, "info-regionMoneyBackBuy", buy);
}
}
// Friends
if(!buy.getFriendsFeature().getFriendNames().isEmpty()) {
String messagePart = "info-friend";
if(DelfriendCommand.canUse(sender, buy)) {
messagePart = "info-friendRemove";
}
plugin.messageNoPrefix(sender, "info-regionFriends", buy, Utils.combinedMessage(buy.getFriendsFeature().getFriendNames(), messagePart));
}
} else {
plugin.messageNoPrefix(sender, "info-regionCanBeBought", buy);
}
if(buy.getLandlord() != null) {
plugin.messageNoPrefix(sender, "info-regionLandlord", buy);
}
if(buy.getInactivityThresholdMillis() != -1) {
plugin.messageNoPrefix(sender, "info-regionInactiveSell", buy);
}
// Teleport
Message tp = Message.fromKey("info-prefix");
boolean foundSomething = false;
if(TeleportCommand.canUse(sender, buy)) {
foundSomething = true;
tp.append(Message.fromKey("info-regionTeleport").replacements(buy));
}
if(SetteleportCommand.canUse(sender, buy)) {
if(foundSomething) {
tp.append(", ");
}
foundSomething = true;
tp.append(Message.fromKey("info-setRegionTeleport").replacements(buy));
}
if(foundSomething) {
tp.append(".");
tp.send(sender);
}
// Signs
List<String> signLocations = new ArrayList<>();
for(Location location : buy.getSignsFeature().getSignLocations()) {
signLocations.add(Message.fromKey("info-regionSignLocation").replacements(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()).getPlain());
}
if(!signLocations.isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionSigns", Utils.createCommaSeparatedList(signLocations));
}
// Groups
if(sender.hasPermission("areashop.groupinfo") && !buy.getGroupNames().isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionGroups", Utils.createCommaSeparatedList(buy.getGroupNames()));
}
// Restoring
if(buy.isRestoreEnabled()) {
plugin.messageNoPrefix(sender, "info-regionRestoringBuy", buy);
}
// Restrictions
if(!buy.isSold()) {
if(buy.restrictedToRegion()) {
plugin.messageNoPrefix(sender, "info-regionRestrictedRegionBuy", buy);
} else if(buy.restrictedToWorld()) {
plugin.messageNoPrefix(sender, "info-regionRestrictedWorldBuy", buy);
}
}
plugin.messageNoPrefix(sender, "info-regionFooterBuy", buy);
}
} else {
plugin.message(sender, "info-help");
@@ -8,6 +8,7 @@ import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
public class LinksignsCommand extends CommandAreaShop {
@@ -47,14 +48,11 @@ public class LinksignsCommand extends CommandAreaShop {
if(signProfilesSection != null) {
Set<String> profiles = signProfilesSection.getKeys(false);
if(!profiles.contains(profile)) {
ArrayList<String> message = new ArrayList<>();
StringJoiner joiner = new StringJoiner(", ");
for(String p : profiles) {
if(!message.isEmpty()) {
message.add(", ");
}
message.addAll(Message.fromKey("addsign-profile").replacements(p).get());
Message.fromKey("addsign-profile").replacements(p).get().forEach(joiner::add);
}
plugin.message(sender, "addsign-wrongProfile", Message.fromList(message));
plugin.message(sender, "addsign-wrongProfile", joiner.toString());
return;
}
}
@@ -85,7 +85,7 @@ public class ResellCommand extends CommandAreaShop {
buy.update();
plugin.message(sender, "resell-success", buy);
} else if(sender.hasPermission("areashop.resell") && sender instanceof Player) {
if(!buy.isOwner((Player)sender)) {
if(!buy.isTransactionHolder((Player)sender)) {
plugin.message(sender, "resell-noPermissionOther", buy);
return;
}
@@ -2,6 +2,7 @@ package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.TransactionalRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -11,86 +12,87 @@ import java.util.List;
public class SellCommand extends CommandAreaShop {
@Override
public String getCommandStart() {
return "areashop sell";
}
/**
* Check if a person can sell the region.
*
* @param person The person to check
* @param region The region to check for
* @return true if the person can sell it, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if (person.hasPermission("areashop.sell")) {
return true;
}
if (person instanceof Player && region instanceof TransactionalRegion) {
Player player = (Player) person;
return ((TransactionalRegion) region).isTransactionHolder(player) && person.hasPermission("areashop.sellown");
}
return false;
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.sell") || target.hasPermission("areashop.sellown")) {
return "help-sell";
}
return null;
}
@Override
public String getCommandStart() {
return "areashop sell";
}
/**
* Check if a person can sell the region.
* @param person The person to check
* @param region The region to check for
* @return true if the person can sell it, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if(person.hasPermission("areashop.sell")) {
return true;
}
if(person instanceof Player) {
Player player = (Player)person;
return region.isOwner(player) && person.hasPermission("areashop.sellown");
}
return false;
}
@Override
public String getHelp(CommandSender target) {
if (target.hasPermission("areashop.sell") || target.hasPermission("areashop.sellown")) {
return "help-sell";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.sell") && !sender.hasPermission("areashop.sellown")) {
plugin.message(sender, "sell-noPermission");
return;
}
BuyRegion buy;
if(args.length <= 1) {
if(sender instanceof Player) {
// get the region by location
List<BuyRegion> regions = Utils.getImportantBuyRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
buy = regions.get(0);
}
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
buy = plugin.getFileManager().getBuy(args[1]);
}
if(buy == null) {
plugin.message(sender, "sell-notRegistered");
return;
}
if(!buy.isSold()) {
plugin.message(sender, "sell-notBought", buy);
return;
}
buy.sell(true, sender);
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!sender.hasPermission("areashop.sell") && !sender.hasPermission("areashop.sellown")) {
plugin.message(sender, "sell-noPermission");
return;
}
BuyRegion buy;
if (args.length <= 1) {
if (sender instanceof Player) {
// get the region by location
List<BuyRegion> regions = Utils.getImportantBuyRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
buy = regions.get(0);
}
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
buy = plugin.getFileManager().getBuy(args[1]);
}
if (buy == null) {
plugin.message(sender, "sell-notRegistered");
return;
}
if (!buy.isSold()) {
plugin.message(sender, "sell-notBought", buy);
return;
}
buy.sell(true, sender);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(BuyRegion region : plugin.getFileManager().getBuys()) {
if(region.isSold()) {
result.add(region.getName());
}
}
}
return result;
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if (toComplete == 2) {
for (BuyRegion region : plugin.getFileManager().getBuys()) {
if (region.isSold()) {
result.add(region.getName());
}
}
}
return result;
}
}
@@ -3,6 +3,7 @@ package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.regions.TransactionalRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
@@ -77,7 +78,7 @@ public class SetownerCommand extends CommandAreaShop {
UUID uuid = null;
@SuppressWarnings("deprecation")
OfflinePlayer player = Bukkit.getOfflinePlayer(args[1]);
if(player != null) {
if(player.hasPlayedBefore()) {
uuid = player.getUniqueId();
}
if(uuid == null) {
@@ -87,26 +88,27 @@ public class SetownerCommand extends CommandAreaShop {
if(region instanceof RentRegion) {
RentRegion rent = (RentRegion)region;
if(rent.isRenter(uuid)) {
if(rent.isTransactionHolder(uuid)) {
// extend
rent.setRentedUntil(rent.getRentedUntil() + rent.getDuration());
rent.setRenter(uuid);
plugin.message(sender, "setowner-succesRentExtend", region);
} else {
// change
if(!rent.isRented()) {
rent.setRentedUntil(Calendar.getInstance().getTimeInMillis() + rent.getDuration());
rent.setRentedUntil(System.currentTimeMillis() + rent.getDuration());
}
rent.setRenter(uuid);
plugin.message(sender, "setowner-succesRent", region);
}
rent.setTransactionHolder(uuid);
}
if(region instanceof BuyRegion) {
BuyRegion buy = (BuyRegion)region;
buy.setBuyer(uuid);
buy.setTransactionHolder(uuid);
plugin.message(sender, "setowner-succesBuy", region);
}
region.getFriendsFeature().deleteFriend(region.getOwner(), null);
if (region instanceof TransactionalRegion) {
region.getFriendsFeature().deleteFriend(((TransactionalRegion) region).getTransactionHolder(), null);
}
region.update();
region.saveRequired();
}
@@ -66,9 +66,9 @@ public class SetpriceCommand extends CommandAreaShop {
}
if("default".equalsIgnoreCase(args[1]) || "reset".equalsIgnoreCase(args[1])) {
if(region instanceof RentRegion) {
((RentRegion)region).setPrice(null);
((RentRegion)region).setPrice(0);
} else if(region instanceof BuyRegion) {
((BuyRegion)region).setPrice(null);
((BuyRegion)region).setPrice(0);
}
region.update();
plugin.message(sender, "setprice-successRemoved", region);
@@ -5,79 +5,78 @@ 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 {
/**
* 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 getCommandStart() {
return "areashop tp";
}
@Override
public String getCommandStart() {
return "areashop tp";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.teleportall") || target.hasPermission("areashop.teleport")) {
return "help-teleport";
}
return null;
}
@Override
public String getHelp(CommandSender target) {
if (target.hasPermission("areashop.teleportall") || target.hasPermission("areashop.teleport")) {
return "help-teleport";
}
return null;
}
/**
* 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 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) {
if (toComplete == 2) {
return plugin.getFileManager().getRegionNames();
} else if (toComplete == 3) {
return Collections.singletonList("sign");
}
return Collections.emptyList();
}
@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;
}
}
@@ -0,0 +1,31 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.AreaShop;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class TestCommand implements CommandExecutor {
private AreaShop areaShop = AreaShop.getInstance();
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (sender instanceof Player) {
final Player player = (Player) sender;
final Location location = player.getLocation();
ApplicableRegionSet region = areaShop.getRegionManager(player.getWorld()).getApplicableRegions(BlockVector3.at(location.getX(), location.getY(), location.getZ()));
for (ProtectedRegion r : region.getRegions()) {
player.sendMessage(r.getId());
}
}
return true;
}
}
@@ -2,6 +2,7 @@ package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.regions.TransactionalRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -11,86 +12,87 @@ import java.util.List;
public class UnrentCommand extends CommandAreaShop {
@Override
public String getCommandStart() {
return "areashop unrent";
}
/**
* Check if a person can unrent the region.
*
* @param person The person to check
* @param region The region to check for
* @return true if the person can unrent it, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if (person.hasPermission("areashop.unrent")) {
return true;
}
if (person instanceof Player && region instanceof TransactionalRegion) {
Player player = (Player) person;
return ((TransactionalRegion) region).isTransactionHolder(player) && person.hasPermission("areashop.unrentown");
}
return false;
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.unrent") || target.hasPermission("areashop.unrentown")) {
return "help-unrent";
}
return null;
}
@Override
public String getCommandStart() {
return "areashop unrent";
}
/**
* Check if a person can unrent the region.
* @param person The person to check
* @param region The region to check for
* @return true if the person can unrent it, otherwise false
*/
public static boolean canUse(CommandSender person, GeneralRegion region) {
if(person.hasPermission("areashop.unrent")) {
return true;
}
if(person instanceof Player) {
Player player = (Player)person;
return region.isOwner(player) && person.hasPermission("areashop.unrentown");
}
return false;
}
@Override
public String getHelp(CommandSender target) {
if (target.hasPermission("areashop.unrent") || target.hasPermission("areashop.unrentown")) {
return "help-unrent";
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.unrent") && !sender.hasPermission("areashop.unrentown")) {
plugin.message(sender, "unrent-noPermission");
return;
}
RentRegion rent;
if(args.length <= 1) {
if(sender instanceof Player) {
// get the region by location
List<RentRegion> regions = Utils.getImportantRentRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
rent = regions.get(0);
}
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
rent = plugin.getFileManager().getRent(args[1]);
}
if(rent == null) {
plugin.message(sender, "unrent-notRegistered");
return;
}
if(!rent.isRented()) {
plugin.message(sender, "unrent-notRented", rent);
return;
}
rent.unRent(true, sender);
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!sender.hasPermission("areashop.unrent") && !sender.hasPermission("areashop.unrentown")) {
plugin.message(sender, "unrent-noPermission");
return;
}
RentRegion rent;
if (args.length <= 1) {
if (sender instanceof Player) {
// get the region by location
List<RentRegion> regions = Utils.getImportantRentRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
rent = regions.get(0);
}
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
} else {
rent = plugin.getFileManager().getRent(args[1]);
}
if (rent == null) {
plugin.message(sender, "unrent-notRegistered");
return;
}
if (!rent.isRented()) {
plugin.message(sender, "unrent-notRented", rent);
return;
}
rent.unRent(true, sender);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(RentRegion region : plugin.getFileManager().getRents()) {
if(region.isRented()) {
result.add(region.getName());
}
}
}
return result;
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if (toComplete == 2) {
for (RentRegion region : plugin.getFileManager().getRents()) {
if (region.isRented()) {
result.add(region.getName());
}
}
}
return result;
}
}
@@ -56,13 +56,12 @@ public class FriendsFeature extends RegionFeature {
return false;
}
Set<String> friends = new HashSet<>(getRegion().getConfig().getStringList("general.friends"));
List<String> friends = getRegion().getConfig().getStringList("general.friends");
friends.remove(player.toString());
List<String> list = new ArrayList<>(friends);
if(list.isEmpty()) {
if(friends.isEmpty()) {
getRegion().setSetting("general.friends", null);
} else {
getRegion().setSetting("general.friends", list);
getRegion().setSetting("general.friends", friends);
}
return true;
}
@@ -0,0 +1,602 @@
package me.wiefferink.areashop.features;
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.features.signs.SignsFeature;
import me.wiefferink.areashop.handlers.worldguard.v7.WorldGuardHandler;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
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.Location;
import org.bukkit.Material;
import org.bukkit.World;
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
*/
private 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) {
// 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();
for (int x = min.getX(); x < max.getX(); x += 16) {
for (int z = min.getZ(); z < max.getZ(); z += 16) {
PaperLib.getChunkAtAsync(world, x >> 4, z >> 4);
}
}
CompletableFuture<Boolean> future = new CompletableFuture<>();
final boolean finalToSign = toSign;
Bukkit.getScheduler().runTaskLater(plugin, () -> {
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)) {
final SignsFeature feature = getRegion().getSignsFeature();
Collection<RegionSign> signs = feature.getSigns();
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));
final RegionSign first = signs.iterator().next();
Vector signVector = first.getLocation().add(0.5, 0.5, 0.5).toVector();
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);
}
}, 1);
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
Collection<RegionSign> signs = getRegion().getSignsFeature().getSigns();
boolean signAvailable = !signs.isEmpty();
if (toSign.get()) {
RegionSign sign = signs.iterator().next();
if (signAvailable) {
// Use the location 1 below the sign to prevent weird spawing above the sign
startLocation = sign.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 = sign.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) {
final WorldGuardInterface handler = AreaShop.getInstance().getWorldGuardHandler();
// Set to block in the middle, y configured in the config
Vector regionMin = handler.getMinimumPoint(worldguardRegion);
Vector regionMax = handler.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 + "'");
}
}
final Location location = player.getLocation();
startLocation = new Location(getRegion().getWorld(), middle.getX(), middle.getY(), middle.getZ(), location.getYaw(), location.getPitch());
}
// Set location in the center of the block
startLocation.setX(startLocation.getBlockX() + 0.5);
startLocation.setZ(startLocation.getBlockZ() + 0.5);
return startLocation;
}
}
@@ -1,10 +1,7 @@
package me.wiefferink.areashop.features.signs;
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;
@@ -12,10 +9,8 @@ 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;
import org.bukkit.block.Sign;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.WallSign;
@@ -111,7 +106,7 @@ public class RegionSign {
/**
* Get the material of the sign as saved in the config.
*
* @return Material of the sign, usually or one of the other wood types (different result for 1.13-), Material.AIR if none.
* @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.
*/
public Material getMaterial() {
String name = getRegion().getConfig().getString("general.signs." + key + ".signType");
@@ -125,7 +120,6 @@ 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
@@ -134,22 +128,19 @@ public class RegionSign {
return false;
}
final GeneralRegion region = getRegion();
if (region.isDeleted()) {
if (getRegion().isDeleted()) {
return false;
}
YamlConfiguration regionConfig = region.getConfig();
YamlConfiguration regionConfig = getRegion().getConfig();
ConfigurationSection signConfig = getProfile();
Block block = signLocation.getBlock();
final String value = region.getState().getValue();
if (signConfig == null || !signConfig.isSet(value)) {
if (signConfig == null || !signConfig.isSet(getRegion().getState().getValue())) {
block.setType(Material.AIR);
return true;
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(value);
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue());
// Get the lines
String[] signLines = new String[4];
@@ -163,55 +154,53 @@ public class RegionSign {
return true;
}
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 (!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());
if (!Materials.isSign(block.getType())) {
Material signType = getMaterial();
if (signType.name().contains("SIGN")) {
// Don't do physics here, we first need to update the direction
block.setType(signType, false);
BlockData data = block.getBlockData();
if (data instanceof WallSign) {
((WallSign) data).setFacing(getFacing());
} else if (data instanceof Sign) {
((org.bukkit.block.data.type.Sign) data).setRotation(getFacing());
}
block.setBlockData(data, true);
// 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());
}
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")) {
region.setSetting("general.signs." + key + ".signType", blockState.getType().name());
getRegion().setSetting("general.signs." + key + ".signType", block.getType().name());
}
if (!regionConfig.isString("general.signs." + key + ".facing")) {
final BlockFace rotation;
if (Materials.isSign(block.getType())) {
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();
BlockData data = block.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();
} else {
rotation = null;
}
} else {
rotation = null;
}
region.setSetting("general.signs." + key + ".facing", rotation == null ? null : rotation.toString());
getRegion().setSetting("general.signs." + key + ".facing", rotation == null ? null : rotation.toString());
}
// Apply replacements and color and then set it on the sign
Sign signState = (Sign) blockState;
Sign signState = (Sign) block.getState();
for (int i = 0; i < signLines.length; i++) {
if (signLines[i] == null) {
signState.setLine(i, "");
@@ -221,8 +210,7 @@ public class RegionSign {
signLines[i] = Utils.applyColors(signLines[i]);
signState.setLine(i, signLines[i]);
}
// BlockState#update *should* return true here.
blockState.update(false, false);
signState.update();
return true;
}
@@ -264,8 +252,7 @@ public class RegionSign {
if (signConfig == null) {
return false;
}
final GeneralRegion region = getRegion();
ConfigurationSection stateConfig = signConfig.getConfigurationSection(region.getState().getValue().toLowerCase());
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue().toLowerCase());
// Run player commands if specified
List<String> playerCommands = new ArrayList<>();
@@ -273,14 +260,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()));
}
region.runCommands(clicker, playerCommands);
getRegion().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()));
}
region.runCommands(Bukkit.getConsoleSender(), consoleCommands);
getRegion().runCommands(Bukkit.getConsoleSender(), consoleCommands);
return !playerCommands.isEmpty() || !consoleCommands.isEmpty();
}
@@ -7,7 +7,6 @@ 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;
@@ -34,11 +33,12 @@ import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class SignsFeature extends RegionFeature {
private static final Map<String, RegionSign> allSigns = Collections.synchronizedMap(new HashMap<>());
private static final Map<String, List<RegionSign>> signsByChunk = Collections.synchronizedMap(new HashMap<>());
private static final Map<String, RegionSign> allSigns = new ConcurrentHashMap<>();
private static final Map<String, List<RegionSign>> signsByChunk = new ConcurrentHashMap<>();
private Map<String, RegionSign> signs;
@@ -56,14 +56,12 @@ 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) {
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()));
AreaShop.warn("Sign with key " + signKey + " of region " + region.getName() + " does not have a proper location");
continue;
}
signs.put(sign.getStringLocation(), sign);
@@ -581,8 +579,8 @@ public class SignsFeature extends RegionFeature {
*
* @return List of signs
*/
public List<RegionSign> getSigns() {
return new ArrayList<>(signs.values());
public Collection<RegionSign> getSigns() {
return new HashSet<>(signs.values());
}
/**
@@ -621,15 +619,14 @@ public class SignsFeature extends RegionFeature {
i++;
}
String signPath = "general.signs." + i + ".";
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);
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);
if (profile != null && !profile.isEmpty()) {
region.setSetting(signPath + "profile", profile);
getRegion().setSetting(signPath + "profile", profile);
}
// Add to the map
RegionSign sign = new RegionSign(this, String.valueOf(i));
RegionSign sign = new RegionSign(this, i + "");
signs.put(sign.getStringLocation(), sign);
allSigns.put(sign.getStringLocation(), sign);
signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>())
@@ -1,269 +0,0 @@
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;
}
}
@@ -1,639 +0,0 @@
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));
}
}
@@ -4,6 +4,7 @@ import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.regions.TransactionalRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -15,6 +16,7 @@ import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
@@ -54,8 +56,12 @@ public final class PlayerLoginLogoutListener implements Listener {
return false;
}
// Notify for rents that almost run out
for (RentRegion region : plugin.getFileManager().getRents()) {
if (region.isRenter(player)) {
for (GeneralRegion generalRegion : plugin.getFileManager().getRegions()) {
if (!(generalRegion instanceof RentRegion)) {
continue;
}
final RentRegion region = (RentRegion) generalRegion;
if (region.isTransactionHolder(player)) {
String warningSetting = region.getStringSetting("rent.warningOnLoginTime");
if (warningSetting == null || warningSetting.isEmpty()) {
continue;
@@ -79,9 +85,9 @@ public final class PlayerLoginLogoutListener implements Listener {
return true;
}
List<GeneralRegion> regions = new ArrayList<>();
List<GeneralRegion> regions = new LinkedList<>();
for (GeneralRegion region : plugin.getFileManager().getRegions()) {
if (region.isOwner(player)) {
if (region instanceof TransactionalRegion && ((TransactionalRegion) region).isTransactionHolder(player)) {
regions.add(region);
}
}
@@ -119,7 +125,7 @@ public final class PlayerLoginLogoutListener implements Listener {
*/
private void updateLastActive(Player player) {
for (GeneralRegion region : plugin.getFileManager().getRegions()) {
if (region.isOwner(player)) {
if (region instanceof TransactionalRegion && ((TransactionalRegion) region).isTransactionHolder(player)) {
region.updateLastActiveTime();
}
}
@@ -39,15 +39,19 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
public class CommandManager extends Manager implements CommandExecutor, TabCompleter {
private final List<CommandAreaShop> commands = new ArrayList<>();
private final ArrayList<CommandAreaShop> commands;
/**
* Constructor.
*/
public CommandManager() {
commands = new ArrayList<>();
commands.add(new HelpCommand());
commands.add(new RentCommand());
commands.add(new UnrentCommand());
@@ -92,8 +96,7 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
* @return The list with AreaShop commands
*/
public List<CommandAreaShop> getCommands() {
// Return a cloned array
return new ArrayList<>(commands);
return commands;
}
/**
@@ -106,7 +109,7 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
return;
}
// Add all messages to a list
List<String> messages = new ArrayList<>(commands.size());
ArrayList<String> messages = new ArrayList<>();
plugin.message(target, "help-header");
plugin.message(target, "help-alias");
for(CommandAreaShop command : commands) {
@@ -129,21 +132,18 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
}
// Redirect `/as info player <player>` to `/as me <player>`
if(args.length == 3 && "info".equalsIgnoreCase(args[0]) && "player".equalsIgnoreCase(args[1])) {
if(args.length == 3 && "info".equals(args[0]) && "player".equals(args[1])) {
args[0] = "me";
args[1] = args[2];
}
// Execute command
boolean executed = false;
for (final CommandAreaShop cmd : commands) {
if (cmd.canExecute(command, args)) {
cmd.execute(sender, args);
for(int i = 0; i < commands.size() && !executed; i++) {
if(commands.get(i).canExecute(command, args)) {
commands.get(i).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) {
if(!sender.hasPermission("areashop.tabcomplete")) {
return Collections.emptyList();
}
List<String> result = new ArrayList<>();
if(!sender.hasPermission("areashop.tabcomplete")) {
return result;
}
int toCompleteNumber = args.length;
String toCompletePrefix = args[args.length - 1].toLowerCase();
//AreaShop.debug("toCompleteNumber=" + toCompleteNumber + ", toCompletePrefix=" + toCompletePrefix + ", length=" + toCompletePrefix.length());
@@ -184,7 +184,6 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
for(CommandAreaShop c : commands) {
if(c.canExecute(command, args)) {
result = c.getTabCompleteList(toCompleteNumber, start, sender);
break;
}
}
}
@@ -196,7 +195,8 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
set.add(suggestion);
}
}
return new ArrayList<>(set);
result.clear();
result.addAll(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.teleport.TeleportFeature;
import me.wiefferink.areashop.features.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 ignored) {
} catch(NoSuchMethodException | IllegalArgumentException e) {
// The feature does not have a region specific part
}
}
@@ -71,7 +71,7 @@ public class FeatureManager extends Manager {
}
/**
* Instantiate a feature for a certain region.
* Instanciate 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
@@ -1,8 +1,10 @@
package me.wiefferink.areashop.managers;
import co.aikar.taskchain.TaskChain;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.managers.storage.StorageException;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
@@ -22,12 +24,10 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.*;
import java.util.*;
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;
@@ -35,28 +35,26 @@ import java.util.stream.Collectors;
public class FileManager extends Manager {
private final Set<String> worldRegionsRequireSaving;
private final Map<String, BuyRegion> buyRegions;
private final Map<String, RentRegion> rentRegions;
private final String regionsPath;
private final Map<String, RegionGroup> groups;
private final String configPath;
private HashMap<String, GeneralRegion> regions = null;
private String regionsPath = null;
private HashMap<String, RegionGroup> groups = null;
private String configPath = null;
private YamlConfiguration config = null;
private final String groupsPath;
private String groupsPath = null;
private YamlConfiguration groupsConfig = null;
private final String defaultPath;
private String defaultPath = null;
private YamlConfiguration defaultConfig = null;
private YamlConfiguration defaultConfigFallback = null;
private boolean saveGroupsRequired = false;
private Map<String, Integer> versions = null;
private final String versionPath;
private final String schemFolder;
private HashMap<String, Integer> versions = null;
private String versionPath = null;
private String schemFolder = null;
/**
* Constructor, initialize variabeles.
*/
public FileManager() {
buyRegions = new HashMap<>();
rentRegions = new HashMap<>();
regions = new HashMap<>();
regionsPath = plugin.getDataFolder() + File.separator + AreaShop.regionsFolder;
configPath = plugin.getDataFolder() + File.separator + "config.yml";
groups = new HashMap<>();
@@ -152,8 +150,7 @@ public class FileManager extends Manager {
* @return The region if found, otherwise null
*/
public GeneralRegion getRegion(String name) {
GeneralRegion region = buyRegions.get(name.toLowerCase());
return region == null ? rentRegions.get(name.toLowerCase()) : region;
return regions.get(name.toLowerCase());
}
/**
@@ -163,7 +160,11 @@ public class FileManager extends Manager {
* @return RentRegion if it could be found, otherwise null
*/
public RentRegion getRent(String name) {
return rentRegions.get(name.toLowerCase());
GeneralRegion region = regions.get(name.toLowerCase());
if (region instanceof RentRegion) {
return (RentRegion) region;
}
return null;
}
/**
@@ -173,7 +174,11 @@ public class FileManager extends Manager {
* @return BuyRegion if it could be found, otherwise null
*/
public BuyRegion getBuy(String name) {
return buyRegions.get(name.toLowerCase());
GeneralRegion region = regions.get(name.toLowerCase());
if (region instanceof BuyRegion) {
return (BuyRegion) region;
}
return null;
}
/**
@@ -182,7 +187,13 @@ public class FileManager extends Manager {
* @return List of all rental regions
*/
public List<RentRegion> getRents() {
return new ArrayList<>(rentRegions.values());
List<RentRegion> result = new LinkedList<>();
for (GeneralRegion region : regions.values()) {
if (region instanceof RentRegion) {
result.add((RentRegion) region);
}
}
return result;
}
/**
@@ -191,7 +202,13 @@ public class FileManager extends Manager {
* @return List of all buy regions
*/
public List<BuyRegion> getBuys() {
return new ArrayList<>(buyRegions.values());
List<BuyRegion> result = new LinkedList<>();
for (GeneralRegion region : regions.values()) {
if (region instanceof BuyRegion) {
result.add((BuyRegion) region);
}
}
return result;
}
/**
@@ -200,10 +217,7 @@ public class FileManager extends Manager {
* @return List of all regions (it is safe to modify the list)
*/
public List<GeneralRegion> getRegions() {
List<GeneralRegion> regions = new ArrayList<>(buyRegions.size() + rentRegions.size());
regions.addAll(buyRegions.values());
regions.addAll(rentRegions.values());
return regions;
return new ArrayList<>(regions.values());
}
/**
@@ -212,7 +226,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getBuyNames() {
return new ArrayList<>(buyRegions.keySet());
return getBuys().stream().map(GeneralRegion::getName).collect(Collectors.toList());
}
/**
@@ -221,7 +235,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getRentNames() {
return new ArrayList<>(rentRegions.keySet());
return getRents().stream().map(GeneralRegion::getName).collect(Collectors.toList());
}
/**
@@ -230,9 +244,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getRegionNames() {
final List<String> list = getRentNames();
list.addAll(getBuyNames());
return list;
return getRegions().stream().map(GeneralRegion::getName).collect(Collectors.toList());
}
/**
@@ -241,11 +253,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getGroupNames() {
final List<String> list = new ArrayList<>(groups.size());
for (final RegionGroup group : groups.values()) {
list.add(group.getName());
}
return list;
return getGroups().stream().map(RegionGroup::getName).collect(Collectors.toList());
}
/**
@@ -281,11 +289,7 @@ public class FileManager extends Manager {
if (event.isCancelled()) {
return event;
}
if (region instanceof BuyRegion) {
buyRegions.put(region.getName().toLowerCase(), (BuyRegion) region);
} else if (region instanceof RentRegion) {
rentRegions.put(region.getName().toLowerCase(), (RentRegion) region);
}
regions.put(region.getName().toLowerCase(), region);
Bukkit.getPluginManager().callEvent(new AddedRegionEvent(region));
return event;
}
@@ -399,8 +403,7 @@ public class FileManager extends Manager {
}
region.resetRegionFlags();
buyRegions.remove(region.getLowerCaseName());
rentRegions.remove(region.getLowerCaseName());
regions.remove(region.getLowerCaseName());
// Remove file
File file = new File(plugin.getDataFolder() + File.separator + AreaShop.regionsFolder + File.separator + region.getLowerCaseName() + ".yml");
@@ -548,24 +551,24 @@ public class FileManager extends Manager {
}
this.saveWorldGuardRegions();
final AtomicInteger processed = new AtomicInteger();
TaskChain<?> chain = Utils.newChain();
int num = 0;
for (GeneralRegion region : getRegions()) {
if (region.isSaveRequired()) {
region.saveNowAsync().thenAccept((unused) -> processed.getAndIncrement());
chain = region.saveNowAsync(chain);
num++;
}
}
final int finalCount = getRegions().size();
int processed = num;
final CompletableFuture<Boolean> future = new CompletableFuture<>();
new BukkitRunnable() {
@Override
public void run() {
if (processed.get() != finalCount) {
return;
}
this.cancel();
chain.execute((result) -> {
if (result) {
AreaShop.debug("Region save complete! Processed " + processed + " regions.");
} else {
AreaShop.warn("Failed to save region data!");
}
}.runTaskTimer(plugin, 1, 1);
future.complete(result);
});
return future;
}
@@ -603,12 +606,9 @@ public class FileManager extends Manager {
RegionManager manager = plugin.getRegionManager(bukkitWorld);
if (manager != null) {
try {
if (plugin.getWorldGuard().getDescription().getVersion().startsWith("5.")) {
manager.save();
} else {
manager.saveChanges();
}
} catch (Exception e) {
manager.saveChanges();
} catch (StorageException e) {
e.printStackTrace();
AreaShop.warn("WorldGuard regions in world " + world + " could not be saved");
}
}
@@ -658,7 +658,26 @@ public class FileManager extends Manager {
if (checksPerTick < 1) {
checksPerTick = Math.min(getRegions().size() / 10, 10);
}
Utils.runAsBatches(getRegions(), checksPerTick, GeneralRegion::checkInactive, false);
// Run x amount of checks per tick. Will query for offline player async.
Utils.runAsBatches(getRegions(), checksPerTick, (region) -> {
CompletableFuture<OfflinePlayer> future = new CompletableFuture<>();
final String name = region.getLandlordName();
if (name == null) {
future.complete(null);
return;
}
if (Bukkit.getPlayer(name) != null) {
future.complete(Bukkit.getPlayer(name));
} else {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> future.complete(Bukkit.getOfflinePlayer(name)));
}
future.thenAccept(player -> Bukkit.getScheduler().callSyncMethod(plugin, () -> {
if (player != null) {
return region.checkInactivity(player);
}
return null;
} ));
}, false);
}
/**
@@ -854,8 +873,7 @@ public class FileManager extends Manager {
* Load all region files.
*/
public void loadRegionFiles() {
buyRegions.clear();
rentRegions.clear();
regions.clear();
final File file = new File(regionsPath);
if (!file.exists()) {
if (!file.mkdirs()) {
@@ -871,7 +889,7 @@ public class FileManager extends Manager {
private void loadRegionFilesNow() {
File file = new File(regionsPath);
File[] regionFiles = file.listFiles();
if (regionFiles == null) {
if (regionFiles == null || regionFiles.length == 0) {
plugin.setReady(true);
return;
}
@@ -1,79 +0,0 @@
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(uuid);
exitSignLinkMode(Bukkit.getPlayer(uuid));
}
}
@@ -74,18 +74,6 @@ 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
@@ -16,551 +16,471 @@ import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.util.Calendar;
import java.util.UUID;
public class BuyRegion extends GeneralRegion {
public class BuyRegion extends TransactionalRegion {
public BuyRegion(YamlConfiguration config) {
super(config);
}
private static final String UNKNOWN_BUYER = "<UNKNOWN>";
public BuyRegion(String name, World world) {
super(name, world);
}
public BuyRegion(YamlConfiguration config) {
super(config, RegionType.BUY);
}
@Override
public RegionType getType() {
return RegionType.BUY;
}
public BuyRegion(String name, World world) {
super(name, world, RegionType.BUY);
}
@Override
public RegionState getState() {
if(isSold() && isInResellingMode()) {
return RegionState.RESELL;
} else if(isSold() && !isInResellingMode()) {
return RegionState.SOLD;
} else {
return RegionState.FORSALE;
}
}
@Override
public RegionState getState() {
if (isSold() && isInResellingMode()) {
return RegionState.RESELL;
} else if (isSold() && !isInResellingMode()) {
return RegionState.SOLD;
} else {
return RegionState.FORSALE;
}
}
@Override
public boolean isAvailable() {
return !isSold();
}
/**
* Get the UUID of the owner of this region.
*
* @return The UUID of the owner of this region
*/
@Deprecated
public UUID getBuyer() {
String buyer = config.getString("buy.buyer");
if (buyer != null) {
try {
return UUID.fromString(buyer);
} catch (IllegalArgumentException e) {
// Incorrect UUID
}
}
return null;
}
/**
* Get the UUID of the owner of this region.
* @return The UUID of the owner of this region
*/
public UUID getBuyer() {
String buyer = config.getString("buy.buyer");
if(buyer != null) {
try {
return UUID.fromString(buyer);
} catch(IllegalArgumentException e) {
// Incorrect UUID
}
}
return null;
}
/**
* Set the buyer of this region.
*
* @param buyer The UUID of the player that should be set as buyer
*/
@Deprecated
public void setBuyer(UUID buyer) {
if (buyer == null) {
setSetting("buy.buyer", null);
setSetting("buy.buyerName", null);
} else {
setSetting("buy.buyer", buyer.toString());
setSetting("buy.buyerName", Utils.toName(buyer));
}
this.setTransactionHolder(buyer);
}
/**
* Check if a player is the buyer of this region.
* @param player Player to check
* @return true if this player owns this region, otherwise false
*/
public boolean isBuyer(OfflinePlayer player) {
return player != null && isBuyer(player.getUniqueId());
}
/**
* Check if a player is the buyer of this region.
*
* @param player Player to check
* @return true if this player owns this region, otherwise false
*/
@Deprecated
public boolean isBuyer(OfflinePlayer player) {
return player != null && isBuyer(player.getUniqueId());
}
public boolean isBuyer(UUID player) {
UUID buyer = getBuyer();
return !(buyer == null || player == null) && buyer.equals(player);
}
@Deprecated
public boolean isBuyer(UUID player) {
UUID buyer = getBuyer();
return !(buyer == null || player == null) && buyer.equals(player);
}
/**
* Set the buyer of this region.
* @param buyer The UUID of the player that should be set as buyer
*/
public void setBuyer(UUID buyer) {
if(buyer == null) {
setSetting("buy.buyer", null);
setSetting("buy.buyerName", null);
} else {
setSetting("buy.buyer", buyer.toString());
setSetting("buy.buyerName", Utils.toName(buyer));
}
}
/**
* Get the name of the player that owns this region.
*
* @return The name of the player that owns this region, if unavailable by UUID it will return the old cached name, if that is unavailable it will return &lt;UNKNOWN&gt;
*/
public String getPlayerName() {
String result = Utils.toName(getBuyer());
if (result.isEmpty()) {
result = getStringSetting("buy.buyerName");
if (result == null || result.isEmpty()) {
result = UNKNOWN_BUYER;
}
}
return result;
}
/**
* Get the name of the player that owns this region.
* @return The name of the player that owns this region, if unavailable by UUID it will return the old cached name, if that is unavailable it will return &lt;UNKNOWN&gt;
*/
public String getPlayerName() {
String result = Utils.toName(getBuyer());
if(result == null || result.isEmpty()) {
result = getStringSetting("buy.buyerName");
if(result == null || result.isEmpty()) {
result = "<UNKNOWN>";
}
}
return result;
}
/**
* Check if the region is being resold.
*
* @return true if the region is available for reselling, otherwise false
*/
public boolean isInResellingMode() {
return config.getBoolean("buy.resellMode");
}
/**
* Check if the region is sold.
* @return true if the region is sold, otherwise false
*/
public boolean isSold() {
return getBuyer() != null;
}
/**
* Get the resell price of this region.
*
* @return The resell price if isInResellingMode(), otherwise 0.0
*/
public double getResellPrice() {
return Math.max(0, config.getDouble("buy.resellPrice"));
}
/**
* Check if the region is being resold.
* @return true if the region is available for reselling, otherwise false
*/
public boolean isInResellingMode() {
return config.getBoolean("buy.resellMode");
}
/**
* Get the formatted string of the resellprice (includes prefix and suffix).
*
* @return The formatted string of the resellprice
*/
public String getFormattedResellPrice() {
return Utils.formatCurrency(getResellPrice());
}
/**
* Get the price of the region.
* @return The price of the region
*/
public double getPrice() {
return Math.max(0, Utils.evaluateToDouble(getStringSetting("buy.price"), this));
}
/**
* Set the region into resell mode with the given price.
*
* @param price The price this region should be put up for sale
*/
public void enableReselling(double price) {
setSetting("buy.resellMode", true);
setSetting("buy.resellPrice", price);
}
/**
* Get the resell price of this region.
* @return The resell price if isInResellingMode(), otherwise 0.0
*/
public double getResellPrice() {
return Math.max(0, config.getDouble("buy.resellPrice"));
}
/**
* Stop this region from being in resell mode.
*/
public void disableReselling() {
setSetting("buy.resellMode", null);
setSetting("buy.resellPrice", null);
}
/**
* Get the formatted string of the price (includes prefix and suffix).
* @return The formatted string of the price
*/
public String getFormattedPrice() {
return Utils.formatCurrency(getPrice());
}
@Override
public Object provideReplacement(String variable) {
switch (variable) {
case AreaShop.tagResellPrice:
return getFormattedResellPrice();
case AreaShop.tagRawResellPrice:
return getResellPrice();
default:
return super.provideReplacement(variable);
}
}
/**
* Get the formatted string of the resellprice (includes prefix and suffix).
* @return The formatted string of the resellprice
*/
public String getFormattedResellPrice() {
return Utils.formatCurrency(getResellPrice());
}
/**
* Minutes until automatic unrent when player is offline.
*
* @return The number of milliseconds until the region is unrented while player is offline
*/
@Deprecated
public long getInactiveTimeUntilSell() {
return Utils.getDurationFromMinutesOrStringInput(getStringSetting("buy.inactiveTimeUntilSell"));
}
/**
* Change the price of the region.
* @param price The price to set this region to
*/
public void setPrice(Double price) {
setSetting("buy.price", price);
}
/**
* Get a human readable string indicating how long the player can be offline until automatic unrent.
*
* @return String indicating the inactive time until unrent
*/
@Deprecated
public String getFormattedInactiveTimeUntilSell() {
return Utils.millisToHumanFormat(getInactiveTimeUntilSell());
}
/**
* Set the region into resell mode with the given price.
* @param price The price this region should be put up for sale
*/
public void enableReselling(double price) {
setSetting("buy.resellMode", true);
setSetting("buy.resellPrice", price);
}
/**
* Buy a region.
*
* @param offlinePlayer The player that wants to buy the region
* @return true if it succeeded and false if not
*/
@SuppressWarnings("deprecation")
public boolean buy(OfflinePlayer offlinePlayer) {
// Check if the player has permission
if (!plugin.hasPermission(offlinePlayer, "areashop.buy")) {
message(offlinePlayer, "buy-noPermission");
return false;
}
/**
* Stop this region from being in resell mode.
*/
public void disableReselling() {
setSetting("buy.resellMode", null);
setSetting("buy.resellPrice", null);
}
if (plugin.getEconomy() == null) {
message(offlinePlayer, "general-noEconomy");
return false;
}
/**
* Get the moneyBack percentage.
* @return The % of money the player will get back when selling
*/
public double getMoneyBackPercentage() {
return Utils.evaluateToDouble(getStringSetting("buy.moneyBack"), this);
}
if (isInResellingMode()) {
if (!plugin.hasPermission(offlinePlayer, "areashop.buyresell")) {
message(offlinePlayer, "buy-noPermissionResell");
return false;
}
} else {
if (!plugin.hasPermission(offlinePlayer, "areashop.buynormal")) {
message(offlinePlayer, "buy-noPermissionNoResell");
return false;
}
}
/**
* Get the amount of money that should be paid to the player when selling the region.
* @return The amount of money the player should get back
*/
public double getMoneyBackAmount() {
return getPrice() * (getMoneyBackPercentage() / 100.0);
}
if (getWorld() == null) {
message(offlinePlayer, "general-noWorld");
return false;
}
/**
* Get the formatted string of the amount of the moneyBack amount.
* @return String with currency symbols and proper fractional part
*/
public String getFormattedMoneyBackAmount() {
return Utils.formatCurrency(getMoneyBackAmount());
}
if (getRegion() == null) {
message(offlinePlayer, "general-noRegion");
return false;
}
@Override
public Object provideReplacement(String variable) {
switch(variable) {
case AreaShop.tagPrice:
return getFormattedPrice();
case AreaShop.tagRawPrice:
return getPrice();
case AreaShop.tagPlayerName:
return getPlayerName();
case AreaShop.tagPlayerUUID:
return getBuyer();
case AreaShop.tagResellPrice:
return getFormattedResellPrice();
case AreaShop.tagRawResellPrice:
return getResellPrice();
case AreaShop.tagMoneyBackAmount:
return getFormattedMoneyBackAmount();
case AreaShop.tagRawMoneyBackAmount:
return getMoneyBackAmount();
case AreaShop.tagMoneyBackPercentage:
return getMoneyBackPercentage() % 1.0 == 0.0 ? (int)getMoneyBackPercentage() : getMoneyBackPercentage();
case AreaShop.tagMaxInactiveTime:
return this.getFormattedInactiveTimeUntilSell();
if (isSold() && !(isInResellingMode() && !isBuyer(offlinePlayer))) {
if (isBuyer(offlinePlayer)) {
message(offlinePlayer, "buy-yours");
} else {
message(offlinePlayer, "buy-someoneElse");
}
return false;
}
default:
return super.provideReplacement(variable);
}
}
boolean isResell = isInResellingMode();
/**
* Minutes until automatic unrent when player is offline.
* @return The number of milliseconds until the region is unrented while player is offline
*/
public long getInactiveTimeUntilSell() {
return Utils.getDurationFromMinutesOrStringInput(getStringSetting("buy.inactiveTimeUntilSell"));
}
// Only relevant if the player is online
Player player = offlinePlayer.getPlayer();
if (player != null) {
// Check if the players needs to be in the region for buying
if (restrictedToRegion() && (!player.getWorld().getName().equals(getWorldName())
|| !getRegion().contains(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ()))) {
message(offlinePlayer, "buy-restrictedToRegion");
return false;
}
// Check if the players needs to be in the world for buying
if (restrictedToWorld() && !player.getWorld().getName().equals(getWorldName())) {
message(offlinePlayer, "buy-restrictedToWorld", player.getWorld().getName());
return false;
}
}
/**
* Get a human readable string indicating how long the player can be offline until automatic unrent.
* @return String indicating the inactive time until unrent
*/
public String getFormattedInactiveTimeUntilSell() {
return Utils.millisToHumanFormat(getInactiveTimeUntilSell());
}
// Check region limits
LimitResult limitResult = this.limitsAllow(RegionType.BUY, offlinePlayer);
AreaShop.debug("LimitResult: " + limitResult.toString());
if (!limitResult.actionAllowed()) {
if (limitResult.getLimitingFactor() == LimitType.TOTAL) {
message(offlinePlayer, "total-maximum", limitResult.getMaximum(), limitResult.getCurrent(), limitResult.getLimitingGroup());
return false;
}
if (limitResult.getLimitingFactor() == LimitType.BUYS) {
message(offlinePlayer, "buy-maximum", limitResult.getMaximum(), limitResult.getCurrent(), limitResult.getLimitingGroup());
return false;
}
// Should not be reached, but is safe like this
return false;
}
/**
* Buy a region.
* @param offlinePlayer The player that wants to buy the region
* @return true if it succeeded and false if not
*/
@SuppressWarnings("deprecation")
public boolean buy(OfflinePlayer offlinePlayer) {
// Check if the player has permission
if(!plugin.hasPermission(offlinePlayer, "areashop.buy")) {
message(offlinePlayer, "buy-noPermission");
return false;
}
// Check if the player has enough money
if (isResell && !plugin.getEconomy().has(offlinePlayer, getWorldName(), getResellPrice())) {
message(offlinePlayer, "buy-lowMoneyResell", Utils.formatCurrency(plugin.getEconomy().getBalance(offlinePlayer, getWorldName())));
return false;
}
if (!isResell && !plugin.getEconomy().has(offlinePlayer, getWorldName(), getPrice())) {
message(offlinePlayer, "buy-lowMoney", Utils.formatCurrency(plugin.getEconomy().getBalance(offlinePlayer, getWorldName())));
return false;
}
if(plugin.getEconomy() == null) {
message(offlinePlayer, "general-noEconomy");
return false;
}
UUID oldOwner = getBuyer();
if (isResell && oldOwner != null) {
// Broadcast and check event
ResellingRegionEvent event = new ResellingRegionEvent(this, offlinePlayer);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
message(offlinePlayer, "general-cancelled", event.getReason());
return false;
}
if(isInResellingMode()) {
if(!plugin.hasPermission(offlinePlayer, "areashop.buyresell")) {
message(offlinePlayer, "buy-noPermissionResell");
return false;
}
} else {
if(!plugin.hasPermission(offlinePlayer, "areashop.buynormal")) {
message(offlinePlayer, "buy-noPermissionNoResell");
return false;
}
}
getFriendsFeature().clearFriends();
double resellPrice = getResellPrice();
// Transfer the money to the previous owner
EconomyResponse r = plugin.getEconomy().withdrawPlayer(offlinePlayer, getWorldName(), getResellPrice());
if (!r.transactionSuccess()) {
message(offlinePlayer, "buy-payError");
AreaShop.debug("Something went wrong with getting money from " + offlinePlayer.getName() + " while buying " + getName() + ": " + r.errorMessage);
return false;
}
r = null;
OfflinePlayer oldOwnerPlayer = Bukkit.getOfflinePlayer(oldOwner);
String oldOwnerName = getPlayerName();
if (oldOwnerPlayer.hasPlayedBefore() && oldOwnerPlayer.getName() != null) {
r = plugin.getEconomy().depositPlayer(oldOwnerPlayer, getWorldName(), getResellPrice());
oldOwnerName = oldOwnerPlayer.getName();
} else if (oldOwnerName != null) {
r = plugin.getEconomy().depositPlayer(oldOwnerName, getWorldName(), getResellPrice());
}
if (r == null || !r.transactionSuccess()) {
AreaShop.warn("Something went wrong with paying '" + oldOwnerName + "' " + getFormattedPrice() + " for his resell of region " + getName() + " to " + offlinePlayer.getName());
}
// Resell is done, disable that now
disableReselling();
if(getWorld() == null) {
message(offlinePlayer, "general-noWorld");
return false;
}
// Set the owner
setBuyer(offlinePlayer.getUniqueId());
updateLastActiveTime();
if(getRegion() == null) {
message(offlinePlayer, "general-noRegion");
return false;
}
// Update everything
handleSchematicEvent(RegionEvent.RESELL);
if (isSold() && !(isInResellingMode() && !isBuyer(offlinePlayer))) {
if(isBuyer(offlinePlayer)) {
message(offlinePlayer, "buy-yours");
} else {
message(offlinePlayer, "buy-someoneElse");
}
return false;
}
// Notify about updates
this.notifyAndUpdate(new ResoldRegionEvent(this, oldOwner));
boolean isResell = isInResellingMode();
// Send message to the player
message(offlinePlayer, "buy-successResale", oldOwnerName);
Player seller = Bukkit.getPlayer(oldOwner);
if (seller != null) {
message(seller, "buy-successSeller", resellPrice);
}
} else {
// Broadcast and check event
BuyingRegionEvent event = new BuyingRegionEvent(this, offlinePlayer);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
message(offlinePlayer, "general-cancelled", event.getReason());
return false;
}
// Only relevant if the player is online
Player player = offlinePlayer.getPlayer();
if(player != null) {
// Check if the players needs to be in the region for buying
if(restrictedToRegion() && (!player.getWorld().getName().equals(getWorldName())
|| !getRegion().contains(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ()))) {
message(offlinePlayer, "buy-restrictedToRegion");
return false;
}
// Check if the players needs to be in the world for buying
if(restrictedToWorld() && !player.getWorld().getName().equals(getWorldName())) {
message(offlinePlayer, "buy-restrictedToWorld", player.getWorld().getName());
return false;
}
}
// Substract the money from the players balance
EconomyResponse r = plugin.getEconomy().withdrawPlayer(offlinePlayer, getWorldName(), getPrice());
if (!r.transactionSuccess()) {
message(offlinePlayer, "buy-payError");
return false;
}
// Optionally give money to the landlord
OfflinePlayer landlordPlayer = null;
if (getLandlord() != null) {
landlordPlayer = Bukkit.getOfflinePlayer(getLandlord());
}
String landlordName = getLandlordName();
if (landlordName != null) {
if (landlordPlayer != null && landlordPlayer.getName() != null) {
r = plugin.getEconomy().depositPlayer(landlordPlayer, getWorldName(), getPrice());
} else {
r = plugin.getEconomy().depositPlayer(landlordName, getWorldName(), getPrice());
}
if (r != null && !r.transactionSuccess()) {
AreaShop.warn("Something went wrong with paying '" + landlordName + "' " + getFormattedPrice() + " for his sell of region " + getName() + " to " + offlinePlayer.getName());
}
}
// Check region limits
LimitResult limitResult = this.limitsAllow(RegionType.BUY, offlinePlayer);
AreaShop.debug("LimitResult: " + limitResult.toString());
if(!limitResult.actionAllowed()) {
if(limitResult.getLimitingFactor() == LimitType.TOTAL) {
message(offlinePlayer, "total-maximum", limitResult.getMaximum(), limitResult.getCurrent(), limitResult.getLimitingGroup());
return false;
}
if(limitResult.getLimitingFactor() == LimitType.BUYS) {
message(offlinePlayer, "buy-maximum", limitResult.getMaximum(), limitResult.getCurrent(), limitResult.getLimitingGroup());
return false;
}
// Should not be reached, but is safe like this
return false;
}
// Set the owner
setBuyer(offlinePlayer.getUniqueId());
updateLastActiveTime();
// Check if the player has enough money
if (isResell && !plugin.getEconomy().has(offlinePlayer, getWorldName(), getResellPrice())) {
message(offlinePlayer, "buy-lowMoneyResell", Utils.formatCurrency(plugin.getEconomy().getBalance(offlinePlayer, getWorldName())));
return false;
}
if (!isResell && !plugin.getEconomy().has(offlinePlayer, getWorldName(), getPrice())) {
message(offlinePlayer, "buy-lowMoney", Utils.formatCurrency(plugin.getEconomy().getBalance(offlinePlayer, getWorldName())));
return false;
}
// Send message to the player
message(offlinePlayer, "buy-succes");
UUID oldOwner = getBuyer();
if(isResell && oldOwner != null) {
// Broadcast and check event
ResellingRegionEvent event = new ResellingRegionEvent(this, offlinePlayer);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
message(offlinePlayer, "general-cancelled", event.getReason());
return false;
}
// Update everything
handleSchematicEvent(RegionEvent.BOUGHT);
getFriendsFeature().clearFriends();
double resellPrice = getResellPrice();
// Transfer the money to the previous owner
EconomyResponse r = plugin.getEconomy().withdrawPlayer(offlinePlayer, getWorldName(), getResellPrice());
if(!r.transactionSuccess()) {
message(offlinePlayer, "buy-payError");
AreaShop.debug("Something went wrong with getting money from " + offlinePlayer.getName() + " while buying " + getName() + ": " + r.errorMessage);
return false;
}
r = null;
OfflinePlayer oldOwnerPlayer = Bukkit.getOfflinePlayer(oldOwner);
String oldOwnerName = getPlayerName();
if(oldOwnerPlayer != null && oldOwnerPlayer.getName() != null) {
r = plugin.getEconomy().depositPlayer(oldOwnerPlayer, getWorldName(), getResellPrice());
oldOwnerName = oldOwnerPlayer.getName();
} else if(oldOwnerName != null) {
r = plugin.getEconomy().depositPlayer(oldOwnerName, getWorldName(), getResellPrice());
}
if(r == null || !r.transactionSuccess()) {
AreaShop.warn("Something went wrong with paying '" + oldOwnerName + "' " + getFormattedPrice() + " for his resell of region " + getName() + " to " + offlinePlayer.getName());
}
// Resell is done, disable that now
disableReselling();
// Notify about updates
this.notifyAndUpdate(new BoughtRegionEvent(this));
}
return true;
}
// Set the owner
setBuyer(offlinePlayer.getUniqueId());
updateLastActiveTime();
/**
* Sell a purchased region, get part of the money back.
*
* @param giveMoneyBack true if the player should be given money back, otherwise false
* @param executor CommandSender to receive a message when the sell fails, or null
* @return true if the region has been sold, otherwise false
*/
@SuppressWarnings("deprecation")
public boolean sell(boolean giveMoneyBack, CommandSender executor) {
boolean own = executor instanceof Player && this.isBuyer((Player) executor);
if (executor != null) {
if (!executor.hasPermission("areashop.sell") && !own) {
message(executor, "sell-noPermissionOther");
return false;
}
if (!executor.hasPermission("areashop.sell") && !executor.hasPermission("areashop.sellown") && own) {
message(executor, "sell-noPermission");
return false;
}
if (!executor.hasPermission("areashop.sell")
&& executor.hasPermission("areashop.sellown")
&& own
&& getBooleanSetting("buy.sellDisabled")) {
message(executor, "sell-disabled");
return false;
}
}
// Update everything
handleSchematicEvent(RegionEvent.RESELL);
if (plugin.getEconomy() == null) {
return false;
}
// Notify about updates
this.notifyAndUpdate(new ResoldRegionEvent(this, oldOwner));
// Broadcast and check event
SellingRegionEvent event = new SellingRegionEvent(this);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
message(executor, "general-cancelled", event.getReason());
return false;
}
// Send message to the player
message(offlinePlayer, "buy-successResale", oldOwnerName);
Player seller = Bukkit.getPlayer(oldOwner);
if(seller != null) {
message(seller, "buy-successSeller", resellPrice);
}
} else {
// Broadcast and check event
BuyingRegionEvent event = new BuyingRegionEvent(this, offlinePlayer);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
message(offlinePlayer, "general-cancelled", event.getReason());
return false;
}
disableReselling();
// Give part of the buying price back
double moneyBack = getMoneyBackAmount();
if (moneyBack > 0 && giveMoneyBack) {
boolean noPayBack = false;
OfflinePlayer landlordPlayer = null;
if (getLandlord() != null) {
landlordPlayer = Bukkit.getOfflinePlayer(getLandlord());
}
String landlordName = getLandlordName();
EconomyResponse r;
if (landlordName != null) {
if (landlordPlayer != null && landlordPlayer.getName() != null) {
r = plugin.getEconomy().withdrawPlayer(landlordPlayer, getWorldName(), moneyBack);
} else {
r = plugin.getEconomy().withdrawPlayer(landlordName, getWorldName(), moneyBack);
}
if (r == null || !r.transactionSuccess()) {
noPayBack = true;
}
}
// Substract the money from the players balance
EconomyResponse r = plugin.getEconomy().withdrawPlayer(offlinePlayer, getWorldName(), getPrice());
if(!r.transactionSuccess()) {
message(offlinePlayer, "buy-payError");
return false;
}
// Optionally give money to the landlord
OfflinePlayer landlordPlayer = null;
if(getLandlord() != null) {
landlordPlayer = Bukkit.getOfflinePlayer(getLandlord());
}
String landlordName = getLandlordName();
if(landlordName != null) {
if(landlordPlayer != null && landlordPlayer.getName() != null) {
r = plugin.getEconomy().depositPlayer(landlordPlayer, getWorldName(), getPrice());
} else {
r = plugin.getEconomy().depositPlayer(landlordName, getWorldName(), getPrice());
}
if(r != null && !r.transactionSuccess()) {
AreaShop.warn("Something went wrong with paying '" + landlordName + "' " + getFormattedPrice() + " for his sell of region " + getName() + " to " + offlinePlayer.getName());
}
}
// Give back the money
OfflinePlayer player = Bukkit.getOfflinePlayer(getTransactionHolder());
if (player.hasPlayedBefore() && !noPayBack) {
EconomyResponse response = null;
boolean error = false;
try {
if (player.getName() != null) {
response = plugin.getEconomy().depositPlayer(player, getWorldName(), moneyBack);
} else if (getPlayerName() != null) {
response = plugin.getEconomy().depositPlayer(getPlayerName(), getWorldName(), moneyBack);
}
} catch (Exception e) {
error = true;
}
if (error || response == null || !response.transactionSuccess()) {
AreaShop.warn("Something went wrong with paying back money to " + getPlayerName() + " while selling region " + getName());
}
}
}
// Set the owner
setBuyer(offlinePlayer.getUniqueId());
updateLastActiveTime();
// Handle schematic save/restore (while %uuid% is still available)
handleSchematicEvent(RegionEvent.SOLD);
// Send message to the player
message(offlinePlayer, "buy-succes");
// Send message: before actual removal of the buyer so that it is still available for variables
message(executor, "sell-sold");
// Update everything
handleSchematicEvent(RegionEvent.BOUGHT);
// Remove friends and the owner
getFriendsFeature().clearFriends();
UUID oldBuyer = getBuyer();
setBuyer(null);
removeLastActiveTime();
// Notify about updates
this.notifyAndUpdate(new BoughtRegionEvent(this));
}
return true;
}
/**
* Sell a buyed region, get part of the money back.
* @param giveMoneyBack true if the player should be given money back, otherwise false
* @param executor CommandSender to receive a message when the sell fails, or null
* @return true if the region has been sold, otherwise false
*/
@SuppressWarnings("deprecation")
public boolean sell(boolean giveMoneyBack, CommandSender executor) {
boolean own = executor instanceof Player && this.isBuyer((Player)executor);
if(executor != null) {
if(!executor.hasPermission("areashop.sell") && !own) {
message(executor, "sell-noPermissionOther");
return false;
}
if(!executor.hasPermission("areashop.sell") && !executor.hasPermission("areashop.sellown") && own) {
message(executor, "sell-noPermission");
return false;
}
if(!executor.hasPermission("areashop.sell")
&& executor.hasPermission("areashop.sellown")
&& own
&& getBooleanSetting("buy.sellDisabled")) {
message(executor, "sell-disabled");
return false;
}
}
if(plugin.getEconomy() == null) {
return false;
}
// Broadcast and check event
SellingRegionEvent event = new SellingRegionEvent(this);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
message(executor, "general-cancelled", event.getReason());
return false;
}
disableReselling();
// Give part of the buying price back
double moneyBack = getMoneyBackAmount();
if(moneyBack > 0 && giveMoneyBack) {
boolean noPayBack = false;
OfflinePlayer landlordPlayer = null;
if(getLandlord() != null) {
landlordPlayer = Bukkit.getOfflinePlayer(getLandlord());
}
String landlordName = getLandlordName();
EconomyResponse r;
if(landlordName != null) {
if(landlordPlayer != null && landlordPlayer.getName() != null) {
r = plugin.getEconomy().withdrawPlayer(landlordPlayer, getWorldName(), moneyBack);
} else {
r = plugin.getEconomy().withdrawPlayer(landlordName, getWorldName(), moneyBack);
}
if(r == null || !r.transactionSuccess()) {
noPayBack = true;
}
}
// Give back the money
OfflinePlayer player = Bukkit.getOfflinePlayer(getBuyer());
if(player != null && !noPayBack) {
EconomyResponse response = null;
boolean error = false;
try {
if(player.getName() != null) {
response = plugin.getEconomy().depositPlayer(player, getWorldName(), moneyBack);
} else if(getPlayerName() != null) {
response = plugin.getEconomy().depositPlayer(getPlayerName(), getWorldName(), moneyBack);
}
} catch(Exception e) {
error = true;
}
if(error || response == null || !response.transactionSuccess()) {
AreaShop.warn("Something went wrong with paying back money to " + getPlayerName() + " while selling region " + getName());
}
}
}
// Handle schematic save/restore (while %uuid% is still available)
handleSchematicEvent(RegionEvent.SOLD);
// Send message: before actual removal of the buyer so that it is still available for variables
message(executor, "sell-sold");
// Remove friends and the owner
getFriendsFeature().clearFriends();
UUID oldBuyer = getBuyer();
setBuyer(null);
removeLastActiveTime();
// Notify about updates
this.notifyAndUpdate(new SoldRegionEvent(this, oldBuyer, Math.max(moneyBack, 0)));
return true;
}
@Override
public boolean checkInactive() {
if(isDeleted() || !isSold()) {
return false;
}
long inactiveSetting = getInactiveTimeUntilSell();
OfflinePlayer player = Bukkit.getOfflinePlayer(getBuyer());
if(inactiveSetting <= 0 || player.isOp()) {
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(now > (lastPlayed + inactiveSetting)) {
AreaShop.info("Region " + getName() + " unrented because of inactivity for player " + getPlayerName());
AreaShop.debug("currentTime=" + now + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (now - player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
return this.sell(true, null);
}
return false;
}
// Notify about updates
this.notifyAndUpdate(new SoldRegionEvent(this, oldBuyer, Math.max(moneyBack, 0)));
return true;
}
@Override
public boolean handleInactivity() {
return this.sell(true, null);
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,240 @@
package me.wiefferink.areashop.regions;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.configuration.file.YamlConfiguration;
import java.util.UUID;
public abstract class TransactionalRegion extends GeneralRegion {
private final String key;
private final RegionType type;
private UUID transactionHolder;
private String transactionHolderName;
private double price;
private double moneyBackPercentage;
private long inactivityThresholdMillis;
public TransactionalRegion(YamlConfiguration config, RegionType regionType) {
super(config);
this.type = regionType;
this.key = regionType.name().toLowerCase() + ".";
this.moneyBackPercentage = config.getDouble(this.key + "moneyBack");
}
public TransactionalRegion(String name, World world, RegionType type) {
super(name, world);
this.type = type;
this.key = type.name().toLowerCase() + ".";
}
protected final void setInactivityThresholdMillis(long inactivityThresholdMillis) {
this.inactivityThresholdMillis = inactivityThresholdMillis;
}
@Override
public RegionType getType() {
return type;
}
@Override
public boolean isAvailable() {
return !isSold();
}
/**
* Get the UUID of the owner of this region.
*
* @return The UUID of the owner of this region
*/
public UUID getTransactionHolder() {
return transactionHolder;
}
/**
* Set the buyer of this region. WARNING - May trigger disk lookup for offline players!
*
* @param transactionHolder The UUID of the player that should be set as buyer
*/
public void setTransactionHolder(UUID transactionHolder) {
if (transactionHolder == null) {
this.transactionHolder = null;
this.transactionHolderName = null;
} else {
this.transactionHolder = transactionHolder;
String name = Utils.toName(transactionHolder);
this.transactionHolderName = name.isEmpty() ? null : name;
}
}
/**
* Set the buyer of this region.
*
* @param player The instance of the player.
*/
public void setTransactionHolder(final OfflinePlayer player) {
if (player == null) {
this.transactionHolder = null;
this.transactionHolderName = null;
} else {
String name = player.getName();
this.transactionHolderName = name == null || name.isEmpty() ? null : name;
}
}
@Override
public void setOwner(UUID player) {
setTransactionHolder(player);
}
/**
* Check if a player is the buyer/holder of this region.
*
* @param player Player to check
* @return true if this player owns this region, otherwise false
*/
public boolean isTransactionHolder(OfflinePlayer player) {
return player != null && player.getUniqueId().equals(transactionHolder);
}
public boolean isTransactionHolder(UUID player) {
UUID buyer = getTransactionHolder();
return !(buyer == null || player == null) && buyer.equals(player);
}
/**
* Get the name of the player that owns this region.
*
* @return The name of the player that owns this region, if unavailable by UUID it will return the old cached name, if that is unavailable it will return &lt;UNKNOWN&gt;
*/
public String getPlayerName() {
return this.transactionHolderName == null ? "<UNKNOWN>" : transactionHolderName;
}
/**
* Check if the region is sold.
*
* @return true if the region is sold, otherwise false
*/
public boolean isSold() {
return this.transactionHolder != null;
}
/**
* Get the price of the region.
*
* @return The price of the region
*/
public double getPrice() {
return price;
}
/**
* Change the price of the region.
*
* @param price The price to set this region to
*/
public void setPrice(double price) {
this.price = price;
super.config.set(key + "price", price);
}
/**
* Get the formatted string of the price (includes prefix and suffix).
*
* @return The formatted string of the price
*/
public String getFormattedPrice() {
return Utils.formatCurrency(getPrice());
}
/**
* Get the moneyBack percentage.
*
* @return The % of money the player will get back when selling
*/
public double getMoneyBackPercentage() {
return moneyBackPercentage;
}
/**
* Get the amount of money that should be paid to the player when selling the region.
*
* @return The amount of money the player should get back
*/
public double getMoneyBackAmount() {
return getPrice() * (moneyBackPercentage / 100D);
}
/**
* Get the formatted string of the amount of the moneyBack amount.
*
* @return String with currency symbols and proper fractional part
*/
public String getFormattedMoneyBackAmount() {
return Utils.formatCurrency(getMoneyBackAmount());
}
@Override
public Object provideReplacement(String variable) {
switch (variable) {
case AreaShop.tagPrice:
return getFormattedPrice();
case AreaShop.tagRawPrice:
return getPrice();
case AreaShop.tagPlayerName:
return getPlayerName();
case AreaShop.tagPlayerUUID:
return getTransactionHolder();
case AreaShop.tagMoneyBackAmount:
return getFormattedMoneyBackAmount();
case AreaShop.tagRawMoneyBackAmount:
return getMoneyBackAmount();
case AreaShop.tagMoneyBackPercentage:
return getMoneyBackPercentage() % 1.0 == 0.0 ? (int) getMoneyBackPercentage() : getMoneyBackPercentage();
case AreaShop.tagMaxInactiveTime:
return this.getFormattedInactivityThreshold();
default:
return super.provideReplacement(variable);
}
}
public long getInactivityThresholdMillis() {
return this.inactivityThresholdMillis;
}
/**
* Get a human readable string indicating how long the player can be offline until automatic unrent.
*
* @return String indicating the inactive time until unrent
*/
public String getFormattedInactivityThreshold() {
return Utils.millisToHumanFormat(this.inactivityThresholdMillis);
}
@Override
public boolean checkInactivity(final OfflinePlayer player) {
if (isDeleted() || !isSold()) {
return false;
}
long inactiveSetting = getInactivityThresholdMillis();
if (inactiveSetting <= 0) {
return false;
}
if (player.isOnline()) {
return false;
}
long lastPlayed = getLastActiveTime();
long now = System.currentTimeMillis();
if (now > (lastPlayed + inactiveSetting)) {
AreaShop.info("Region " + getName() + " unrented because of inactivity for player " + getPlayerName());
AreaShop.debug("currentTime=" + now + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (now - player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
return true;
}
return false;
}
}
@@ -4,140 +4,100 @@ import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import org.bstats.Metrics;
import org.bstats.bukkit.Metrics;
import java.util.HashMap;
import java.util.Map;
public class Analytics {
private Analytics() {
private Analytics() {
}
}
/**
* Start analytics tracking.
*/
public static void start() {
// bStats statistics
try {
Metrics metrics = new Metrics(AreaShop.getInstance());
/**
* Start analytics tracking.
*/
public static void start() {
// bStats statistics
try {
Metrics metrics = new Metrics(AreaShop.getInstance());
// Number of regions
metrics.addCustomChart(new Metrics.SingleLineChart("region_count") {
@Override
public int getValue() {
return AreaShop.getInstance().getFileManager().getRegions().size();
}
});
// Number of regions
metrics.addCustomChart(new Metrics.SingleLineChart("region_count", () -> AreaShop.getInstance().getFileManager().getRegions().size()));
// Number of rental regions
metrics.addCustomChart(new Metrics.SingleLineChart("rental_region_count") {
@Override
public int getValue() {
return AreaShop.getInstance().getFileManager().getRents().size();
}
});
// Number of rental regions
metrics.addCustomChart(new Metrics.SingleLineChart("rental_region_count", () -> {
return AreaShop.getInstance().getFileManager().getRents().size();
}));
// Number of buy regions
metrics.addCustomChart(new Metrics.SingleLineChart("buy_region_count") {
@Override
public int getValue() {
return AreaShop.getInstance().getFileManager().getBuys().size();
}
});
// Number of buy regions
metrics.addCustomChart(new Metrics.SingleLineChart("buy_region_count", () -> AreaShop.getInstance().getFileManager().getBuys().size()));
// Language
metrics.addCustomChart(new Metrics.SimplePie("language") {
@Override
public String getValue() {
return AreaShop.getInstance().getConfig().getString("language");
}
});
// Language
metrics.addCustomChart(new Metrics.SimplePie("language", () -> AreaShop.getInstance().getConfig().getString("language")));
// Pie with region states
metrics.addCustomChart(new Metrics.AdvancedPie("region_state") {
@Override
public HashMap<String, Integer> getValues(HashMap<String, Integer> result) {
RegionStateStats stats = getStateStats();
result.put("For Rent", stats.forrent);
result.put("Rented", stats.rented);
result.put("For Sale", stats.forsale);
result.put("Sold", stats.sold);
result.put("Reselling", stats.reselling);
return result;
}
});
// Pie with region states
metrics.addCustomChart(new Metrics.AdvancedPie("region_state", () -> {
Map<String, Integer> map = new HashMap<>();
RegionStateStats stats = getStateStats();
map.put("For Rent", stats.forrent);
map.put("Rented", stats.rented);
map.put("For Sale", stats.forsale);
map.put("Sold", stats.sold);
map.put("Reselling", stats.reselling);
return map;
}));
// Time series of each region state
metrics.addCustomChart(new Metrics.SingleLineChart("forrent_region_count") {
@Override
public int getValue() {
return getStateStats().forrent;
}
});
metrics.addCustomChart(new Metrics.SingleLineChart("rented_region_count") {
@Override
public int getValue() {
return getStateStats().rented;
}
});
metrics.addCustomChart(new Metrics.SingleLineChart("forsale_region_count") {
@Override
public int getValue() {
return getStateStats().forsale;
}
});
metrics.addCustomChart(new Metrics.SingleLineChart("sold_region_count") {
@Override
public int getValue() {
return getStateStats().sold;
}
});
metrics.addCustomChart(new Metrics.SingleLineChart("reselling_region_count") {
@Override
public int getValue() {
return getStateStats().reselling;
}
});
// Time series of each region state
metrics.addCustomChart(new Metrics.SingleLineChart("forrent_region_count", () -> getStateStats().forrent));
// TODO track rent/buy/unrent/sell/resell actions (so that it can be reported per collection interval)
metrics.addCustomChart(new Metrics.SingleLineChart("rented_region_count", () -> getStateStats().rented));
AreaShop.debug("Started bstats.org statistics service");
} catch(Exception e) {
AreaShop.debug("Could not start bstats.org statistics service");
}
}
metrics.addCustomChart(new Metrics.SingleLineChart("forsale_region_count", () -> getStateStats().forsale));
private static class RegionStateStats {
int forrent = 0;
int forsale = 0;
int rented = 0;
int sold = 0;
int reselling = 0;
}
metrics.addCustomChart(new Metrics.SingleLineChart("sold_region_count", () -> getStateStats().sold));
private static RegionStateStats getStateStats() {
RegionStateStats result = new RegionStateStats();
for(GeneralRegion region : AreaShop.getInstance().getFileManager().getRegions()) {
if(region instanceof RentRegion) {
RentRegion rent = (RentRegion)region;
if(rent.isAvailable()) {
result.forrent++;
} else {
result.rented++;
}
} else if(region instanceof BuyRegion) {
BuyRegion buy = (BuyRegion)region;
if(buy.isAvailable()) {
result.forsale++;
} else if(buy.isInResellingMode()) {
result.reselling++;
} else {
result.sold++;
}
}
}
return result;
}
metrics.addCustomChart(new Metrics.SingleLineChart("reselling_region_count", () -> getStateStats().reselling));
// TODO track rent/buy/unrent/sell/resell actions (so that it can be reported per collection interval)
AreaShop.debug("Started bstats.org statistics service");
} catch (Exception e) {
AreaShop.debug("Could not start bstats.org statistics service");
}
}
private static RegionStateStats getStateStats() {
RegionStateStats result = new RegionStateStats();
for (GeneralRegion region : AreaShop.getInstance().getFileManager().getRegions()) {
if (region instanceof RentRegion) {
RentRegion rent = (RentRegion) region;
if (rent.isAvailable()) {
result.forrent++;
} else {
result.rented++;
}
} else if (region instanceof BuyRegion) {
BuyRegion buy = (BuyRegion) region;
if (buy.isAvailable()) {
result.forsale++;
} else if (buy.isInResellingMode()) {
result.reselling++;
} else {
result.sold++;
}
}
}
return result;
}
private static class RegionStateStats {
int forrent = 0;
int forsale = 0;
int rented = 0;
int sold = 0;
int reselling = 0;
}
}
@@ -0,0 +1,28 @@
package me.wiefferink.areashop.tools;
import org.bukkit.Chunk;
public class ChunkUtils {
private final long twoPower32 = Integer.MAX_VALUE + 1L;
public static long getPackedChunkCoords(Chunk chunk) {
return packToLong(chunk.getX(), chunk.getZ());
}
public static long packToLong(int primary, int secondary) {
return (long) primary & 0xffffffffL | ((long) secondary & 0xffffffffL) << 32;
}
public static int getChunkX(long packed) {
return (int) packed;
}
public static int getChunkZ(long packed) {
return (int) (packed >> 32);
}
public static int[] getChunkCoords(long packed) {
return new int[]{(int) packed, (int) (packed >> 32)};
}
}
@@ -126,7 +126,7 @@ public class Materials {
* @return true if the given material is a sign
*/
public static boolean isSign(String name) {
return name != null && (FLOOR_SIGN_TYPES.contains(name) || WALL_SIGN_TYPES.contains(name));
return name != null && name.toUpperCase().contains("SIGN");
}
}
@@ -734,6 +734,7 @@ 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);
@@ -749,6 +750,7 @@ 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);
@@ -835,6 +837,22 @@ public class Utils {
return "";
}
public static CompletableFuture<String> toNameAsync(UUID uuid) {
if (uuid == null) {
return CompletableFuture.completedFuture("");
}
CompletableFuture<String> completableFuture = new CompletableFuture<>();
Bukkit.getScheduler().runTaskAsynchronously(AreaShop.getInstance(), () -> {
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
if (!offlinePlayer.hasPlayedBefore()) {
completableFuture.complete("");
} else {
completableFuture.complete(offlinePlayer.getName());
}
});
return completableFuture;
}
/**
* Conversion from name to uuid.
*
@@ -861,7 +879,7 @@ 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: " + batchSize);
throw new IllegalArgumentException("Invalid batch size!");
}
final List<E> cloned = new ArrayList<>(objects);
final CompletableFuture<Object> future = new CompletableFuture<>();
@@ -871,14 +889,12 @@ public class Utils {
@Override
public void run() {
if (lastIndex >= cloned.size() - 1) {
cancel();
future.complete(true);
cancel();
return;
}
int index = Math.min(lastIndex + batchSize, cloned.size() - 1);
for (; lastIndex < index; lastIndex++) {
task.accept(cloned.get(lastIndex));
}
int index = Math.min(batchSize, objects.size() - 1);
cloned.subList(lastIndex, index).forEach(task);
}
};
if (async) {
+181 -181
View File
@@ -1,181 +1,181 @@
# ╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
# ║ config.yml file of the AreaShop plugin created by NLThijs48, Github can be found at https://github.com/NLthijs48/AreaShop. ║
# ║ This file contains options to change the working of the plugin, the profiles defined here can be used in default.yml. ║
# ║ More information and tutorials can be found on the wiki: https://github.com/NLthijs48/AreaShop/wiki ║
# ╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ GENERAL: Options that influence the global state of the plugin │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# Chat prefix used for all messages in the chat, also changes the greeting messages.
chatPrefix:
- '[darkgreen][AreaShop][reset]'
- ' hover: AreaShop region management plugin'
- ' hover: %lang:action|Click to check the available commands|%'
- ' command: /areashop help'
- ' '
# The language file that should be used, check the 'lang' folder for build-in languages (use the filename without .yml here).
# More information can be found here: https://github.com/NLthijs48/AreaShop/wiki/Language-support.
language: EN
# The tags that need to be written on the first line of a sign to add it to AreaShop.
signTags:
# Add a rental region.
rent: '[asrent]'
# Add a buy region.
buy: '[asbuy]'
# Add a sign to an existing region.
add: '[as]'
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ ECONOMY: Options to change the display of prices. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# The characters displayed before a price.
moneyCharacter: '$'
# The characters displayed after a price.
moneyCharacterAfter: ''
# The character(s) to use as decimal mark.
decimalMark: '.'
# How many numbers behind the dot should be shown (2 will make numbers like '8.55', '9.01', '5.20').
fractionalNumbers: 2
# How many numbers behind the dot should be shown when using a metric symbol suffix.
fractionalNumbersShort: 3
# Set this to true if you want to hide '.0' for a number like '15.0' ('4.50' will still stay '4.50').
hideEmptyFractionalPart: true
# Use a suffix if the price is above a certain number (use 1.00M instead of 1000000.00 etc.)
metricSymbols:
#1000: "k"
1000000: "M"
1000000000: "G"
1000000000000: "T"
1000000000000000: "P"
1000000000000000000: "E"
1000000000000000000000: "Z"
1000000000000000000000000: "Y"
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ RENTING: Options that apply to all rent regions. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# Timeformat to use on the signs, default is like '30-06 14:52', US format: 'MM-dd KK:mm a'.
# Search for 'java SimpleDateFormat' for more options and documentation.
timeFormatSign: 'dd-MM HH:mm'
# Timeformat used in the chat, default is like '30 june 2014 14:52', US format: 'MMMMMMMMMMMMMMMMM dd yyyy KK:mm a'.
# Search for 'java SimpleDateFormat' for more options and documentation.
timeFormatChat: 'dd MMMMMMMMMMMMMMMMM yyyy HH:mm'
# Time indicators, used for specifing time periods (for example rent duration).
seconds: [s, sec, secs, second, seconds]
minutes: [m, min, mins, minute, minutes]
hours: [h, hour, hours]
days: [d, day, days]
weeks: [w, week, weeks]
months: [M, month, months]
years: [y, year, years]
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ PERMISSION GROUPS: Assigned by giving players certain permissions. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# Configure the max total regions, max rent regions and max buy regions with different groups (-1 is unlimited).
# Assign a group to players by giving them the permission 'areashop.limits.<group>'.
# Every player already has the group 'default', OPs bypass the limits because of the 'areashop.limitbypass' permission.
# All the groups applied to the player will be checked and only if they all allow an extra region it will be allowed.
# More information like limits for regions of a certain group or in a certain word can be found here:
# https://github.com/NLthijs48/AreaShop/wiki/Limitgroups-information-and-examples.
limitGroups:
default:
total: 1
rents: 1
buys: 1
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ ADVANCED AND DEBUG: Specific options to tweak the plugin precisely and check if it functions correctly. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# Blacklist of region names that cannot be added to AreaShop for renting or buying. Regular expressions can be used, search
# for 'java regex pattern' to find documentation about how to use it.
# Example: Blocking all regions that have a name like 'house_1', 'house_2', etc. would be done with 'house_\d+'.
# The '\d' represents the numerical digits 0-9, the + symbol represents 1 or more times repeated.
# This means it would also block 'house_123' and 'house_000456'. It will not block 'ahouse_1' or 'house_'.
blacklist:
- '__global__'
# Minimum length of the numbers that are suffixed for region names generated by the '/as stack' command.
# When having this at 3 it will generate names like this: 'region-001', 'region-014', 'region-4567'.
stackRegionNumberLength: 3
# Allow/disallow adding players that did not visit the server yet as friend of a region.
addFriendNotExistingPlayers: false
# Enable sending stats to http://mcstats.org/ (Metrics plugin).
# This information will give me an indication about how much the plugin is used and encourages me to continue development.
sendStats: true
# If enabled it will check for updates when loading the plugin, it will never download files, it will only notify about it.
checkForUpdates: true
# Use colors when sending messages to console and log files.
useColorsInConsole: false
# Use tellraw style messages, which can be interacted with by players
useFancyMessages: true
# Update all region flags and signs after starting the plugin.
# This ensures that changes to the config are directly visible after restarting the server.
updateRegionsOnStartup: true
# Enables / disables debug messages in the console, could be useful to figure out where errors come from.
debug: false
# Version of the config, do not change!
version: ${project.version}
# Maximum number of blocks to save to or restore from a .schemetic file.
maximumBlocks: 1000000
# Maximum number of locations the teleport function should check to find a safe spot.
maximumTries: 50000
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ VARIABLES: Variables that can be used in messages and settings where a region is available. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# If a tag has no value (for example %player% when the region is not rented/bought) then the tag will not get replaced.
# %region% The region name (with correct capitalization).
# %player% The name of the player that rents/buys the region (with correct capitalization).
# %uuid% The UUID of the player that rents/buys the region.
# %friends% The names of friends added to this region separated by ', '.
# %friendsuuid% The UUID's of friends added to this region separated by ', '.
# %landlord% The name of the landlord.
# %landlorduuid% The UUID of the landlord.
# %price% The price of the region formatted with the configured characters before and after the number.
# %rawprice% The price without formatting, like '10.0' or '7.77'.
# %duration% The duration of a rent region, for example '1 d', '4 minutes' or '2 years'.
# %world% The name of the world that the region is in.
# %type% The type of the region, 'rent' or 'buy'.
# %until% The end time of a rent formatted as configured with 'timeFormatChat'.
# %untilshort% The end time of a rent formatted as configured with 'timeFormatSign'.
# %width% The width of the region (number of blocks on the x-axis).
# %depth% The depth of the region (number of blocks on the z-axis).
# %volume% The number of blocks in a region (accounting for polygon shaped regions).
# %height% The height of the region (number of blocks on the y-axis).
# %timeleft% The time left on the rent (uses the unit of time that fits best, minutes used from 121 seconds till 120 minutes).
# %clicker% The name of the player that clicked the sign (only to be used in the signProfiles section).
# %resellprice% The price of a region when it is in resell mode.
# %rawresellprice% The resellprice without formatting.
# %moneyback% The amount of money the player will get back when unrenting/selling the region (formatted with currency layout).
# %rawmoneyback% The moneyback without formatting.
# %moneybackpercent% The percentage of the price the player will get back when unrenting the region.
# %timesExtended% The number of times the region has been extended (zero when first rented)
# %maxextends% The maximum number of extends a player can do on the region.
# %extendsleft% The number of extends that are left (maxextends - timesextended).
# %maxrenttime% The maximum time you can rent a region in advance (human readable).
# %inactivetime% The maximum time a player may be inactive before unrent/sell (human readable).
# %year% The current year.
# %month% The current month in the year.
# %day% The current day in the month.
# %hour% The current hour in the day (0-23).
# %minute% The current minute of the hour.
# %second% The current second of the minute.
# %millisecond% The current millisecond of the second.
# %epoch% The number of milliseconds since January 1, 1970 (Unix Epoch).
# %tpx% The exact x coordinate of the location that the teleport is set to
# %tpy% The exact y coordinate of the location that the teleport is set to
# %tpz% The exact z coordinate of the location that the teleport is set to
# %tpblockx% The rounded x coordinate of the location that the teleport is set to
# %tpblocky% The rounded y coordinate of the location that the teleport is set to
# %tpblockz% The rounded z coordinate of the location that the teleport is set to
# %tppitch% The exact pitch of the teleport location
# %tppitchround% The rounded pitch of the teleport location
# %tpyaw% The exact yaw of the teleport location
# %tpyawround% The rounded yaw of the teleport location
# %tpworld% The world name of the teleport location
# %lang:<languageKey>|Optional arguments|% Insert a message from EN.yml, for example: '%lang:timeleft-ended%' or '%lang:timeleft-years|15|%'
# ╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
# ║ config.yml file of the AreaShop plugin created by NLThijs48, Github can be found at https://github.com/NLthijs48/AreaShop. ║
# ║ This file contains options to change the working of the plugin, the profiles defined here can be used in default.yml. ║
# ║ More information and tutorials can be found on the wiki: https://github.com/NLthijs48/AreaShop/wiki ║
# ╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ GENERAL: Options that influence the global state of the plugin │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# Chat prefix used for all messages in the chat, also changes the greeting messages.
chatPrefix:
- '[darkgreen][AreaShop][reset]'
- ' hover: AreaShop region management plugin'
- ' hover: %lang:action|Click to check the available commands|%'
- ' command: /areashop help'
- ' '
# The language file that should be used, check the 'lang' folder for build-in languages (use the filename without .yml here).
# More information can be found here: https://github.com/NLthijs48/AreaShop/wiki/Language-support.
language: EN
# The tags that need to be written on the first line of a sign to add it to AreaShop.
signTags:
# Add a rental region.
rent: '[asrent]'
# Add a buy region.
buy: '[asbuy]'
# Add a sign to an existing region.
add: '[as]'
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ ECONOMY: Options to change the display of prices. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# The characters displayed before a price.
moneyCharacter: '$'
# The characters displayed after a price.
moneyCharacterAfter: ''
# The character(s) to use as decimal mark.
decimalMark: '.'
# How many numbers behind the dot should be shown (2 will make numbers like '8.55', '9.01', '5.20').
fractionalNumbers: 2
# How many numbers behind the dot should be shown when using a metric symbol suffix.
fractionalNumbersShort: 3
# Set this to true if you want to hide '.0' for a number like '15.0' ('4.50' will still stay '4.50').
hideEmptyFractionalPart: true
# Use a suffix if the price is above a certain number (use 1.00M instead of 1000000.00 etc.)
metricSymbols:
#1000: "k"
1000000: "M"
1000000000: "G"
1000000000000: "T"
1000000000000000: "P"
1000000000000000000: "E"
1000000000000000000000: "Z"
1000000000000000000000000: "Y"
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ RENTING: Options that apply to all rent regions. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# Timeformat to use on the signs, default is like '30-06 14:52', US format: 'MM-dd KK:mm a'.
# Search for 'java SimpleDateFormat' for more options and documentation.
timeFormatSign: 'dd-MM HH:mm'
# Timeformat used in the chat, default is like '30 june 2014 14:52', US format: 'MMMMMMMMMMMMMMMMM dd yyyy KK:mm a'.
# Search for 'java SimpleDateFormat' for more options and documentation.
timeFormatChat: 'dd MMMMMMMMMMMMMMMMM yyyy HH:mm'
# Time indicators, used for specifing time periods (for example rent duration).
seconds: [s, sec, secs, second, seconds]
minutes: [m, min, mins, minute, minutes]
hours: [h, hour, hours]
days: [d, day, days]
weeks: [w, week, weeks]
months: [M, month, months]
years: [y, year, years]
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ PERMISSION GROUPS: Assigned by giving players certain permissions. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# Configure the max total regions, max rent regions and max buy regions with different groups (-1 is unlimited).
# Assign a group to players by giving them the permission 'areashop.limits.<group>'.
# Every player already has the group 'default', OPs bypass the limits because of the 'areashop.limitbypass' permission.
# All the groups applied to the player will be checked and only if they all allow an extra region it will be allowed.
# More information like limits for regions of a certain group or in a certain word can be found here:
# https://github.com/NLthijs48/AreaShop/wiki/Limitgroups-information-and-examples.
limitGroups:
default:
total: 1
rents: 1
buys: 1
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ ADVANCED AND DEBUG: Specific options to tweak the plugin precisely and check if it functions correctly. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# Blacklist of region names that cannot be added to AreaShop for renting or buying. Regular expressions can be used, search
# for 'java regex pattern' to find documentation about how to use it.
# Example: Blocking all regions that have a name like 'house_1', 'house_2', etc. would be done with 'house_\d+'.
# The '\d' represents the numerical digits 0-9, the + symbol represents 1 or more times repeated.
# This means it would also block 'house_123' and 'house_000456'. It will not block 'ahouse_1' or 'house_'.
blacklist:
- '__global__'
# Minimum length of the numbers that are suffixed for region names generated by the '/as stack' command.
# When having this at 3 it will generate names like this: 'region-001', 'region-014', 'region-4567'.
stackRegionNumberLength: 3
# Allow/disallow adding players that did not visit the server yet as friend of a region.
addFriendNotExistingPlayers: false
# Enable sending stats to http://mcstats.org/ (Metrics plugin).
# This information will give me an indication about how much the plugin is used and encourages me to continue development.
sendStats: true
# If enabled it will check for updates when loading the plugin, it will never download files, it will only notify about it.
checkForUpdates: true
# Use colors when sending messages to console and log files.
useColorsInConsole: false
# Use tellraw style messages, which can be interacted with by players
useFancyMessages: true
# Update all region flags and signs after starting the plugin.
# This ensures that changes to the config are directly visible after restarting the server.
updateRegionsOnStartup: true
# Enables / disables debug messages in the console, could be useful to figure out where errors come from.
debug: false
# Version of the config, do not change!
version: ${project.version}
# Maximum number of blocks to save to or restore from a .schemetic file.
maximumBlocks: 1000000
# Maximum number of locations the teleport function should check to find a safe spot.
maximumTries: 50000
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
# │ VARIABLES: Variables that can be used in messages and settings where a region is available. │
# └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# If a tag has no value (for example %player% when the region is not rented/bought) then the tag will not get replaced.
# %region% The region name (with correct capitalization).
# %player% The name of the player that rents/buys the region (with correct capitalization).
# %uuid% The UUID of the player that rents/buys the region.
# %friends% The names of friends added to this region separated by ', '.
# %friendsuuid% The UUID's of friends added to this region separated by ', '.
# %landlord% The name of the landlord.
# %landlorduuid% The UUID of the landlord.
# %price% The price of the region formatted with the configured characters before and after the number.
# %rawprice% The price without formatting, like '10.0' or '7.77'.
# %duration% The duration of a rent region, for example '1 d', '4 minutes' or '2 years'.
# %world% The name of the world that the region is in.
# %type% The type of the region, 'rent' or 'buy'.
# %until% The end time of a rent formatted as configured with 'timeFormatChat'.
# %untilshort% The end time of a rent formatted as configured with 'timeFormatSign'.
# %width% The width of the region (number of blocks on the x-axis).
# %depth% The depth of the region (number of blocks on the z-axis).
# %volume% The number of blocks in a region (accounting for polygon shaped regions).
# %height% The height of the region (number of blocks on the y-axis).
# %timeleft% The time left on the rent (uses the unit of time that fits best, minutes used from 121 seconds till 120 minutes).
# %clicker% The name of the player that clicked the sign (only to be used in the signProfiles section).
# %resellprice% The price of a region when it is in resell mode.
# %rawresellprice% The resellprice without formatting.
# %moneyback% The amount of money the player will get back when unrenting/selling the region (formatted with currency layout).
# %rawmoneyback% The moneyback without formatting.
# %moneybackpercent% The percentage of the price the player will get back when unrenting the region.
# %timesExtended% The number of times the region has been extended (zero when first rented)
# %maxextends% The maximum number of extends a player can do on the region.
# %extendsleft% The number of extends that are left (maxextends - timesextended).
# %maxrenttime% The maximum time you can rent a region in advance (human readable).
# %inactivetime% The maximum time a player may be inactive before unrent/sell (human readable).
# %year% The current year.
# %month% The current month in the year.
# %day% The current day in the month.
# %hour% The current hour in the day (0-23).
# %minute% The current minute of the hour.
# %second% The current second of the minute.
# %millisecond% The current millisecond of the second.
# %epoch% The number of milliseconds since January 1, 1970 (Unix Epoch).
# %tpx% The exact x coordinate of the location that the teleport is set to
# %tpy% The exact y coordinate of the location that the teleport is set to
# %tpz% The exact z coordinate of the location that the teleport is set to
# %tpblockx% The rounded x coordinate of the location that the teleport is set to
# %tpblocky% The rounded y coordinate of the location that the teleport is set to
# %tpblockz% The rounded z coordinate of the location that the teleport is set to
# %tppitch% The exact pitch of the teleport location
# %tppitchround% The rounded pitch of the teleport location
# %tpyaw% The exact yaw of the teleport location
# %tpyawround% The rounded yaw of the teleport location
# %tpworld% The world name of the teleport location
# %lang:<languageKey>|Optional arguments|% Insert a message from EN.yml, for example: '%lang:timeleft-ended%' or '%lang:timeleft-years|15|%'
-1
View File
@@ -343,7 +343,6 @@ 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."
+276 -273
View File
@@ -1,273 +1,276 @@
name: AreaShop
main: me.wiefferink.areashop.AreaShop
version: "${project.version}"
description: "Selling and renting WorldGuard regions to your players, highly configurable."
depend: [Vault, WorldGuard, WorldEdit]
softdepend: [Multiverse-Core, FastAsyncWorldEdit]
author: NLThijs48
website: https://github.com/NLthijs48/AreaShop
commands:
AreaShop:
description: 'For all commands use /as help.'
usage: 'AreaShop has not started correctly, check the log file (or console) and correct any errors.'
aliases: [as]
awareness:
- !@UTF8
api-version: 1.13
permissions:
areashop.*:
description: Give access to all commands of AreaShop
children:
areashop.help: true
areashop.createrent: true
areashop.createbuy: true
areashop.destroyrent: true
areashop.destroybuy: true
areashop.rent: true
areashop.buy: true
areashop.buyresell: true
areashop.buynormal: true
areashop.unrent: true
areashop.info: true
areashop.reload: true
areashop.setrestore: true
areashop.setprice: true
areashop.setduration: true
areashop.teleport: true
areashop.teleportall: true
areashop.setteleportall: true
areashop.teleportsign: true
areashop.teleportsignall: true
areashop.tabcomplete: true
areashop.setteleportoutsideregion: true
areashop.find: true
areashop.groupadd: true
areashop.groupdel: true
areashop.grouplist: true
areashop.groupinfo: true
areashop.limitbypass: true
areashop.schematicevents: true
areashop.addsign: true
areashop.delsign: true
areashop.renttimebypass: true
areashop.me: true
areashop.resellall: true
areashop.stopresellall: true
areashop.addfriendall: true
areashop.delfriendall: true
areashop.notifyupdate: true
areashop.stack: true
areashop.setlandlord: true
areashop.linksigns: true
areashop.message: true
areashop.import: true
areashop.landlord.*:
description: Give access to all landlord features
children:
areashop.createrent.owner: true
areashop.createbuy.owner: true
areashop.setprice.landlord: true
areashop.setduration.landlord: true
areashop.destroyrent.landlord: true
areashop.destroybuy.landlord: true
areashop.help:
description: Allows you to see the help pages
default: true
areashop.rent:
description: Allows you to rent a region
default: true
areashop.buy:
description: Allows you to buy a region
default: true
areashop.buyresell:
description: Allows you to buy a region that is in resell mode
default: true
areashop.buynormal:
description: Allows you to buy a region that is not in resell mode
default: true
areashop.unrent:
description: Allows you to unrent a specific region
default: op
areashop.unrentown:
description: Allows you to unrent your own region
default: true
areashop.sell:
description: Allows you to sell a specific region
default: op
areashop.sellown:
description: Allows you to sell your own region
default: true
areashop.createrent:
description: Allows you to add rent regions to AreaShop
default: op
areashop.createrent.member:
description: Allows you to add rent regions to AreaShop for which you are registered as member
default: false
areashop.createrent.owner:
description: Allows you to add rent regions to AreaShop for which you are registered as owner
default: false
areashop.createbuy:
description: Allows you to add buy regions to AreaShop
default: op
areashop.createbuy.member:
description: Allows you to add buy regions to AreaShop for which you are registered as member
default: false
areashop.createbuy.owner:
description: Allows you to add buy regions to AreaShop for which you are registered as owner
default: false
areashop.destroyrent:
description: Allows you remove rental regions from AreaShop
default: op
areashop.destroyrent.landlord:
description: Allows you to remove rental regions from AreaShop when you are the landlord
default: false
areashop.destroybuy:
description: Allows you to remove buy regions from AreaShop
default: op
areashop.destroybuy.landlord:
description: Allows you to remove buy regions from AreaShop when you are the landlord
default: false
areashop.info:
description: Allows you check the status of regions and players
default: true
areashop.reload:
description: Allows you to reload the config and update all region flags and signs
default: op
areashop.setrestore:
description: Allows you to change the restore settings
default: op
areashop.setprice:
description: Allows you to change the price
default: op
areashop.setprice.landlord:
description: Allows you to change the price of a region that you are landlord of
default: false
areashop.setduration:
description: Allows you to change the duration of a rent
default: op
areashop.setduration.landlord:
description: Allows you to change the duration of a region that you are landlord of
default: false
areashop.teleport:
description: Teleport to your region
default: true
areashop.teleportall:
description: Teleport to all regions
default: op
areashop.teleportsign:
description: Teleport to signs of regions you own
default: true
areashop.teleportsignall:
description: Teleport to signs of all regions
default: op
areashop.teleportfriend:
description: Teleport to regions where you are added as friend
default: true
areashop.teleportfriendsign:
description: Teleport to the sign of regions where you are added as friend
default: true
areashop.teleportavailable:
description: Teleport to regions which are available
default: true
areashop.teleportavailablesign:
description: Teleport to the sign of regions which are available
default: true
areashop.setteleport:
description: Set the teleport spot for your region
default: true
areashop.setteleportall:
description: Set the teleport for other regions
default: op
areashop.setteleportoutsideregion:
description: Set the teleport location outside the WorldGuard region
default: op
areashop.tabcomplete:
description: Allows you to tabcomplete all commands (also where you dont have permission)
default: op
areashop.find:
description: Allows you to teleport to a free region (/as find)
default: true
areashop.groupadd:
description: Allows you to add regions to groups
default: op
areashop.groupdel:
description: Allows you to delete regions from groups
default: op
areashop.grouplist:
description: Allows you to list all the groups currently created
default: op
areashop.groupinfo:
description: Allows you to display information about a group (all members)
default: op
areashop.rentextendbypass:
description: Bypass the extend limit on rents
default: op
areashop.limits.default:
description: Assign limits as set in the 'default' group in the 'limitGroups' section of the config
default: true
areashop.limitbypass:
description: Bypass the limits for renting/buying regions (how many you can have at the same time)
default: op
areashop.schematicevents:
description: Allows you to trigger schematic events for regions
default: op
areashop.addsign:
description: Allows you to add signs to existing regions
default: op
areashop.delsign:
description: Allows you to delete signs from existing regions
default: op
areashop.renttimebypass:
description: Bypass the maximum rent time you can have in advance
default: op
areashop.me:
description: Allows you to get information about your regions
default: true
areashop.setownerrent:
description: Allows you to set the owner of rent regions
default: op
areashop.setownerbuy:
description: Allows you to set the owner of buy regions
default: op
areashop.resell:
description: Allows you to set your region to sell mode for selling to other players
default: true
areashop.resellall:
description: Allows you to set any region into selling mode
default: op
areashop.stopresell:
description: Allows you to set your region into normal mode if it is in sell mode
default: true
areashop.stopresellall:
description: Allows you to set any region into normal mode if it is in sell mode
default: op
areashop.addfriend:
description: Allows you to add friends to your regions
default: true
areashop.addfriendall:
description: Allows you to add friends to all regions
default: op
areashop.delfriend:
description: Allows you to delete friends from your regions
default: true
areashop.delfriendall:
description: Allows you to delete friends from all regions
default: op
areashop.notifyupdate:
description: Will notify this player when an update is available
default: op
areashop.linksigns:
description: Allows you to enter sign linking mode
default: op
areashop.stack:
description: Allows you to create regions and add them to AreaShop like the WorldEdit //stack command
default: op
areashop.setlandlord:
description: Allows you to set the landlord of a region, the landlord receives all revenue
default: op
areashop.message:
description: Allows you to send AreaShop messages to players
default: op
areashop.import:
description: Allows importing regions from other plugins
default: op
name: AreaShop
main: me.wiefferink.areashop.AreaShop
version: "${project.version}${build.number}"
description: "Selling and renting WorldGuard regions to your players, highly configurable."
depend: [Vault, WorldGuard, WorldEdit]
softdepend: [Multiverse-Core, FastAsyncWorldEdit]
author: NLThijs48
website: https://github.com/NLthijs48/AreaShop
commands:
AreaShop:
description: 'For all commands use /as help.'
usage: 'AreaShop has not started correctly, check the log file (or console) and correct any errors.'
aliases: [as]
test:
awareness:
- !@UTF8
api-version: 1.13
permissions:
areashop.*:
description: Give access to all commands of AreaShop
children:
areashop.help: true
areashop.createrent: true
areashop.createbuy: true
areashop.destroyrent: true
areashop.destroybuy: true
areashop.rent: true
areashop.buy: true
areashop.buyresell: true
areashop.buynormal: true
areashop.unrent: true
areashop.info: true
areashop.reload: true
areashop.setrestore: true
areashop.setprice: true
areashop.setduration: true
areashop.teleport: true
areashop.teleportall: true
areashop.setteleportall: true
areashop.teleportsign: true
areashop.teleportsignall: true
areashop.tabcomplete: true
areashop.setteleportoutsideregion: true
areashop.find: true
areashop.groupadd: true
areashop.groupdel: true
areashop.grouplist: true
areashop.groupinfo: true
areashop.limitbypass: true
areashop.schematicevents: true
areashop.addsign: true
areashop.delsign: true
areashop.renttimebypass: true
areashop.me: true
areashop.resellall: true
areashop.stopresellall: true
areashop.addfriendall: true
areashop.delfriendall: true
areashop.notifyupdate: true
areashop.stack: true
areashop.setlandlord: true
areashop.linksigns: true
areashop.message: true
areashop.import: true
areashop.landlord.*:
description: Give access to all landlord features
children:
areashop.createrent.owner: true
areashop.createbuy.owner: true
areashop.setprice.landlord: true
areashop.setduration.landlord: true
areashop.destroyrent.landlord: true
areashop.destroybuy.landlord: true
areashop.help:
description: Allows you to see the help pages
default: true
areashop.rent:
description: Allows you to rent a region
default: true
areashop.buy:
description: Allows you to buy a region
default: true
areashop.buyresell:
description: Allows you to buy a region that is in resell mode
default: true
areashop.buynormal:
description: Allows you to buy a region that is not in resell mode
default: true
areashop.unrent:
description: Allows you to unrent a specific region
default: op
areashop.unrentown:
description: Allows you to unrent your own region
default: true
areashop.sell:
description: Allows you to sell a specific region
default: op
areashop.sellown:
description: Allows you to sell your own region
default: true
areashop.createrent:
description: Allows you to add rent regions to AreaShop
default: op
areashop.createrent.member:
description: Allows you to add rent regions to AreaShop for which you are registered as member
default: false
areashop.createrent.owner:
description: Allows you to add rent regions to AreaShop for which you are registered as owner
default: false
areashop.createbuy:
description: Allows you to add buy regions to AreaShop
default: op
areashop.createbuy.member:
description: Allows you to add buy regions to AreaShop for which you are registered as member
default: false
areashop.createbuy.owner:
description: Allows you to add buy regions to AreaShop for which you are registered as owner
default: false
areashop.destroyrent:
description: Allows you remove rental regions from AreaShop
default: op
areashop.destroyrent.landlord:
description: Allows you to remove rental regions from AreaShop when you are the landlord
default: false
areashop.destroybuy:
description: Allows you to remove buy regions from AreaShop
default: op
areashop.destroybuy.landlord:
description: Allows you to remove buy regions from AreaShop when you are the landlord
default: false
areashop.info:
description: Allows you check the status of regions and players
default: true
areashop.reload:
description: Allows you to reload the config and update all region flags and signs
default: op
areashop.setrestore:
description: Allows you to change the restore settings
default: op
areashop.setprice:
description: Allows you to change the price
default: op
areashop.setprice.landlord:
description: Allows you to change the price of a region that you are landlord of
default: false
areashop.setduration:
description: Allows you to change the duration of a rent
default: op
areashop.setduration.landlord:
description: Allows you to change the duration of a region that you are landlord of
default: false
areashop.teleport:
description: Teleport to your region
default: true
areashop.teleportall:
description: Teleport to all regions
default: op
areashop.teleportsign:
description: Teleport to signs of regions you own
default: true
areashop.teleportsignall:
description: Teleport to signs of all regions
default: op
areashop.teleportfriend:
description: Teleport to regions where you are added as friend
default: true
areashop.teleportfriendsign:
description: Teleport to the sign of regions where you are added as friend
default: true
areashop.teleportavailable:
description: Teleport to regions which are available
default: true
areashop.teleportavailablesign:
description: Teleport to the sign of regions which are available
default: true
areashop.setteleport:
description: Set the teleport spot for your region
default: true
areashop.setteleportall:
description: Set the teleport for other regions
default: op
areashop.setteleportoutsideregion:
description: Set the teleport location outside the WorldGuard region
default: op
areashop.tabcomplete:
description: Allows you to tabcomplete all commands (also where you dont have permission)
default: op
areashop.find:
description: Allows you to teleport to a free region (/as find)
default: true
areashop.groupadd:
description: Allows you to add regions to groups
default: op
areashop.groupdel:
description: Allows you to delete regions from groups
default: op
areashop.grouplist:
description: Allows you to list all the groups currently created
default: op
areashop.groupinfo:
description: Allows you to display information about a group (all members)
default: op
areashop.rentextendbypass:
description: Bypass the extend limit on rents
default: op
areashop.limits.default:
description: Assign limits as set in the 'default' group in the 'limitGroups' section of the config
default: true
areashop.limitbypass:
description: Bypass the limits for renting/buying regions (how many you can have at the same time)
default: op
areashop.schematicevents:
description: Allows you to trigger schematic events for regions
default: op
areashop.addsign:
description: Allows you to add signs to existing regions
default: op
areashop.delsign:
description: Allows you to delete signs from existing regions
default: op
areashop.renttimebypass:
description: Bypass the maximum rent time you can have in advance
default: op
areashop.me:
description: Allows you to get information about your regions
default: true
areashop.setownerrent:
description: Allows you to set the owner of rent regions
default: op
areashop.setownerbuy:
description: Allows you to set the owner of buy regions
default: op
areashop.resell:
description: Allows you to set your region to sell mode for selling to other players
default: true
areashop.resellall:
description: Allows you to set any region into selling mode
default: op
areashop.stopresell:
description: Allows you to set your region into normal mode if it is in sell mode
default: true
areashop.stopresellall:
description: Allows you to set any region into normal mode if it is in sell mode
default: op
areashop.addfriend:
description: Allows you to add friends to your regions
default: true
areashop.addfriendall:
description: Allows you to add friends to all regions
default: op
areashop.delfriend:
description: Allows you to delete friends from your regions
default: true
areashop.delfriendall:
description: Allows you to delete friends from all regions
default: op
areashop.notifyupdate:
description: Will notify this player when an update is available
default: op
areashop.linksigns:
description: Allows you to enter sign linking mode
default: op
areashop.stack:
description: Allows you to create regions and add them to AreaShop like the WorldEdit //stack command
default: op
areashop.setlandlord:
description: Allows you to set the landlord of a region, the landlord receives all revenue
default: op
areashop.message:
description: Allows you to send AreaShop messages to players
default: op
areashop.import:
description: Allows importing regions from other plugins
default: op
+5 -5
View File
@@ -25,20 +25,20 @@
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.14.4-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.intellectualsites.fawe</groupId>
<artifactId>FAWE-Bukkit</artifactId>
<version>1.16-380</version> <!-- Equivalent to WE 7.0.2? -->
<version>1.16-313</version> <!-- Equivalent to WE 7.0.2? -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId>
<version>7.0.3</version>
<version>7.0.3</version>
<scope>provided</scope>
</dependency>
@@ -46,7 +46,7 @@
<groupId>me.wiefferink</groupId>
<artifactId>areashop-interface</artifactId>
<version>latest</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -2,15 +2,13 @@ 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;
@@ -23,7 +21,6 @@ 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;
@@ -32,10 +29,8 @@ 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
@@ -46,38 +41,9 @@ public class FastAsyncWorldEditHandler extends WorldEditInterface {
super(pluginInterface);
}
private static class RegionBoundMask implements Mask {
private final BoundingBox boundingBox;
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
@Beta
public CompletableFuture<Boolean> restoreRegionBlocksAsync(File rawFile, GeneralRegionInterface regionInterface) {
public boolean restoreRegionBlocks(File rawFile, GeneralRegionInterface regionInterface) {
// TODO implement using the FastAsyncWorldEdit api to paste async
File file = null;
ClipboardFormat format = null;
for (ClipboardFormat formatOption : BuiltInClipboardFormat.values()) {
@@ -92,7 +58,7 @@ 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 CompletableFuture.completedFuture(false);
return false;
}
pluginInterface.debugI("Trying to restore region", regionInterface.getName(), " from file",
file.getAbsolutePath(), "with format", format.getName());
@@ -105,96 +71,93 @@ public class FastAsyncWorldEditHandler extends WorldEditInterface {
pluginInterface.getLogger().info(
"Did not restore region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return CompletableFuture.completedFuture(false);
return 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).relightMode(RelightMode.OPTIMAL).build();
EditSession editSession = FaweAPI.getEditSessionBuilder(world).limit(limit).build();
editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE);
ProtectedRegion region = regionInterface.getRegion();
final RegionType regionType = region.getType();
// Get the origin and size of the region
final BlockVector3 origin = BlockVector3
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);
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);
}
clipboard.setOrigin(clipboard.getMinimumPoint());
ClipboardHolder clipboardHolder = new ClipboardHolder(clipboard);
session.setBlockChangeLimit(limit.MAX_CHANGES);
session.setClipboard(clipboardHolder);
// Build operation
// 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);
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) {
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(
"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);
"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());
}
editSession.flushQueue();
CompletableFuture.completedFuture(true);
});
return completableFuture;
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(new Mask() {
@Override
public boolean test(BlockVector3 vector) {
return region.contains(vector);
}
@Override
public Mask2D toMask2D() {
return null;
}
});
}
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;
}
@Override
public boolean restoreRegionBlocks(File rawFile, GeneralRegionInterface regionInterface) {
return restoreRegionBlocksAsync(rawFile, regionInterface).join();
}
@Override
@Beta
public CompletableFuture<Boolean> saveRegionBlocksAsync(File file, GeneralRegionInterface regionInterface) {
public boolean saveRegionBlocks(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)
@@ -210,62 +173,50 @@ public class FastAsyncWorldEditHandler extends WorldEditInterface {
pluginInterface.getLogger().warning(
"Did not save region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return CompletableFuture.completedFuture(false);
return false;
}
final FaweLimit limit = new FaweLimit();
limit.MAX_CHANGES = pluginInterface.getConfig().getInt("maximumBlocks");
EditSession editSession = FaweAPI.getEditSessionBuilder(world).relightMode(RelightMode.OPTIMAL).limit(limit).build();
EditSession editSession =
pluginInterface.getWorldEdit().getWorldEdit().getEditSessionFactory()
.getEditSession(world, pluginInterface.getConfig().getInt("maximumBlocks"));
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);
}
// 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;
}
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 boolean saveRegionBlocks(File file, GeneralRegionInterface regionInterface) {
return saveRegionBlocksAsync(file, regionInterface).join();
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 occurred 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;
}
@Override
+1
View File
@@ -27,6 +27,7 @@
<version>7.1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId>
@@ -3,11 +3,10 @@ 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 extends Plugin {
public interface AreaShopInterface {
void debugI(Object... message);
YamlConfiguration getConfig();
@@ -3,7 +3,6 @@ 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;
@@ -21,10 +20,6 @@ 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
@@ -34,10 +29,6 @@ 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
+1
View File
@@ -35,6 +35,7 @@
<version>7.0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId>
+2 -4
View File
@@ -14,20 +14,19 @@
</parent>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.13.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<type>jar</type>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-bukkit</artifactId>
<scope>provided</scope>
<version>7.2.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
@@ -40,7 +39,6 @@
<groupId>me.wiefferink</groupId>
<artifactId>areashop-interface</artifactId>
<version>latest</version>
<type>jar</type>
</dependency>
</dependencies>
+69 -101
View File
@@ -1,116 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-parent</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<name>AreaShop Parent</name>
<url>https://github.com/NLthijs48/AreaShop</url>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-parent</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<name>AreaShop Parent</name>
<url>https://github.com/NLthijs48/AreaShop</url>
<licenses>
<license>
<name>GNU General Public License v3.0</name>
<url>https://www.gnu.org/licenses/gpl-3.0.en.html</url>
<distribution>repo</distribution>
</license>
</licenses>
<licenses>
<license>
<name>GNU General Public License v3.0</name>
<url>https://www.gnu.org/licenses/gpl-3.0.en.html</url>
<distribution>repo</distribution>
</license>
</licenses>
<issueManagement>
<url>https://github.com/NLthijs48/AreaShop/issues</url>
<system>GitHub Issues</system>
</issueManagement>
<issueManagement>
<url>https://github.com/md5sha256/AreaShop/issues</url>
<system>GitHub Issues</system>
</issueManagement>
<ciManagement>
<system>Jenkins</system>
<url>http://jenkins.wiefferink.me/job/AreaShop</url>
</ciManagement>
<!--ciManagement>
<system>Jenkins</system>
<url>http://jenkins.wiefferink.me/job/AreaShop</url>
</ciManagement-->
<repositories>
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository>
<repository>
<id>wiefferink-repo</id>
<url>http://maven.wiefferink.me</url>
</repository>
<repository>
<id>bstats-repo</id>
<url>http://repo.bstats.org/content/repositories/releases/</url>
</repository>
<repository>
<id>mvn-repo</id>
<url>https://mvnrepository.com/artifact/org.bstats/bstats-bukkit</url>
</repository>
<repository>
<id>sk89q-repo</id>
<url>https://maven.enginehub.org/repo/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
<repositories>
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository>
<repository>
<id>wiefferink-repo</id>
<url>http://maven.wiefferink.me</url>
</repository>
<repository>
<id>bstats-repo</id>
<url>http://repo.bstats.org/content/repositories/releases/</url>
</repository>
<repository>
<id>mvn-repo</id>
<url>https://mvnrepository.com/artifact/org.bstats/bstats-bukkit</url>
</repository>
<repository>
<id>sk89q-repo</id>
<url>https://maven.enginehub.org/repo/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!--build.number>#CUSTOM</build.number-->
</properties>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!--build.number>#CUSTOM</build.number-->
</properties>
<modules>
<module>hooks/areashop-interface</module>
<modules>
<module>hooks/areashop-interface</module>
<!-- FAWE Compat -->
<module>hooks/areashop-fawe-1-15</module>
<!-- FAWE Compat -->
<module>hooks/areashop-fawe-1-15</module>
<!-- World Edit Compat -->
<module>hooks/areashop-worldedit-7</module>
<!-- World Edit Compat -->
<module>hooks/areashop-worldedit-7</module>
<!-- World Guard Compat -->
<module>hooks/areashop-worldguard-7</module>
<!-- World Guard Compat -->
<module>hooks/areashop-worldguard-7</module>
<module>AreaShop</module>
</modules>
<module>AreaShop</module>
</modules>
<build>
<defaultGoal>clean package</defaultGoal>
<!--plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>8.29</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<configuration>
<configLocation>config/checkstyle.xml</configLocation>
<suppressionsLocation>config/checkstyle-suppressions.xml</suppressionsLocation>
<suppressionsFileExpression>config/checkstyle.suppressions.file
</suppressionsFileExpression>
<encoding>UTF-8</encoding>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<violationSeverity>error</violationSeverity>
</configuration>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins-->
</build>
<build>
<defaultGoal>clean package</defaultGoal>
</build>
</project>