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
74 changed files with 4201 additions and 4704 deletions

No files matched your search

+27 -33
View File
@@ -1,38 +1,32 @@
name : "build"
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
on : [ "pull_request", "push" ]
name: Java CI with Maven
jobs :
build :
strategy :
matrix :
java : [ 16 ]
# java: ["1.8", "11"]
os : [ "ubuntu-18.04" ]
runs-on : "${{ matrix.os }}"
steps :
- name : "Checkout Repository"
uses : "actions/checkout@v2.3.4"
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
- name : "Checkout submodules"
uses : "srt32/git-actions@v0.0.3"
with :
args : "git submodule update --init --recursive"
jobs:
build:
- name : "Restore Gradle cache"
uses : "actions/cache@v2"
with :
path : "~/.gradle/caches"
key : "${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}"
restore-keys : |
${{ runner.os }}-gradle-
- name : "Setup JDK ${{ matrix.java }}"
uses : "actions/setup-java@v1.4.3"
with :
java-version : "${{ matrix.java }}"
runs-on: ubuntu-latest
- name : "Grant execute permission for gradlew"
run : "chmod +x gradlew"
- name : "Build with Gradle"
run : "./gradlew clean build"
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Cache local Maven repository
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build with Maven
run: mvn -B package --file pom.xml
+3 -22
View File
@@ -1,4 +1,4 @@
### Maven ###
# Maven stuff
target
.project
@@ -14,31 +14,12 @@ local.properties
.settings/
.loadpath
### Locally stored "Eclipse launch configurations" ###
# Locally stored "Eclipse launch configurations"
*.launch
### IntelliJ IDEA ###
# IntelliJ IDEA
*.iml
.idea
dependency-reduced-pom.xml
desktop.ini
### Gradle ###
.gradle
build/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Cache of project
.gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
### Gradle Patch ###
**/build/
-60
View File
@@ -1,60 +0,0 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id 'java-library'
id 'com.github.johnrengelman.shadow' version '7.0.0'
}
description = 'AreaShop'
version = '2.6.5'
dependencies {
// Spigot api
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
// Libs
implementation 'com.github.NLthijs48:InteractiveMessenger:e7749258ca'
implementation 'co.aikar:taskchain-bukkit:3.7.2'
implementation 'io.papermc:paperlib:1.0.6'
// Hooks
implementation project(':areashop-interface')
implementation project(':areashop-worldguard-7')
implementation project(':areashop-worldedit-7')
implementation project(':areashop-fawe-1-16')
implementation project(':areashop-fawe-1-17')
implementation project(':areashop-nms-1-16-5')
implementation project(':areashop-nms-1-17')
// Runtime plugins
compileOnly 'com.sk89q.worldedit:worldedit-bukkit:7.2.5'
compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.5'
compileOnly 'com.github.MilkBowl:VaultAPI:1.7'
}
processResources {
from (sourceSets.main.resources) {
filter ReplaceTokens, tokens: [version: project.version]
duplicatesStrategy(DuplicatesStrategy.INCLUDE)
}
}
assemble {
dependsOn(shadowJar)
}
jar {
archiveBaseName.set('AreaShop')
classifier('original')
}
shadowJar {
archiveBaseName.set('AreaShop')
String relocationBase = 'me.wiefferink.areashop.shaded'
classifier('')
relocate('co.aikar.taskchain', "${relocationBase}.taskchain")
relocate('me.wiefferink.interactivemessenger', "${relocationBase}.interactivemessenger")
relocate('org.bstats', "${relocationBase}.bstats")
relocate('io.papermc', "${relocationBase}.papermc")
}
+300
View File
@@ -0,0 +1,300 @@
<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>
<parent>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>areashop</artifactId>
<packaging>jar</packaging>
<name>AreaShop</name>
<version>2.7.1-SNAPSHOT</version> <!-- Main plugin version -->
<repositories>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>aikar</id>
<url>https://repo.aikar.co/content/groups/aikar/</url>
</repository>
</repositories>
<dependencies>
<!-- Bukkit, AreaShop aims to support 1.13.2+ -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.13.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- InteractiveMessenger library -->
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>interactivemessenger</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
<!-- TaskChain library -->
<dependency>
<groupId>co.aikar</groupId>
<artifactId>taskchain-bukkit</artifactId>
<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.5</version>
</dependency>
<!-- WorldEdit/WorldGuard -->
<dependency>
<groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-bukkit</artifactId>
<version>7.1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId>
<version>7.0.3</version>
<scope>provided</scope>
</dependency>
<!-- Interfaces that specific WorldGuard and WorldEdit versions are built against -->
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-interface</artifactId>
<version>latest</version>
<scope>compile</scope>
</dependency>
<!-- WorldGuard implementations -->
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-worldguard-7</artifactId>
<version>latest</version>
<scope>compile</scope>
</dependency>
<!-- WorldEdit implementations -->
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-worldedit-7</artifactId>
<version>latest</version>
<scope>compile</scope>
</dependency>
<!-- FAWE compat -->
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-fawe-1-15</artifactId>
<version>latest</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<!-- Vault -->
<dependency>
<groupId>net.milkbowl.vault</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<scope>provided</scope>
</dependency>
<!-- bStats -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>1.6</version>
<scope>compile</scope>
</dependency>
</dependencies>
<profiles>
<!-- Copy the jar file to a test server, activate by using -DcopyResult="/path/to/test/server" -->
<profile>
<id>test-locally</id>
<activation>
<property>
<name>copyResult</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<configuration>
<target>
<copy file="./target/${project.build.finalName}.jar"
tofile="${copyResult}\${project.build.finalName}.jar"/>
</target>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<!-- Package the source files in a jar, activate by using -Dsources -->
<profile>
<id>package-sources</id>
<activation>
<property>
<name>sources</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<!-- Produce javadoc files, activate by using -Djavadoc -->
<profile>
<id>generate-javadoc</id>
<activation>
<property>
<name>javadoc</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<excludePackageNames>me.wiefferink.areashop.lib</excludePackageNames>
<destDir>${javadoc}</destDir>
<links>
<link>https://hub.spigotmc.org/javadocs/bukkit/</link>
<link>http://docs.sk89q.com/worldedit/apidocs/</link>
<link>http://docs.sk89q.com/worldguard/apidocs/</link>
</links>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<directory>target</directory>
<finalName>AreaShop</finalName>
<resources>
<resource>
<targetPath>.</targetPath>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<includes>
<include>plugin.yml</include>
<include>config.yml</include>
<include>hiddenConfig.yml</include>
<include>default.yml</include>
<include>README.md</include>
<include>lang/*.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<!-- Relocate TaskChain -->
<relocation>
<pattern>co.aikar.taskchain</pattern>
<shadedPattern>me.wiefferink.areashop.shaded.taskchain</shadedPattern>
</relocation>
<!-- Relocate messaging framework -->
<relocation>
<pattern>me.wiefferink.interactivemessenger</pattern>
<shadedPattern>me.wiefferink.areashop.shaded.interactivemessenger</shadedPattern>
</relocation>
<!-- Relocate bstats -->
<relocation>
<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>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -4,12 +4,13 @@ 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.NMS;
import me.wiefferink.areashop.interfaces.WorldEditInterface;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.listeners.PlayerLoginLogoutListener;
import me.wiefferink.areashop.managers.*;
import me.wiefferink.areashop.tools.Analytics;
import me.wiefferink.areashop.tools.GithubUpdateCheck;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
@@ -111,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
@@ -129,8 +130,6 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
private List<String> chatprefix = null;
private boolean ready = false;
private GithubUpdateCheck githubUpdateCheck = null;
private NMS nms;
private SignErrorLogger signErrorLogger;
public static AreaShop getInstance() {
return AreaShop.instance;
@@ -266,14 +265,6 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
this.chatprefix = chatprefix;
}
public NMS getNMSHelper() {
return this.nms;
}
public SignErrorLogger getSignErrorLogger() {
return this.signErrorLogger;
}
/**
* Function to get WorldGuardInterface for version dependent things.
*
@@ -348,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;
@@ -573,22 +564,6 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
HandlerList.unregisterAll(this);
}
private void setupNMS() throws IllegalStateException {
final String packageName = Bukkit.getServer().getClass().getPackage().getName();
final String[] split = packageName.split("\\.");
final String nmsRevision = split[3];
try {
Class<?> rawClazz = Class.forName("me.wiefferink.areashop.nms." + nmsRevision + ".NMSImpl");
if (!NMS.class.isAssignableFrom(rawClazz)) {
throw new IllegalStateException("Invalid NMS Impl: " + rawClazz.getCanonicalName() + " does not implement the NMS!");
}
Class<? extends NMS> casted = rawClazz.asSubclass(NMS.class);
this.nms = casted.getConstructor().newInstance();
} catch (ReflectiveOperationException ex) {
throw new IllegalStateException("Failed to instantiate the NMS Helper!", ex);
}
}
/**
* Called on start or reload of the server.
*/
@@ -596,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();
@@ -605,9 +581,6 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
error = true;
}
// Setup the NMS helper
setupNMS();
// Load all data from files and check versions
fileManager = new FileManager();
managers.add(fileManager);
@@ -636,6 +609,11 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
signLinkerManager = new SignLinkerManager();
managers.add(signLinkerManager);
// Enable Metrics if config allows it
if (getConfig().getBoolean("sendStats")) {
Analytics.start();
}
// Register dynamic permission (things declared in config)
registerDynamicPermissions();
@@ -646,10 +624,9 @@ 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()) {
// FIXME log invalid version
return false;
throw new IllegalArgumentException("Invalid Versions Detected!");
}
dual0 = true;
}
@@ -658,8 +635,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
if (!matcher1.find()) {
matcher1 = VERSION_MATCHER_2.matcher(currentVersion);
if (!matcher1.find()) {
// FIXME log invalid version
return false;
throw new IllegalArgumentException("Invalid Versions Detected!");
}
dual1 = true;
}
@@ -710,20 +686,10 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
final Plugin fawe = getServer().getPluginManager().getPlugin("FastAsyncWorldEdit");
if (fawe != null && fawe.isEnabled()) {
final String version = fawe.getDescription().getVersion();
if (version.startsWith("1.16")) {
if (version.startsWith("1.15") || version.startsWith("1.16")) {
try {
this.worldEditInterface = (WorldEditInterface) Class.forName(packageName + "fawe.v1_16.FastAsyncWorldEditHandler")
.getDeclaredConstructor(AreaShopInterface.class)
.newInstance(this);
} catch (ReflectiveOperationException ex) {
getLogger().log(Level.SEVERE, "Failed to hook into FAWE!");
ex.printStackTrace();
}
} else if (version.startsWith("1.17")) {
try {
this.worldEditInterface = (WorldEditInterface) Class.forName(packageName + "fawe.v1_17.FastAsyncWorldEditHandler")
.getDeclaredConstructor(AreaShopInterface.class)
.newInstance(this);
this.worldEditInterface = (WorldEditInterface) Class.forName(packageName + "fawe.v1_15.FastAsyncWorldEditHandler")
.getDeclaredConstructor(AreaShopInterface.class).newInstance(this);
} catch (ReflectiveOperationException ex) {
getLogger().log(Level.SEVERE, "Failed to hook into FAWE!");
ex.printStackTrace();
@@ -831,3 +797,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopInterface {
}
}
@@ -125,7 +125,7 @@ public class AddCommand extends CommandAreaShop {
TreeSet<String> namesNoPermission = new TreeSet<>();
TreeSet<String> namesAddCancelled = new TreeSet<>(); // Denied by an event listener
Utils.runAsBatches(regions.entrySet(),
Math.min(plugin.getConfig().getInt("adding.regionsPerTick"), 1),
plugin.getConfig().getInt("adding.regionsPerTick"),
(regionEntry) -> {
String regionName = regionEntry.getKey();
ProtectedRegion region = regionEntry.getValue();
@@ -6,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.
@@ -43,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<>();
}
/**
@@ -12,7 +12,6 @@ import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
public class DelCommand extends CommandAreaShop {
@@ -52,7 +51,7 @@ public class DelCommand extends CommandAreaShop {
return;
}
List<GeneralRegion> regions = Utils.getRegionsInSelection(selection);
if(regions.isEmpty()) {
if(regions == null || regions.isEmpty()) {
plugin.message(player, "cmd-noRegionsFound");
return;
}
@@ -93,41 +92,37 @@ public class DelCommand extends CommandAreaShop {
plugin.message(sender, "del-cancelled", Utils.combinedMessage(regionsCancelled, "region"));
}
} else {
CompletableFuture<GeneralRegion> future = plugin.getFileManager().getRegionUnsafe(args[1]);
Utils.newChain()
.future(future)
.syncLast(region -> {
if(region == null) {
plugin.message(sender, "cmd-notRegistered", args[1]);
return;
}
boolean isLandlord = sender instanceof Player && region.isLandlord(((Player)sender).getUniqueId());
if(region instanceof RentRegion) {
// Remove the rent if the player has permission
if(sender.hasPermission("areashop.destroyrent") || (isLandlord && sender.hasPermission("areashop.destroyrent.landlord"))) {
DeletingRegionEvent event = plugin.getFileManager().deleteRegion(region, true);
if (event.isCancelled()) {
plugin.message(sender, "general-cancelled", event.getReason());
} else {
plugin.message(sender, "destroy-successRent", region);
}
} else {
plugin.message(sender, "destroy-noPermissionRent", region);
}
} else if(region instanceof BuyRegion) {
// Remove the buy if the player has permission
if(sender.hasPermission("areashop.destroybuy") || (isLandlord && sender.hasPermission("areashop.destroybuy.landlord"))) {
DeletingRegionEvent event = plugin.getFileManager().deleteRegion(region, true);
if (event.isCancelled()) {
plugin.message(sender, "general-cancelled", event.getReason());
} else {
plugin.message(sender, "destroy-successBuy", region);
}
} else {
plugin.message(sender, "destroy-noPermissionBuy", region);
}
}
}).execute();
GeneralRegion region = plugin.getFileManager().getRegion(args[1]);
if(region == null) {
plugin.message(sender, "cmd-notRegistered", args[1]);
return;
}
boolean isLandlord = sender instanceof Player && region.isLandlord(((Player)sender).getUniqueId());
if(region instanceof RentRegion) {
// Remove the rent if the player has permission
if(sender.hasPermission("areashop.destroyrent") || (isLandlord && sender.hasPermission("areashop.destroyrent.landlord"))) {
DeletingRegionEvent event = plugin.getFileManager().deleteRegion(region, true);
if (event.isCancelled()) {
plugin.message(sender, "general-cancelled", event.getReason());
} else {
plugin.message(sender, "destroy-successRent", region);
}
} else {
plugin.message(sender, "destroy-noPermissionRent", region);
}
} else if(region instanceof BuyRegion) {
// Remove the buy if the player has permission
if(sender.hasPermission("areashop.destroybuy") || (isLandlord && sender.hasPermission("areashop.destroybuy.landlord"))) {
DeletingRegionEvent event = plugin.getFileManager().deleteRegion(region, true);
if (event.isCancelled()) {
plugin.message(sender, "general-cancelled", event.getReason());
} else {
plugin.message(sender, "destroy-successBuy", region);
}
} else {
plugin.message(sender, "destroy-noPermissionBuy", region);
}
}
}
}
@@ -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);
@@ -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;
}
@@ -92,7 +91,7 @@ public class FriendsFeature extends RegionFeature {
HashSet<String> result = new HashSet<>();
for(UUID friend : getFriends()) {
OfflinePlayer player = Bukkit.getOfflinePlayer(friend);
if(player.hasPlayedBefore() && player.getName() != null) {
if(player != null && player.getName() != null) {
result.add(player.getName());
}
}
@@ -1,21 +1,22 @@
package me.wiefferink.areashop.features;
import co.aikar.taskchain.TaskChain;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import io.papermc.lib.PaperLib;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.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.Tag;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
@@ -42,18 +43,18 @@ public class TeleportFeature extends RegionFeature {
*/
private static boolean isSafe(Location location) {
Block feet = location.getBlock();
BlockState head = PaperLib.getBlockState(feet.getRelative(BlockFace.UP), false).getState();
BlockState below = PaperLib.getBlockState(feet.getRelative(BlockFace.DOWN), false).getState();
BlockState above = PaperLib.getBlockState(head.getBlock().getRelative(BlockFace.UP), false).getState();
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.getBlock().isLiquid()) {
} else if ((head.getType().isSolid() && !canSpawnIn(head.getType())) || head.isLiquid()) {
return false;
} else if (!below.getType().isSolid() || cannotSpawnOn(below.getType()) || below.getBlock().isLiquid()) {
} else if (!below.getType().isSolid() || cannotSpawnOn(below.getType()) || below.isLiquid()) {
return false;
} else if (above.getBlock().isLiquid() || cannotSpawnBeside(above.getType())) {
} else if (above.isLiquid() || cannotSpawnBeside(above.getType())) {
return false;
}
@@ -67,7 +68,7 @@ public class TeleportFeature extends RegionFeature {
continue;
}
around.add(PaperLib.getBlockState(below.getBlock().getRelative(x, y, z), false).getState().getType());
around.add(below.getRelative(x, y, z).getType());
}
}
}
@@ -102,14 +103,17 @@ public class TeleportFeature extends RegionFeature {
* @return true when it is safe to spawn on top of, otherwise false
*/
private static boolean cannotSpawnOn(Material material) {
return switch (material) {
case CACTUS, DRAGON_EGG, MAGMA_BLOCK -> false;
default -> !Tag.SIGNS.isTagged(material)
&& !Tag.FENCES.isTagged(material)
&& !Tag.FENCE_GATES.isTagged(material)
&& !Tag.WALL_SIGNS.isTagged(material)
&& !Tag.DOORS.isTagged(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");
}
/**
@@ -119,10 +123,11 @@ public class TeleportFeature extends RegionFeature {
* @return true when it is safe to spawn next to, otherwise false
*/
private static boolean cannotSpawnBeside(Material material) {
return switch (material) {
case LAVA, CACTUS, FIRE, MAGMA_BLOCK -> false;
default -> true;
};
String name = material.name();
return name.contains("LAVA")
|| name.contains("CACTUS")
|| name.equals("FIRE")
|| name.contains("MAGMA");
}
/**
@@ -248,20 +253,14 @@ public class TeleportFeature extends RegionFeature {
BlockVector3 min = worldguardRegion.getMinimumPoint();
BlockVector3 max = worldguardRegion.getMaximumPoint();
final Collection<int[]> collection = new HashSet<>();
for (int x = min.getX(); x < max.getX(); x += 16) {
for (int z = min.getZ(); z < max.getZ(); z += 16) {
collection.add(new int[]{x, z});
PaperLib.getChunkAtAsync(world, x >> 4, z >> 4);
}
}
final CompletableFuture<Boolean> future = new CompletableFuture<>();
CompletableFuture<Boolean> future = new CompletableFuture<>();
final boolean finalToSign = toSign;
TaskChain<?> chain = Utils.newChain();
if (collection.size() < 64) {
chain = chain.future(Utils.runAsBatches(collection, 16, (arr) -> PaperLib.getChunkAtAsync(world, arr[0], arr[1]), false));
}
chain.delay(3).syncLast((unused) -> {
Bukkit.getScheduler().runTaskLater(plugin, () -> {
boolean blocksInRegionCopy = blocksInRegion;
Location safeLocation = startLocation;
// Tries limit tracking
@@ -285,9 +284,7 @@ public class TeleportFeature extends RegionFeature {
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -305,9 +302,7 @@ public class TeleportFeature extends RegionFeature {
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -325,9 +320,7 @@ public class TeleportFeature extends RegionFeature {
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -345,9 +338,7 @@ public class TeleportFeature extends RegionFeature {
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -365,9 +356,7 @@ public class TeleportFeature extends RegionFeature {
}
if (!done && !topDone) {
safeLocation = startLocation.clone().add(0, radius, 0);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -378,9 +367,7 @@ public class TeleportFeature extends RegionFeature {
// North
for (int x = -r + 1; x <= r && !done; x++) {
safeLocation = startLocation.clone().add(x, radius, -r);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -390,9 +377,7 @@ public class TeleportFeature extends RegionFeature {
// East
for (int z = -r + 1; z <= r && !done; z++) {
safeLocation = startLocation.clone().add(r, radius, z);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -402,9 +387,7 @@ public class TeleportFeature extends RegionFeature {
// South side
for (int x = r - 1; x >= -r && !done; x--) {
safeLocation = startLocation.clone().add(x, radius, r);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -414,9 +397,7 @@ public class TeleportFeature extends RegionFeature {
// West side
for (int z = r - 1; z >= -r && !done; z--) {
safeLocation = startLocation.clone().add(-r, radius, z);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -434,9 +415,7 @@ public class TeleportFeature extends RegionFeature {
}
if (!done && !bottomDone) {
safeLocation = startLocation.clone().add(0, -radius, 0);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -447,9 +426,7 @@ public class TeleportFeature extends RegionFeature {
// North
for (int x = -r + 1; x <= r && !done; x++) {
safeLocation = startLocation.clone().add(x, -radius, -r);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -459,9 +436,7 @@ public class TeleportFeature extends RegionFeature {
// East
for (int z = -r + 1; z <= r && !done; z++) {
safeLocation = startLocation.clone().add(r, -radius, z);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -471,9 +446,7 @@ public class TeleportFeature extends RegionFeature {
// South side
for (int x = r - 1; x >= -r && !done; x--) {
safeLocation = startLocation.clone().add(x, -radius, r);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -483,9 +456,7 @@ public class TeleportFeature extends RegionFeature {
// West side
for (int z = r - 1; z >= -r && !done; z--) {
safeLocation = startLocation.clone().add(-r, -radius, z);
if (!insideRegion || worldguardRegion.contains(safeLocation.getBlockX(),
safeLocation.getBlockY(),
safeLocation.getBlockZ())) {
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
@@ -500,13 +471,16 @@ public class TeleportFeature extends RegionFeature {
}
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));
Vector signVector = getRegion().getSignsFeature().getSigns().get(0).getLocation().toVector().add(new Vector(0.5, 0.5, 0.5));
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())));
@@ -521,7 +495,7 @@ public class TeleportFeature extends RegionFeature {
AreaShop.debug("No location found, checked " + checked + " spots of max " + maxTries);
future.complete(false);
}
}).execute();
}, 1);
return future;
}
@@ -558,19 +532,20 @@ public class TeleportFeature extends RegionFeature {
ProtectedRegion worldguardRegion = getRegion().getRegion();
// Try to get sign location
List<RegionSign> signs = getRegion().getSignsFeature().getSigns();
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 = signs.get(0).getLocation(); //.subtract(0.0, 1.0, 0.0);
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 = getRegion().getSignsFeature().getSigns().get(0).getFacing();
BlockFace facing = sign.getFacing();
Vector facingVector = new Vector(facing.getModX(), facing.getModY(), facing.getModZ())
.normalize()
.multiply(distance);
@@ -592,9 +567,10 @@ public class TeleportFeature extends RegionFeature {
// 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 = AreaShop.getInstance().getWorldGuardHandler().getMinimumPoint(worldguardRegion);
Vector regionMax = AreaShop.getInstance().getWorldGuardHandler().getMaximumPoint(worldguardRegion);
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)) {
@@ -611,7 +587,8 @@ public class TeleportFeature extends RegionFeature {
AreaShop.warn("Could not parse general.teleportLocationY: '" + configSetting + "'");
}
}
startLocation = middle.toLocation(getRegion().getWorld(), player.getLocation().getYaw(), player.getLocation().getPitch());
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
@@ -255,7 +255,7 @@ public class WorldGuardRegionFlagsFeature extends RegionFeature {
result = result.replace("&1", "&B");
result = result.replace("&d", "&p");
result = result.replace("&5", "&P");
//result = result.replace("&0", "&0");
result = result.replace("&0", "&0");
result = result.replace("&8", "&1");
result = result.replace("&7", "&2");
result = result.replace("&f", "&w");
@@ -1,11 +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.interfaces.BlockBehaviourHelper;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.Utils;
@@ -13,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;
@@ -26,51 +20,12 @@ import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
/**
* Sign that is connected to a region to display information and interact with the region.
*/
public class RegionSign {
public static final BlockBehaviourHelper behaviourHelper = AreaShop.getInstance().getNMSHelper().behaviourHelper();
public static Level invalidSignLogLevel;
public static boolean removeInvalidSigns;
static {
YamlConfiguration configuration = AreaShop.getInstance().getConfig();
removeInvalidSigns = configuration.getBoolean("signs.removeInvalid");
final String rawLogLevel = configuration.getString("signs.invalidSignLogLevel", "WARNING");
try {
assert rawLogLevel != null;
invalidSignLogLevel = Level.parse(rawLogLevel);
} catch (IllegalArgumentException ex) {
AreaShop.warn("Invalid Logging Level: " + rawLogLevel);
invalidSignLogLevel = Level.WARNING;
}
}
private static boolean canLogSignError() {
return AreaShop.getInstance().getLogger().isLoggable(invalidSignLogLevel);
}
private static void logSignError(String... messages) {
if (invalidSignLogLevel == Level.OFF) {
return;
}
if (invalidSignLogLevel == Level.SEVERE) {
AreaShop.error((Object) messages);
} else if (invalidSignLogLevel == Level.WARNING) {
AreaShop.warn((Object) messages);
} else if (invalidSignLogLevel.intValue() >= Level.FINE.intValue()) {
AreaShop.debug((Object) messages);
} else {
AreaShop.info((Object) messages);
}
}
private final SignsFeature signsFeature;
private final String key;
@@ -151,7 +106,7 @@ public class RegionSign {
/**
* Get the material of the sign as saved in the config.
*
* @return Material of the sign, usually, a sign or {@link 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");
@@ -186,7 +141,7 @@ public class RegionSign {
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue());
assert stateConfig != null;
// Get the lines
String[] signLines = new String[4];
boolean signEmpty = true;
@@ -198,31 +153,29 @@ public class RegionSign {
block.setType(Material.AIR);
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 (!Materials.isSign(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, getRegion().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, getRegion().getName(), signType.name()));
}
// Save current rotation and type
@@ -232,7 +185,7 @@ public class RegionSign {
if (!regionConfig.isString("general.signs." + key + ".facing")) {
final BlockFace rotation;
if (Materials.isSign(block.getType())) {
BlockData data = blockState.getBlockData();
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) {
@@ -247,7 +200,7 @@ public class RegionSign {
}
// 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, "");
@@ -257,8 +210,8 @@ public class RegionSign {
signLines[i] = Utils.applyColors(signLines[i]);
signState.setLine(i, signLines[i]);
}
// BlockState#update *should* return true here.
return blockState.update(true, false);
signState.update();
return true;
}
/**
@@ -1,121 +0,0 @@
package me.wiefferink.areashop.features.signs;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.events.notify.UpdateRegionEvent;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SignCache implements Listener {
private final Map<String, RegionSign> allSigns = new HashMap<>();
private final Map<String, List<RegionSign>> signsByChunk = new HashMap<>();
public void registerListeners() {
Bukkit.getPluginManager().registerEvents(this, AreaShop.getInstance());
}
/**
* Convert a location to a string to use as map key.
*
* @param location The location to get the key for
* @return A string to use in a map for a location
*/
public static String locationToString(Location location) {
return location.getWorld().getName() + ";" + location.getBlockX() + ";" + location.getBlockY() + ";" + location.getBlockZ();
}
/**
* Convert a chunk to a string to use as map key.
*
* @param location The location to get the key for
* @return A string to use in a map for a chunk
*/
public static String chunkToString(Location location) {
return location.getWorld().getName() + ";" + (location.getBlockX() >> 4) + ";" + (location.getBlockZ() >> 4);
}
/**
* Convert a chunk to a string to use as map key.
* Use a Location argument to prevent chunk loading!
*
* @param chunk The location to get the key for
* @return A string to use in a map for a chunk
*/
public static String chunkToString(Chunk chunk) {
return chunk.getWorld().getName() + ";" + chunk.getX() + ";" + chunk.getZ();
}
/**
* Get a sign by a location.
*
* @param location The location to get the sign for
* @return The RegionSign that is at the location, or null if none
*/
public RegionSign getSignByLocation(Location location) {
return allSigns.get(locationToString(location));
}
/**
* Get the map with all signs.
*
* @return Map with all signs: locationString -&gt; RegionSign
*/
public Map<String, RegionSign> getAllSigns() {
return allSigns;
}
/**
* Get the map with signs by chunk.
*
* @return Map with signs by chunk: chunkString -&gt; List&lt;RegionSign&gt;
*/
public Map<String, List<RegionSign>> getSignsByChunk() {
return signsByChunk;
}
public void addSign(RegionSign regionSign) {
signsByChunk.computeIfAbsent(regionSign.getStringChunk(), (unused) -> new ArrayList<>()).add(regionSign);
}
public void removeSign(RegionSign regionSign) {
final List<RegionSign> signs = signsByChunk.get(regionSign.getStringChunk());
if (signs != null) {
signs.remove(regionSign);
}
allSigns.remove(regionSign.getStringLocation());
}
public void shutdown() {
HandlerList.unregisterAll(this);
signsByChunk.clear();
allSigns.clear();
}
@EventHandler
public void regionUpdate(UpdateRegionEvent event) {
event.getRegion().getSignsFeature().update();
}
@EventHandler(priority = EventPriority.MONITOR)
public void onChunkLoad(ChunkLoadEvent event) {
List<RegionSign> chunkSigns = signsByChunk.get(chunkToString(event.getChunk()));
if (chunkSigns == null) {
return;
}
int batchSize = Math.max(chunkSigns.size() / 10, 10);
Utils.runAsBatches(chunkSigns, batchSize, RegionSign::update, false);
}
}
@@ -4,10 +4,9 @@ import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.events.notify.UpdateRegionEvent;
import me.wiefferink.areashop.features.RegionFeature;
import me.wiefferink.areashop.interfaces.BlockBehaviourHelper;
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;
@@ -23,7 +22,6 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Sign;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -32,22 +30,15 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.SignChangeEvent;
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 {
public static final SignCache signCache = new SignCache();
private static final BlockBehaviourHelper behaviourHelper = AreaShop.getInstance().getNMSHelper().behaviourHelper();
static {
signCache.registerListeners();
}
public static void shutdownGlobalState() {
signCache.shutdown();
}
private static final Map<String, RegionSign> allSigns = new ConcurrentHashMap<>();
private static final Map<String, List<RegionSign>> signsByChunk = new ConcurrentHashMap<>();
private Map<String, RegionSign> signs;
@@ -65,19 +56,19 @@ 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);
signCache.addSign(sign);
signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>())
.add(sign);
}
allSigns.putAll(signs);
}
}
@@ -119,7 +110,7 @@ public class SignsFeature extends RegionFeature {
* @return The RegionSign that is at the location, or null if none
*/
public static RegionSign getSignByLocation(Location location) {
return signCache.getSignByLocation(location);
return allSigns.get(locationToString(location));
}
/**
@@ -128,7 +119,7 @@ public class SignsFeature extends RegionFeature {
* @return Map with all signs: locationString -&gt; RegionSign
*/
public static Map<String, RegionSign> getAllSigns() {
return signCache.getAllSigns();
return allSigns;
}
/**
@@ -137,7 +128,7 @@ public class SignsFeature extends RegionFeature {
* @return Map with signs by chunk: chunkString -&gt; List&lt;RegionSign&gt;
*/
public static Map<String, List<RegionSign>> getSignsByChunk() {
return signCache.getSignsByChunk();
return signsByChunk;
}
@Override
@@ -145,26 +136,24 @@ public class SignsFeature extends RegionFeature {
// Deregister signs from the registry
if (signs != null) {
for (Map.Entry<String, RegionSign> entry : signs.entrySet()) {
signCache.removeSign(entry.getValue());
allSigns.remove(entry.getKey());
signsByChunk.get(entry.getValue().getStringChunk()).remove(entry.getValue());
}
}
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGH)
public void onSignBreak(BlockBreakEvent event) {
Block block = event.getBlock();
RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if (regionSign == null) {
if (event.isCancelled()) {
return;
}
if (regionSign.getRegion().invalidated()) {
AreaShop.warn("SignsFeature: Sign feature for an invalidated region is still active!");
}
Block block = event.getBlock();
// Check if it is a sign
if (Materials.isSign(block.getType())) {
// Check if the rent sign is really the same as a saved rent
if (regionSign.getRegion().invalidated()) {
AreaShop.warn("SignsFeature: Sign feature for an invalidated region object is still active!");
RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if (regionSign == null) {
return;
}
// Remove the sign of the rental region if the player has permission
if (event.getPlayer().hasPermission("areashop.delsign")) {
@@ -179,7 +168,26 @@ public class SignsFeature extends RegionFeature {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onIndirectSignBreak(BlockPhysicsEvent event) {
// Check if the block is a sign
if (!Materials.isSign(event.getBlock().getType())) {
return;
}
final Block block = event.getBlock();
final BlockData blockData = block.getBlockData();
final Block attachedBlock;
if (blockData instanceof WallSign) {
attachedBlock = block.getRelative(((WallSign) blockData).getFacing().getOppositeFace());
} else if (blockData instanceof Sign) {
attachedBlock = block.getRelative(BlockFace.DOWN);
} else {
attachedBlock = null;
}
// Check if still attached to a block
// TODO: signs cannot be placed on all blocks, improve this check to isSolid()?
if (attachedBlock == null || attachedBlock.getType() != Material.AIR) {
return;
}
// Check if the sign is really the same as a saved rent
RegionSign regionSign = SignsFeature.getSignByLocation(event.getBlock().getLocation());
@@ -187,18 +195,6 @@ public class SignsFeature extends RegionFeature {
return;
}
if (regionSign.getRegion().invalidated()) {
AreaShop.warn("SignsFeature: Sign feature for an invalidated region is still active!");
}
// Check if the block is a sign
if (!Materials.isSign(event.getBlock().getType())) {
return;
}
// If the block is in an invalid position then we remove the sign
if (!behaviourHelper.isBlockValid(block)) {
regionSign.remove();
}
// Remove the sign so that it does not fall on the floor as an item (next region update will place it back when possible)
AreaShop.debug("onIndirectSignBreak: Removed block of sign for", regionSign.getRegion().getName(), "at", regionSign.getStringLocation());
event.getBlock().setType(Material.AIR);
@@ -211,15 +207,6 @@ public class SignsFeature extends RegionFeature {
if (block == null) {
return;
}
// Check if this sign belongs to a region
RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if (regionSign == null) {
return;
}
if (regionSign.getRegion().invalidated()) {
AreaShop.warn("SignsFeature: Sign feature for an invalidated region is still active!");
}
// Only listen to left and right clicks on blocks
if (!(event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK)) {
@@ -231,6 +218,11 @@ public class SignsFeature extends RegionFeature {
return;
}
// Check if this sign belongs to a region
RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if (regionSign == null) {
return;
}
// Ignore players that are in sign link mode (which will handle the event itself)
Player player = event.getPlayer();
@@ -256,17 +248,11 @@ public class SignsFeature extends RegionFeature {
event.setCancelled(ran);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@EventHandler(priority = EventPriority.MONITOR)
public void onSignChange(SignChangeEvent event) {
RegionSign regionSign = SignsFeature.getSignByLocation(event.getBlock().getLocation());
if (regionSign == null) {
if (event.isCancelled()) {
return;
}
if (regionSign.getRegion().invalidated()) {
AreaShop.warn("SignsFeature: Sign feature for an invalidated region is still active!");
}
Player player = event.getPlayer();
if (!plugin.isReady()) {
plugin.message(player, "general-notReady");
@@ -546,6 +532,21 @@ public class SignsFeature extends RegionFeature {
}
}
@EventHandler
public void regionUpdate(UpdateRegionEvent event) {
event.getRegion().getSignsFeature().update();
}
@EventHandler(priority = EventPriority.MONITOR)
public void onChunkLoad(ChunkLoadEvent event) {
List<RegionSign> chunkSigns = signsByChunk.get(chunkToString(event.getChunk()));
if (chunkSigns == null) {
return;
}
int batchSize = Math.max(chunkSigns.size() / 10, 10);
Utils.runAsBatches(chunkSigns, batchSize, RegionSign::update, false);
}
/**
* Update all signs connected to this region.
*
@@ -578,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());
}
/**
@@ -597,7 +598,7 @@ public class SignsFeature extends RegionFeature {
* @return A List with all sign locations
*/
public List<Location> getSignLocations() {
List<Location> result = new ArrayList<>(signs.size());
List<Location> result = new ArrayList<>();
for (RegionSign sign : signs.values()) {
result.add(sign.getLocation());
}
@@ -614,8 +615,7 @@ public class SignsFeature extends RegionFeature {
*/
public void addSign(Location location, Material signType, BlockFace facing, String profile) {
int i = 0;
final YamlConfiguration configuration = getRegion().getConfig();
while (configuration.isSet("general.signs." + i)) {
while (getRegion().getConfig().isSet("general.signs." + i)) {
i++;
}
String signPath = "general.signs." + i + ".";
@@ -628,7 +628,9 @@ public class SignsFeature extends RegionFeature {
// Add to the map
RegionSign sign = new RegionSign(this, i + "");
signs.put(sign.getStringLocation(), sign);
signCache.addSign(sign);
allSigns.put(sign.getStringLocation(), sign);
signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>())
.add(sign);
}
}
@@ -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();
}
}
@@ -34,11 +34,17 @@ import me.wiefferink.areashop.commands.StackCommand;
import me.wiefferink.areashop.commands.StopresellCommand;
import me.wiefferink.areashop.commands.TeleportCommand;
import me.wiefferink.areashop.commands.UnrentCommand;
import org.bukkit.command.*;
import org.bukkit.command.Command;
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 TabExecutor {
public class CommandManager extends Manager implements CommandExecutor, TabCompleter {
private final ArrayList<CommandAreaShop> commands;
/**
@@ -82,6 +88,7 @@ public class CommandManager extends Manager implements TabExecutor {
// Register commands in bukkit
plugin.getCommand("AreaShop").setExecutor(this);
plugin.getCommand("AreaShop").setTabCompleter(this);
}
/**
@@ -158,10 +165,10 @@ public class CommandManager extends Manager implements TabExecutor {
@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());
@@ -68,8 +68,6 @@ public class FeatureManager extends Manager {
for(RegionFeature feature : globalFeatures) {
feature.shutdown();
}
// FIXME make this system less hacky!
SignsFeature.shutdownGlobalState();
}
/**
@@ -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,40 +24,37 @@ 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;
public class FileManager extends Manager {
private final Set<String> worldRegionsRequireSaving;
private final Map<String, BuyRegion> buyRegions = new HashMap<>();
private final Map<String, RentRegion> rentRegions = new HashMap<>();
private final Map<String, GeneralRegion> allRegions = new HashMap<>();
private final String regionsPath;
private final HashMap<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 HashMap<String, Integer> versions = null;
private final String versionPath;
private final String schemFolder;
private String versionPath = null;
private String schemFolder = null;
/**
* Constructor, initialize variabeles.
*/
public FileManager() {
regions = new HashMap<>();
regionsPath = plugin.getDataFolder() + File.separator + AreaShop.regionsFolder;
configPath = plugin.getDataFolder() + File.separator + "config.yml";
groups = new HashMap<>();
@@ -151,26 +150,7 @@ public class FileManager extends Manager {
* @return The region if found, otherwise null
*/
public GeneralRegion getRegion(String name) {
return allRegions.get(name.toLowerCase());
}
public CompletableFuture<GeneralRegion> getRegionUnsafe(String name) {
final GeneralRegion cached = allRegions.get(name);
if (cached != null) {
return CompletableFuture.completedFuture(cached);
}
final File file = new File(regionsPath, name + ".yml");
if (!file.exists()) {
return CompletableFuture.completedFuture(null);
}
CompletableFuture<GeneralRegion> completableFuture = new CompletableFuture<>();
Utils.newChain()
.async(() -> {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file);
completableFuture.complete(loadRegion(configuration));
})
.execute();
return completableFuture;
return regions.get(name.toLowerCase());
}
/**
@@ -180,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;
}
/**
@@ -190,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;
}
/**
@@ -199,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;
}
/**
@@ -208,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;
}
/**
@@ -217,9 +217,7 @@ public class FileManager extends Manager {
* @return List of all regions (it is safe to modify the list)
*/
public List<GeneralRegion> getRegions() {
final List<GeneralRegion> list = new ArrayList<>(allRegions.size());
list.addAll(allRegions.values());
return list;
return new ArrayList<>(regions.values());
}
/**
@@ -228,11 +226,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getBuyNames() {
final List<String> list = new ArrayList<>(buyRegions.size());
for (GeneralRegion region : buyRegions.values()) {
list.add(region.getName());
}
return list;
return getBuys().stream().map(GeneralRegion::getName).collect(Collectors.toList());
}
/**
@@ -241,11 +235,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getRentNames() {
final List<String> list = new ArrayList<>(rentRegions.size());
for (GeneralRegion region : rentRegions.values()) {
list.add(region.getName());
}
return list;
return getRents().stream().map(GeneralRegion::getName).collect(Collectors.toList());
}
/**
@@ -254,11 +244,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getRegionNames() {
final List<String> list = new ArrayList<>(allRegions.size());
for (GeneralRegion region : rentRegions.values()) {
list.add(region.getName());
}
return list;
return getRegions().stream().map(GeneralRegion::getName).collect(Collectors.toList());
}
/**
@@ -267,11 +253,7 @@ public class FileManager extends Manager {
* @return A String list with all the names
*/
public List<String> getGroupNames() {
final List<String> names = new ArrayList<>(groups.size());
for (RegionGroup group : groups.values()) {
names.add(group.getName());
}
return names;
return getGroups().stream().map(RegionGroup::getName).collect(Collectors.toList());
}
/**
@@ -307,22 +289,7 @@ public class FileManager extends Manager {
if (event.isCancelled()) {
return event;
}
final String name = region.getName().toLowerCase();
// Forcefully remove regions from cache.
buyRegions.remove(name);
rentRegions.remove(name);
final GeneralRegion existing;
if (region instanceof BuyRegion) {
existing = buyRegions.put(region.getName().toLowerCase(), (BuyRegion) region);
} else if (region instanceof RentRegion) {
existing = rentRegions.put(region.getName().toLowerCase(), (RentRegion) region);
} else {
existing = null;
}
if (existing != null) {
AreaShop.debug("FileManager: Region ", existing.getName(), " was overwritten.");
}
allRegions.put(region.getName().toLowerCase(), region);
regions.put(region.getName().toLowerCase(), region);
Bukkit.getPluginManager().callEvent(new AddedRegionEvent(region));
return event;
}
@@ -436,9 +403,8 @@ public class FileManager extends Manager {
}
region.resetRegionFlags();
rentRegions.remove(region.getLowerCaseName());
buyRegions.remove(region.getLowerCaseName());
allRegions.remove(region.getLowerCaseName());
regions.remove(region.getLowerCaseName());
// Remove file
File file = new File(plugin.getDataFolder() + File.separator + AreaShop.regionsFolder + File.separator + region.getLowerCaseName() + ".yml");
if (file.exists()) {
@@ -585,24 +551,24 @@ public class FileManager extends Manager {
}
this.saveWorldGuardRegions();
final AtomicInteger processed = new AtomicInteger();
for (GeneralRegion region : allRegions.values()) {
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 = allRegions.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;
}
@@ -613,7 +579,7 @@ public class FileManager extends Manager {
if (isSaveGroupsRequired()) {
saveGroupsNow();
}
for (GeneralRegion region : allRegions.values()) {
for (GeneralRegion region : getRegions()) {
if (region.isSaveRequired()) {
region.saveNow();
}
@@ -640,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");
}
}
@@ -695,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);
}
/**
@@ -866,6 +848,7 @@ public class FileManager extends Manager {
* @return true if succeeded, otherwise false
*/
public boolean loadGroupsFile() {
boolean result = true;
File groupFile = new File(groupsPath);
if (groupFile.exists() && groupFile.isFile()) {
try (
@@ -883,19 +866,14 @@ public class FileManager extends Manager {
RegionGroup group = new RegionGroup(plugin, groupName);
groups.put(groupName, group);
}
return true;
return result;
}
/**
* Load all region files.
*/
public void loadRegionFiles() {
buyRegions.values().forEach(GeneralRegion::invalidate);
buyRegions.clear();
rentRegions.values().forEach(GeneralRegion::invalidate);
rentRegions.clear();
buyRegions.clear();
allRegions.clear();
regions.clear();
final File file = new File(regionsPath);
if (!file.exists()) {
if (!file.mkdirs()) {
@@ -908,24 +886,10 @@ public class FileManager extends Manager {
}
}
private GeneralRegion loadRegion(YamlConfiguration config) {
// Construct the correct type of region
String type = config.getString("general.type");
GeneralRegion region;
if (RegionType.RENT.getValue().equals(type)) {
region = new RentRegion(config);
} else if (RegionType.BUY.getValue().equals(type)) {
region = new BuyRegion(config);
} else {
return null;
}
return region;
}
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;
}
@@ -953,11 +917,17 @@ public class FileManager extends Manager {
}
// Construct the correct type of region
GeneralRegion region = loadRegion(regionConfig);
if (region == null) {
String type = regionConfig.getString("general.type");
GeneralRegion region;
if (RegionType.RENT.getValue().equals(type)) {
region = new RentRegion(regionConfig);
} else if (RegionType.BUY.getValue().equals(type)) {
region = new BuyRegion(regionConfig);
} else {
noRegionType.add(regionFile.getPath());
continue;
}
// Check consistency
boolean added = false;
if (region.getName() == null) {
@@ -998,7 +968,7 @@ public class FileManager extends Manager {
boolean noWorldRegions = !noWorld.isEmpty();
while (!noWorld.isEmpty()) {
List<GeneralRegion> toDisplay = new ArrayList<>(noWorld.size());
List<GeneralRegion> toDisplay = new ArrayList<>();
String missingWorld = noWorld.get(0).getWorldName();
toDisplay.add(noWorld.get(0));
for (int i = 1; i < noWorld.size(); i++) {
@@ -1018,7 +988,7 @@ public class FileManager extends Manager {
}
if (!incorrectDuration.isEmpty()) {
List<String> incorrectDurationNames = new ArrayList<>(incorrectDuration.size());
List<String> incorrectDurationNames = new ArrayList<>();
for (GeneralRegion region : incorrectDuration) {
incorrectDurationNames.add(region.getName());
}
@@ -1326,7 +1296,7 @@ public class FileManager extends Manager {
// Add 'general.lastActive' to rented/bought regions (initialize at current time)
if (fileStatus == null || fileStatus < 3) {
for (GeneralRegion region : allRegions.values()) {
for (GeneralRegion region : getRegions()) {
region.updateLastActiveTime();
}
// Update versions file to 3
@@ -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.");
}
}
}
@@ -16,550 +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.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();
// 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();
//AreaShop.debug("currentTime=" + Calendar.getInstance().getTimeInMillis() + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (Calendar.getInstance().getTimeInMillis()-player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
if(Calendar.getInstance().getTimeInMillis() > (lastPlayed + inactiveSetting)) {
AreaShop.info("Region " + getName() + " unrented because of inactivity for player " + getPlayerName());
AreaShop.debug("currentTime=" + Calendar.getInstance().getTimeInMillis() + ", getLastPlayed()=" + lastPlayed + ", timeInactive=" + (Calendar.getInstance().getTimeInMillis() - player.getLastPlayed()) + ", inactiveSetting=" + inactiveSetting);
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);
}
}
@@ -1,5 +1,6 @@
package me.wiefferink.areashop.regions;
import co.aikar.taskchain.TaskChain;
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.AreaShop;
@@ -30,113 +31,24 @@ import org.bukkit.util.Vector;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.temporal.ChronoField;
import java.util.*;
import java.util.concurrent.CompletableFuture;
public abstract class GeneralRegion implements GeneralRegionInterface, Comparable<GeneralRegion>, ReplacementProvider {
static final AreaShop plugin = AreaShop.getInstance();
protected final YamlConfiguration config;
final YamlConfiguration config;
private boolean saveRequired = false;
private boolean deleted = false;
private boolean invalidated = false;
private long volume = -1;
private transient boolean saveRequired = false;
private transient boolean deleted = false;
private transient long volume = -1;
private final String name;
private final String worldName;
private Map<Class<? extends RegionFeature>, RegionFeature> features;
// Enum for region types
public enum RegionType {
RENT("rent"),
BUY("buy");
private final String value;
RegionType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// Enum for schematic event types
public enum RegionEvent {
CREATED("created"),
DELETED("deleted"),
RENTED("rented"),
EXTENDED("extended"),
UNRENTED("unrented"),
BOUGHT("bought"),
SOLD("sold"),
RESELL("resell");
private final String value;
RegionEvent(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// Enum for Region states
public enum RegionState {
FORRENT("forrent"),
RENTED("rented"),
FORSALE("forsale"),
SOLD("sold"),
RESELL("resell");
private final String value;
RegionState(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// Enum for click types
public enum ClickType {
RIGHTCLICK("rightClick"),
LEFTCLICK("leftClick"),
SHIFTRIGHTCLICK("shiftRightClick"),
SHIFTLEFTCLICK("shiftLeftClick");
private final String value;
ClickType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// Enum for limit types
public enum LimitType {
RENTS("rents"),
BUYS("buys"),
TOTAL("total"),
EXTEND("extend");
private final String value;
LimitType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
/**
* Constructor, used to restore regions from disk at startup.
*
@@ -145,6 +57,8 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
public GeneralRegion(YamlConfiguration config) {
this.config = config;
setup();
this.name = config.getString("general.name");
this.worldName = config.getString("general.world");
}
/**
@@ -155,12 +69,18 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*/
public GeneralRegion(String name, World world) {
config = new YamlConfiguration();
setSetting("general.name", name);
setSetting("general.world", world.getName());
setSetting("general.type", getType().getValue().toLowerCase());
this.name = name;
this.worldName = world.getName();
writeSettings();
setup();
}
private void writeSettings() {
setSetting("general.name", name);
setSetting("general.world", worldName);
setSetting("general.type", getType().getValue().toLowerCase());
}
/**
* Shared setup of all constructors.
*/
@@ -185,7 +105,12 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
* @return The feature (either just instantiated or cached)
*/
public <T extends RegionFeature> T getFeature(Class<T> clazz) {
return clazz.cast(features.computeIfAbsent(clazz, (unused) -> plugin.getFeatureManager().getRegionFeature(this, clazz)));
RegionFeature result = features.get(clazz);
if (result == null) {
result = plugin.getFeatureManager().getRegionFeature(this, clazz);
features.put(clazz, result);
}
return clazz.cast(result);
}
/**
@@ -215,8 +140,6 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return getFeature(TeleportFeature.class);
}
// ABSTRACT
/**
* Get the region type of the region.
*
@@ -231,8 +154,6 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*/
public abstract boolean isAvailable();
// Sorting by name
/**
* Compare this region to another region by name.
*
@@ -254,6 +175,8 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return region instanceof GeneralRegion && ((GeneralRegion) region).getName().equals(getName());
}
// ABSTRACT
@Override
public int hashCode() {
return getName().hashCode();
@@ -264,10 +187,12 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*
* @return The config file that stores the region information
*/
public YamlConfiguration getConfig() {
public final YamlConfiguration getConfig() {
return config;
}
// Sorting by name
/**
* Broadcast an event to indicate that region settings have been changed.
* This will update region flags, signs, etc.
@@ -281,7 +206,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*
* @param event The update event that should be broadcasted
*/
public void notifyAndUpdate(NotifyRegionEvent event) {
public <T> void notifyAndUpdate(NotifyRegionEvent<T> event) {
Bukkit.getPluginManager().callEvent(event);
update();
}
@@ -293,8 +218,6 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*/
public abstract RegionState getState();
// GETTERS
/**
* Check if the region has been deleted.
*
@@ -311,14 +234,6 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
deleted = true;
}
public boolean invalidated() {
return invalidated;
}
public void invalidate() {
invalidated = true;
}
/**
* Get the name of the region.
*
@@ -326,7 +241,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*/
@Override
public String getName() {
return config.getString("general.name");
return name;
}
/**
@@ -347,6 +262,8 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return getBooleanSetting("general.enableRestore");
}
// GETTERS
/**
* Get the time that the player was last active.
*
@@ -413,6 +330,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
* @param player Player to check ownership for
* @return true if the player currently rents or buys this region
*/
@Deprecated
public boolean isOwner(OfflinePlayer player) {
return isOwner(player.getUniqueId());
}
@@ -423,8 +341,9 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
* @param player Player to check ownership for
* @return true if the player currently rents or buys this region
*/
@Deprecated
public boolean isOwner(UUID player) {
return (this instanceof RentRegion && ((RentRegion) this).isRenter(player)) || (this instanceof BuyRegion && ((BuyRegion) this).isBuyer(player));
return Objects.equals(getOwner(), player);
}
/**
@@ -432,6 +351,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*
* @return The UUID of the owner of this region
*/
@Deprecated
public UUID getOwner() {
if (this instanceof RentRegion) {
return ((RentRegion) this).getRenter();
@@ -445,25 +365,20 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*
* @param player The player that should be the owner
*/
public void setOwner(UUID player) {
if (this instanceof RentRegion) {
((RentRegion) this).setRenter(player);
} else {
((BuyRegion) this).setBuyer(player);
}
}
@Deprecated
public abstract void setOwner(UUID player);
/**
* Get the landlord of this region (the player that receives any revenue from this region).
*
* @return The UUID of the landlord of this region
* @return The UUID of the landlord of this region (nullable)
*/
public UUID getLandlord() {
String landlord = getStringSetting("general.landlord");
if (landlord != null && !landlord.isEmpty()) {
try {
return UUID.fromString(landlord);
} catch (IllegalArgumentException e) {
} catch (IllegalArgumentException ignored) {
// Incorrect UUID
}
}
@@ -478,6 +393,30 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return null;
}
/**
* Get the landlord of this region (the player that receives any revenue from this region) asynchronously.
*
* @return A future containing the The UUID (which may be null) of the landlord of this region
*/
public CompletableFuture<UUID> getLandLordAsync() {
String landlord = getStringSetting("general.landlord");
if (landlord != null && !landlord.isEmpty()) {
try {
return CompletableFuture.completedFuture(UUID.fromString(landlord));
} catch (IllegalArgumentException ignored) {
}
}
String landlordName = getStringSetting("general.landlordName");
CompletableFuture<UUID> future = new CompletableFuture<>();
if (landlordName != null && !landlordName.isEmpty()) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(landlordName);
future.complete(offlinePlayer.hasPlayedBefore() ? offlinePlayer.getUniqueId() : null);
});
}
return future;
}
/**
* Get the name of the landlord.
*
@@ -624,11 +563,8 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*/
public List<String> getGroupNames() {
List<String> result = new ArrayList<>();
// Inline #getGroups
for (RegionGroup group : plugin.getFileManager().getGroups()) {
if (group.isMember(this)) {
result.add(group.getName());
}
for (RegionGroup group : getGroups()) {
result.add(group.getName());
}
return result;
}
@@ -663,21 +599,20 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
// Date/time
case AreaShop.tagEpoch:
return Calendar.getInstance().getTimeInMillis();
case AreaShop.tagMillisecond:
return Calendar.getInstance().get(Calendar.MILLISECOND);
return System.currentTimeMillis();
case AreaShop.tagSecond:
return Calendar.getInstance().get(Calendar.SECOND);
return Instant.now().getEpochSecond();
case AreaShop.tagMinute:
return Calendar.getInstance().get(Calendar.MINUTE);
return Instant.now().get(ChronoField.MINUTE_OF_DAY);
case AreaShop.tagHour:
return Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
return Instant.now().get(ChronoField.HOUR_OF_DAY);
case AreaShop.tagDay:
return Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
return Instant.now().get(ChronoField.DAY_OF_MONTH);
case AreaShop.tagMonth:
return Calendar.getInstance().get(Calendar.MONTH) + 1;
return Instant.now().get(ChronoField.MONTH_OF_YEAR);
case AreaShop.tagYear:
return Calendar.getInstance().get(Calendar.YEAR);
return Instant.now().get(ChronoField.YEAR);
case AreaShop.tagDateTime:
return new SimpleDateFormat(plugin.getConfig().getString("timeFormatChat")).format(Calendar.getInstance().getTime());
case AreaShop.tagDateTimeShort:
@@ -737,11 +672,26 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
}
/**
* Check now if the player has been inactive for too long, unrent/sell will happen when true.
*
* @return true if the region has been unrented/sold, otherwise false
* Check now if the player has been inactive for too long.
*/
public abstract boolean checkInactive();
public boolean checkInactive() {
return checkInactivity(Bukkit.getOfflinePlayer(getOwner()));
}
/**
* Check now if the player has been inactive for too long.
*
* @param player The offline player instance of {@link #getOwner()}, used to prevent sync IO lookups.
* @return Returns true if the player has been inactive for too long.
*/
public abstract boolean checkInactivity(final OfflinePlayer player);
/**
* Handler for when the inactivity thereshold has been reached.
*
* @return Returns whether the operation was handled successfully.
*/
public abstract boolean handleInactivity();
/**
* Method to send a message to a CommandSender, using chatprefix if it is a player.
@@ -781,7 +731,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
*
* @param restore true, false or general
*/
public void setRestoreSetting(Boolean restore) {
public void setRestoreSetting(boolean restore) {
setSetting("general.enableRestore", restore);
}
@@ -824,39 +774,6 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return true;
}
/**
* Save all blocks in a region for restoring later.
*
* @param fileName The name of the file to save to (extension and folder will be added)
* @return true if the region has been saved properly, otherwise false
*/
public CompletableFuture<Boolean> saveRegionBlocksAsync(String fileName) {
// Check if the region is correct
ProtectedRegion region = getRegion();
if (region == null) {
AreaShop.debug("Region '" + getName() + "' does not exist in WorldGuard, save failed");
return CompletableFuture.completedFuture(false);
}
// The path to save the schematic
File saveFile = new File(plugin.getFileManager()
.getSchematicFolder() + File.separator + fileName);
// Create parent directories
File parent = saveFile.getParentFile();
if (parent != null && !parent.exists()) {
if (!parent.mkdirs()) {
AreaShop.warn("Did not save region " + getName() + ", schematic directory could not be created: " + saveFile
.getAbsolutePath());
return CompletableFuture.completedFuture(false);
}
}
boolean result = plugin.getWorldEditHandler().saveRegionBlocks(saveFile, this);
if (result) {
AreaShop.debug("Saved schematic for region " + getName());
}
// FIXME
return CompletableFuture.completedFuture(result);
}
/**
* Restore all blocks in a region for restoring later.
*
@@ -880,30 +797,6 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return result;
}
/**
* Restore all blocks in a region for restoring later.
*
* @param fileName The name of the file to save to (extension and folder will be added)
* @return true if the region has been restored properly, otherwise false
*/
public CompletableFuture<Boolean> restoreRegionBlocksAsync(String fileName) {
if (getRegion() == null) {
AreaShop.debug("Region '" + getName() + "' does not exist in WorldGuard, restore failed");
return CompletableFuture.completedFuture(false);
}
// The path to save the schematic
File restoreFile = new File(plugin.getFileManager()
.getSchematicFolder() + File.separator + fileName);
boolean result = plugin.getWorldEditHandler().restoreRegionBlocks(restoreFile, this);
if (result) {
AreaShop.debug("Restored schematic for region " + getName());
// Workaround for signs inside the region in combination with async restore of plugins like AsyncWorldEdit and FastAsyncWorldEdit
Bukkit.getScheduler().runTaskLater(plugin, getSignsFeature()::update, 10);
}
return CompletableFuture.completedFuture(result);
}
/**
* Reset all flags of the region.
*/
@@ -928,7 +821,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
* @return true if a save is required because some data changed, otherwise false
*/
public boolean isSaveRequired() {
return saveRequired;
return saveRequired && !isDeleted();
}
/**
@@ -937,50 +830,60 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
* @return true if the region is saved successfully, otherwise false
*/
public boolean saveNow() {
File file = new File(plugin.getFileManager().getRegionFolder() + File.separator + getName().toLowerCase() + ".yml");
if (isDeleted()) {
return !file.exists() || file.delete();
return false;
}
saveRequired = false;
File file = new File(plugin.getFileManager().getRegionFolder() + File.separator + getName().toLowerCase() + ".yml");
try {
config.save(file);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public CompletableFuture<Boolean> saveNowAsync() {
final String fileName = plugin.getFileManager().getRegionFolder() + File.separator + getName().toLowerCase() + ".yml";
final File file = new File(fileName);
final CompletableFuture<Boolean> future = new CompletableFuture<>();
if (isDeleted()) {
Utils.newChain().async(() -> future.complete(!file.exists() || file.delete())).execute();
return future;
}
final YamlConfiguration clone = new YamlConfiguration();
final String fileName = plugin.getFileManager().getRegionFolder() + File.separator + getName().toLowerCase() + ".yml";
try {
clone.loadFromString(config.saveToString());
} catch (InvalidConfigurationException ex) {
ex.printStackTrace();
return CompletableFuture.completedFuture(false);
}
Utils.newChain().async(() -> {
final CompletableFuture<Boolean> future = new CompletableFuture<>();
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
try {
clone.save(file);
clone.save(new File(fileName));
future.complete(true);
} catch (IOException ex) {
ex.printStackTrace();
future.complete(false);
}
}).execute();
});
return future;
}
// CONFIG
public TaskChain<Boolean> saveNowAsync(final TaskChain<?> chain) {
final YamlConfiguration clone = new YamlConfiguration();
final String fileName = plugin.getFileManager().getRegionFolder() + File.separator + getName().toLowerCase() + ".yml";
try {
clone.loadFromString(config.saveToString());
} catch (InvalidConfigurationException ex) {
ex.printStackTrace();
return chain.sync((unused) -> false);
}
return chain.async((unused) -> {
try {
clone.save(new File(fileName));
return true;
} catch (IOException ex) {
ex.printStackTrace();
return false;
}
});
}
/**
* Get a boolean setting for this region, defined as follows
@@ -994,7 +897,8 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
public boolean getBooleanSetting(String path) {
if (config.isSet(path)) {
if (config.isString(path)) {
return config.getString(path).equalsIgnoreCase("true");
String s = config.getString(path);
return s != null && s.equalsIgnoreCase("true");
}
return config.getBoolean(path);
}
@@ -1162,6 +1066,9 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
}
}
// CONFIG
/**
* Get a string list setting for this region, defined as follows
* - If the region has the setting in its own file (/regions/regionName.yml), use that
@@ -1302,9 +1209,6 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
this.saveRequired();
}
// LIMIT FUNCTIONS
/**
* Check if the player can buy/rent this region, detailed info in the result object.
*
@@ -1339,9 +1243,9 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
typePath = "buys";
}
// Check all limitgroups the player has
List<String> groups = new ArrayList<>(plugin.getConfig().getConfigurationSection("limitGroups").getKeys(false));
Collection<String> groups = plugin.getConfig().getConfigurationSection("limitGroups").getKeys(false);
while (!groups.isEmpty()) {
String group = groups.get(0);
String group = groups.iterator().next();
if (plugin.hasPermission(offlinePlayer, "areashop.limits." + group) && this.matchesLimitGroup(group)) {
String pathPrefix = "limitGroups." + group + ".";
if (!plugin.getConfig().isInt(pathPrefix + "total")) {
@@ -1370,8 +1274,9 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
String typeHighestGroup = group;
groups.remove(group);
// Get the highest number from the groups of the same category
List<String> groupsCopy = new ArrayList<>(groups);
for (String checkGroup : groupsCopy) {
Iterator<String> iterator = groups.iterator();
while (iterator.hasNext()) {
String checkGroup = iterator.next();
if (plugin.hasPermission(offlinePlayer, "areashop.limits." + checkGroup) && this.matchesLimitGroup(checkGroup)) {
if (limitGroupsOfSameCategory(group, checkGroup)) {
groups.remove(checkGroup);
@@ -1391,7 +1296,7 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
}
}
} else {
groups.remove(checkGroup);
iterator.remove();;
}
}
// Check if the limits stop the player from buying the region
@@ -1417,84 +1322,6 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return new LimitResult(true, null, 0, 0, null);
}
/**
* Class to store the result of a limits check.
*/
public static class LimitResult {
private final boolean actionAllowed;
private final LimitType limitingFactor;
private final int maximum;
private final int current;
private final String limitingGroup;
/**
* Constructor.
*
* @param actionAllowed has the action been allowed?
* @param limitingFactor The LimitType that has prevented the action (if actionAllowed is false)
* @param maximum The maximum number of regions allowed (if actionAllowed is false)
* @param current The current number of regions the player has (if actionAllowed is false)
* @param limitingGroup The group that is enforcing this limit (if actionAllowed is false)
*/
public LimitResult(boolean actionAllowed, LimitType limitingFactor, int maximum, int current, String limitingGroup) {
this.actionAllowed = actionAllowed;
this.limitingFactor = limitingFactor;
this.maximum = maximum;
this.current = current;
this.limitingGroup = limitingGroup;
}
/**
* Check if the action is allowed.
*
* @return true if the actions is allowed, otherwise false
*/
public boolean actionAllowed() {
return actionAllowed;
}
/**
* Get the type of the factor that is limiting the action, assuming actionAllowed() is false.
*
* @return The type of the limiting factor
*/
public LimitType getLimitingFactor() {
return limitingFactor;
}
/**
* Get the maximum number of the group that is the limiting factor, assuming actionAllowed() is false.
*
* @return The maximum
*/
public int getMaximum() {
return maximum;
}
/**
* Get the current number of regions in the group that is the limiting factor, assuming actionAllowed() is false.
*
* @return The current number of regions the player has
*/
public int getCurrent() {
return current;
}
/**
* Get the name of the group that is limiting the action, assuming actionAllowed() is false.
*
* @return The name of the group
*/
public String getLimitingGroup() {
return limitingGroup;
}
@Override
public String toString() {
return "actionAllowed=" + actionAllowed + ", limitingFactor=" + limitingFactor + ", maximum=" + maximum + ", current=" + current + ", limitingGroup=" + limitingGroup;
}
}
/**
* Checks if two limitGroups are of the same category (same groups and worlds lists).
*
@@ -1558,6 +1385,9 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return false;
}
// LIMIT FUNCTIONS
/**
* Checks an event and handles saving to and restoring from schematic for it.
*
@@ -1580,17 +1410,15 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
// Save the region if needed
if (save != null && !save.isEmpty()) {
save = Message.fromString(save).replacements(this).getSingle();
saveRegionBlocksAsync(save);
saveRegionBlocks(save);
}
// Restore the region if needed
if (restore != null && !restore.isEmpty()) {
restore = Message.fromString(restore).replacements(this).getSingle();
restoreRegionBlocksAsync(restore);
restoreRegionBlocks(restore);
}
}
// COMMAND EXECUTING
/**
* Run commands as the CommandsSender, replacing all tags with the relevant values.
*
@@ -1708,6 +1536,178 @@ public abstract class GeneralRegion implements GeneralRegionInterface, Comparabl
return region.volume();
}
}
// COMMAND EXECUTING
// Enum for region types
public enum RegionType {
RENT("rent"),
BUY("buy");
private final String value;
RegionType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// Enum for schematic event types
public enum RegionEvent {
CREATED("created"),
DELETED("deleted"),
RENTED("rented"),
EXTENDED("extended"),
UNRENTED("unrented"),
BOUGHT("bought"),
SOLD("sold"),
RESELL("resell");
private final String value;
RegionEvent(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// Enum for Region states
public enum RegionState {
FORRENT("forrent"),
RENTED("rented"),
FORSALE("forsale"),
SOLD("sold"),
RESELL("resell");
private final String value;
RegionState(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// Enum for click types
public enum ClickType {
RIGHTCLICK("rightClick"),
LEFTCLICK("leftClick"),
SHIFTRIGHTCLICK("shiftRightClick"),
SHIFTLEFTCLICK("shiftLeftClick");
private final String value;
ClickType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// Enum for limit types
public enum LimitType {
RENTS("rents"),
BUYS("buys"),
TOTAL("total"),
EXTEND("extend");
private final String value;
LimitType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
/**
* Class to store the result of a limits check.
*/
public static class LimitResult {
private final boolean actionAllowed;
private final LimitType limitingFactor;
private final int maximum;
private final int current;
private final String limitingGroup;
/**
* Constructor.
*
* @param actionAllowed has the action been allowed?
* @param limitingFactor The LimitType that has prevented the action (if actionAllowed is false)
* @param maximum The maximum number of regions allowed (if actionAllowed is false)
* @param current The current number of regions the player has (if actionAllowed is false)
* @param limitingGroup The group that is enforcing this limit (if actionAllowed is false)
*/
public LimitResult(boolean actionAllowed, LimitType limitingFactor, int maximum, int current, String limitingGroup) {
this.actionAllowed = actionAllowed;
this.limitingFactor = limitingFactor;
this.maximum = maximum;
this.current = current;
this.limitingGroup = limitingGroup;
}
/**
* Check if the action is allowed.
*
* @return true if the actions is allowed, otherwise false
*/
public boolean actionAllowed() {
return actionAllowed;
}
/**
* Get the type of the factor that is limiting the action, assuming actionAllowed() is false.
*
* @return The type of the limiting factor
*/
public LimitType getLimitingFactor() {
return limitingFactor;
}
/**
* Get the maximum number of the group that is the limiting factor, assuming actionAllowed() is false.
*
* @return The maximum
*/
public int getMaximum() {
return maximum;
}
/**
* Get the current number of regions in the group that is the limiting factor, assuming actionAllowed() is false.
*
* @return The current number of regions the player has
*/
public int getCurrent() {
return current;
}
/**
* Get the name of the group that is limiting the action, assuming actionAllowed() is false.
*
* @return The name of the group
*/
public String getLimitingGroup() {
return limitingGroup;
}
@Override
public String toString() {
return "actionAllowed=" + actionAllowed + ", limitingFactor=" + limitingFactor + ", maximum=" + maximum + ", current=" + current + ", limitingGroup=" + limitingGroup;
}
}
}
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;
}
}
@@ -0,0 +1,103 @@
package me.wiefferink.areashop.tools;
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.bukkit.Metrics;
import java.util.HashMap;
import java.util.Map;
public class Analytics {
private Analytics() {
}
/**
* 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", () -> AreaShop.getInstance().getFileManager().getRegions().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", () -> AreaShop.getInstance().getFileManager().getBuys().size()));
// Language
metrics.addCustomChart(new Metrics.SimplePie("language", () -> AreaShop.getInstance().getConfig().getString("language")));
// 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", () -> getStateStats().forrent));
metrics.addCustomChart(new Metrics.SingleLineChart("rented_region_count", () -> getStateStats().rented));
metrics.addCustomChart(new Metrics.SingleLineChart("forsale_region_count", () -> getStateStats().forsale));
metrics.addCustomChart(new Metrics.SingleLineChart("sold_region_count", () -> getStateStats().sold));
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)};
}
}
@@ -1,11 +1,11 @@
package me.wiefferink.areashop.tools;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import java.io.BufferedReader;
import java.io.IOException;
@@ -18,242 +18,234 @@ import java.util.logging.Logger;
public class GithubUpdateCheck {
public static final String API_HOST = "https://api.github.com/repos";
public static final String API_LATEST_RELEASE = "releases/latest";
public static final String USER_AGENT = "GithubUpdateCheck by NLThijs48";
public static final boolean DEBUG = false;
private static final Gson GSON = new GsonBuilder().create();
public static final String API_HOST = "https://api.github.com/repos";
public static final String API_LATEST_RELEASE = "releases/latest";
public static final String USER_AGENT = "GithubUpdateCheck by NLThijs48";
public static final boolean DEBUG = false;
private final String author;
private final String repository;
private final Plugin plugin;
private final Logger logger;
private final String currentVersion;
private URL url;
private VersionComparator versionComparator;
// Status
private boolean checking;
private boolean error;
private boolean hasUpdate;
private String latestVersion;
private final String author;
private final String repository;
private final Plugin plugin;
private final Logger logger;
private URL url;
private VersionComparator versionComparator;
/**
* Create a new GithubUpdateCheck with the required information.
*
* @param plugin The plugin to create it for (used for logging and checking version)
* @param author The author of the plugin as used on Github
* @param repository The repository name of the plugin on Github
*/
public GithubUpdateCheck(Plugin plugin, String author, String repository) {
this.plugin = plugin;
this.logger = plugin.getLogger();
this.author = author;
this.repository = repository;
this.currentVersion = plugin.getDescription().getVersion();
this.versionComparator = (latest, current) ->
!latest.equalsIgnoreCase(current);
// Status
private boolean checking;
private boolean error;
private boolean hasUpdate;
private String latestVersion;
private final String currentVersion;
this.checking = false;
this.error = false;
this.hasUpdate = false;
}
/**
* Create a new GithubUpdateCheck with the required information.
* @param plugin The plugin to create it for (used for logging and checking version)
* @param author The author of the plugin as used on Github
* @param repository The repository name of the plugin on Github
*/
public GithubUpdateCheck(Plugin plugin, String author, String repository) {
this.plugin = plugin;
this.logger = plugin.getLogger();
this.author = author;
this.repository = repository;
this.currentVersion = plugin.getDescription().getVersion();
this.versionComparator = (latest, current) ->
!latest.equalsIgnoreCase(current);
/**
* Change the version comparator.
*
* @param versionComparator VersionComparator to use for checking if one version is newer than the other
* @return this
*/
public GithubUpdateCheck withVersionComparator(VersionComparator versionComparator) {
this.versionComparator = versionComparator;
return this;
}
this.checking = false;
this.error = false;
this.hasUpdate = false;
}
/**
* Check if an update is available.
*/
public void checkUpdate() {
checkUpdate(null);
}
/**
* Change the version comparator.
* @param versionComparator VersionComparator to use for checking if one version is newer than the other
* @return this
*/
public GithubUpdateCheck withVersionComparator(VersionComparator versionComparator) {
this.versionComparator = versionComparator;
return this;
}
/**
* Check if an update is available.
*
* @param callback Callback to execute when the update check is done
* @return GithubUpdateCheck containing the status of the check
*/
public GithubUpdateCheck checkUpdate(UpdateCallback callback) {
checking = true;
final GithubUpdateCheck self = this;
// Check for update on async thread
Utils.newChain()
.async(() -> {
try {
try {
String rawUrl = API_HOST + "/" + author + "/" + repository + "/" + API_LATEST_RELEASE;
url = new URL(rawUrl);
} catch (MalformedURLException e) {
logger.severe("Invalid url: '" + url + "', are the author '" + author + "' and repository '" + repository + "' correct?");
error = true;
return;
}
/**
* Check if an update is available.
*/
public void checkUpdate() {
checkUpdate(null);
}
try {
URLConnection conn = url.openConnection();
// Give up after 15 seconds
conn.setConnectTimeout(15000);
// Identify ourselves
conn.addRequestProperty("User-Agent", USER_AGENT);
// Make sure we access the correct api version
conn.addRequestProperty("Accept", "application/vnd.github.v3+json");
// We want to read the result
conn.setDoOutput(true);
// Open connection
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String response = reader.readLine();
debug("Response:", response);
/**
* Check if an update is available.
* @param callback Callback to execute when the update check is done
* @return GithubUpdateCheck containing the status of the check
*/
public GithubUpdateCheck checkUpdate(UpdateCallback callback) {
checking = true;
final GithubUpdateCheck self = this;
// Check for update on asyn thread
new BukkitRunnable() {
@Override
public void run() {
try {
try {
String rawUrl = API_HOST + "/" + author + "/" + repository + "/" + API_LATEST_RELEASE;
url = new URL(rawUrl);
} catch(MalformedURLException e) {
logger.severe("Invalid url: '" + url + "', are the author '" + author + "' and repository '" + repository + "' correct?");
error = true;
return;
}
JsonObject latestRelease = GSON.fromJson(response, JsonObject.class);
try {
URLConnection conn = url.openConnection();
// Give up after 15 seconds
conn.setConnectTimeout(15000);
// Identify ourselves
conn.addRequestProperty("User-Agent", USER_AGENT);
// Make sure we access the correct api version
conn.addRequestProperty("Accept", "application/vnd.github.v3+json");
// We want to read the result
conn.setDoOutput(true);
// Open connection
try(BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String response = reader.readLine();
debug("Response:", response);
if (latestRelease.isJsonNull()) {
logger.warning("Failed to get api response from " + url);
error = true;
return;
}
debug("json: " + latestRelease.toString());
JSONObject latestRelease = (JSONObject)JSONValue.parse(response);
// Latest version
latestVersion = latestRelease.get("tag_name").getAsString();
debug("Tag name:", latestVersion);
if(latestRelease.isEmpty()) {
logger.warning("Failed to get api response from " + url);
error = true;
return;
}
debug("json: " + latestRelease.toJSONString());
// Current version
debug("Plugin version:", currentVersion);
// Latest version
latestVersion = (String)latestRelease.get("tag_name");
debug("Tag name:", latestVersion);
// Compare version
hasUpdate = versionComparator.isNewer(latestVersion, currentVersion);
}
} catch (IOException e) {
logger.severe("Failed to get latest release:" + ExceptionUtils
.getStackTrace(e));
error = true;
} catch (ClassCastException e) {
logger.info("Unexpected structure of the result, failed to parse it");
error = true;
}
} finally {
checking = false;
debug("result:", self);
}
}
).syncLast(x -> {
if (callback != null) {
callback.run(this);
}
}).execute();
return this;
}
// Current version
debug("Plugin version:", currentVersion);
/**
* Check if an update check is running.
*
* @return true if an update check is running
*/
public boolean isChecking() {
return checking;
}
// Compare version
hasUpdate = versionComparator.isNewer(latestVersion, currentVersion);
}
} catch(IOException e) {
logger.severe("Failed to get latest release:" + ExceptionUtils.getStackTrace(e));
error = true;
} catch(ClassCastException e) {
logger.info("Unexpected structure of the result, failed to parse it");
error = true;
}
} finally {
checking = false;
debug("result:", self);
if(callback != null) {
// Switch back to main thread and call the callback
new BukkitRunnable() {
@Override
public void run() {
callback.run(self);
}
}.runTask(plugin);
}
}
}
}.runTaskAsynchronously(plugin);
return this;
}
/**
* Check if the update check failed.
*
* @return true if the update check failed (an error message has been logged)
*/
public boolean hasFailed() {
return error;
}
/**
* Check if an update check is running.
* @return true if an update check is running
*/
public boolean isChecking() {
return checking;
}
/**
* Check if an update has been found.
*
* @return true if an update has been found
*/
public boolean hasUpdate() {
return hasUpdate;
}
/**
* Check if the update check failed.
* @return true if the update check failed (an error message has been logged)
*/
public boolean hasFailed() {
return error;
}
/**
* Get the repository that this update checker is checking.
*
* @return Used repository
*/
public String getRepository() {
return repository;
}
/**
* Check if an update has been found.
* @return true if an update has been found
*/
public boolean hasUpdate() {
return hasUpdate;
}
/**
* Get the author that this update checker is checking.
*
* @return Used author
*/
public String getAuthor() {
return author;
}
/**
* Get the repository that this update checker is checking.
* @return Used repository
*/
public String getRepository() {
return repository;
}
/**
* Get the current version.
*
* @return Current version of the plugin
*/
public String getCurrentVersion() {
return currentVersion;
}
/**
* Get the author that this update checker is checking.
* @return Used author
*/
public String getAuthor() {
return author;
}
/**
* Get the latest version.
*
* @return Latest version of the plugin (if checking is complete)
*/
public String getLatestVersion() {
return latestVersion;
}
/**
* Get the current version.
* @return Current version of the plugin
*/
public String getCurrentVersion() {
return currentVersion;
}
/**
* Print a debug message if DEBUG is enabled.
*
* @param message Message to print
*/
private void debug(Object... message) {
if (DEBUG) {
logger.info("[" + this.getClass().getSimpleName() + "] [DEBUG] " + StringUtils
.join(message, " "));
}
}
/**
* Get the latest version.
* @return Latest version of the plugin (if checking is complete)
*/
public String getLatestVersion() {
return latestVersion;
}
@Override
public String toString() {
return "GithubUpdateCheck(" + StringUtils.join(Arrays.asList(
"author=" + author,
"repository=" + repository,
"plugin=" + plugin.getName(),
"checking=" + checking,
"hasUpdate=" + hasUpdate,
"error=" + error,
"currentVersion=" + currentVersion,
"latestVersion=" + latestVersion
), ", ") + ")";
}
public interface VersionComparator {
/**
* Check if a version should be considered 'newer' than another.
* @param latestVersion Version that is available on Github
* @param currentVersion Version of the current plugin
* @return true if the latestVersion is newer than the localVersion, otherwise false
*/
boolean isNewer(String latestVersion, String currentVersion);
}
public interface VersionComparator {
/**
* Check if a version should be considered 'newer' than another.
*
* @param latestVersion Version that is available on Github
* @param currentVersion Version of the current plugin
* @return true if the latestVersion is newer than the localVersion, otherwise false
*/
boolean isNewer(String latestVersion, String currentVersion);
}
public interface UpdateCallback {
void run(GithubUpdateCheck result);
}
public interface UpdateCallback {
void run(GithubUpdateCheck result);
}
/**
* Print a debug message if DEBUG is enabled.
* @param message Message to print
*/
private void debug(Object... message) {
if(DEBUG) {
logger.info("[" + this.getClass().getSimpleName() + "] [DEBUG] " + StringUtils.join(message, " "));
}
}
@Override
public String toString() {
return "GithubUpdateCheck(" + StringUtils.join(Arrays.asList(
"author=" + author,
"repository=" + repository,
"plugin=" + plugin.getName(),
"checking=" + checking,
"hasUpdate=" + hasUpdate,
"error=" + error,
"currentVersion=" + currentVersion,
"latestVersion=" + latestVersion
), ", ") + ")";
}
}
@@ -3,7 +3,6 @@ package me.wiefferink.areashop.tools;
import me.wiefferink.areashop.AreaShop;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Tag;
import java.util.Arrays;
import java.util.HashSet;
@@ -12,6 +11,52 @@ import java.util.List;
public class Materials {
private Materials() {
}
private static final HashSet<String> WALL_SIGN_TYPES = new HashSet<>(Arrays.asList(
// 1.14+ types
"ACACIA_WALL_SIGN",
"BIRCH_WALL_SIGN",
"DARK_OAK_WALL_SIGN",
"JUNGLE_WALL_SIGN",
"OAK_WALL_SIGN",
"SPRUCE_WALL_SIGN",
// Legacy types
"LEGACY_WALL_SIGN",
"WALL_SIGN"
));
private static final HashSet<String> FLOOR_SIGN_TYPES = new HashSet<>(Arrays.asList(
// 1.14+ types
"ACACIA_SIGN",
"BIRCH_SIGN",
"DARK_OAK_SIGN",
"JUNGLE_SIGN",
"OAK_SIGN",
"SPRUCE_SIGN",
// Legacy types
"LEGACY_SIGN",
"LEGACY_SIGN_POST",
"SIGN",
"SIGN_POST"
));
private static boolean legacyMaterials = false;
static {
List<String> legacyMaterialVersions = Arrays.asList("1.7", "1.8", "1.9", "1.10", "1.11", "1.12");
for(String legacyMaterialVersion : legacyMaterialVersions) {
String version = Bukkit.getBukkitVersion();
// Detects '1.8', '1.8.3', '1.8-pre1' style versions
if(version.equals(legacyMaterialVersion)
|| version.startsWith(legacyMaterialVersion + ".")
|| version.startsWith(legacyMaterialVersion + "-")) {
legacyMaterials = true;
break;
}
}
}
/**
@@ -24,12 +69,46 @@ public class Materials {
if (!isSign(name)) {
return null;
}
try {
return Material.matchMaterial(name);
} catch (IllegalArgumentException ignored) {
AreaShop.debug("Materials.get() null result:", name);
return null;
Material result = null;
if (legacyMaterials) {
// 1.12 and lower just know SIGN_POST, WALL_SIGN and SIGN
if (FLOOR_SIGN_TYPES.contains(name)) {
result = Material.getMaterial("SIGN_POST");
} else if (WALL_SIGN_TYPES.contains(name)) {
result = Material.getMaterial("WALL_SIGN");
if (result == null) {
result = Material.getMaterial("SIGN");
}
}
} else {
// Try saved name (works for wood types on 1.14, regular types for below)
result = Material.getMaterial(name);
if (result == null) {
// Cases for 1.13, which don't know wood types, but need new materials
if (FLOOR_SIGN_TYPES.contains(name)) {
// SIGN -> OAK_SIGN for 1.14
result = Material.getMaterial("OAK_SIGN");
// Fallback for 1.13
if (result == null) {
result = Material.getMaterial("SIGN");
}
} else if (WALL_SIGN_TYPES.contains(name)) {
// WALL_SIGN -> OAK_WALL_SIGN for 1.14
result = Material.getMaterial("OAK_WALL_SIGN");
// Fallback for 1.13
if (result == null) {
result = Material.getMaterial("WALL_SIGN");
}
}
}
}
if (result == null) {
AreaShop.debug("Materials.get() null result:", name, "legacyMaterials:", legacyMaterials);
}
return result;
}
/**
@@ -38,7 +117,7 @@ public class Materials {
* @return true if the given material is a sign
*/
public static boolean isSign(Material material) {
return Tag.SIGNS.isTagged(material) || Tag.WALL_SIGNS.isTagged(material);
return isSign(material.name());
}
/**
@@ -47,12 +126,7 @@ public class Materials {
* @return true if the given material is a sign
*/
public static boolean isSign(String name) {
try {
return isSign(Material.matchMaterial(name));
} catch (IllegalArgumentException ex) {
AreaShop.debug("Invalid Material: " + name + " passed when performing sign check.");
return false;
}
return name != null && name.toUpperCase().contains("SIGN");
}
}
@@ -1,152 +0,0 @@
package me.wiefferink.areashop.tools;
/**
* Parse math from a string
* Adapted from:
* https://stackoverflow.com/questions/3422673/how-to-evaluate-a-math-expression-given-in-string-form
*/
public final class OperandParser {
private final String str;
private OperandParser(final String str) {
this.str = str;
}
public static double parse(final String str) throws ParseException {
return new OperandParser(str).parse();
}
private int pos = -1, ch;
private void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
private boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
private double parse() throws ParseException {
nextChar();
double x = parseExpression();
if (pos < str.length())
{
throw new ParseException("Unexpected: " + (char) ch);
}
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)`
// | number | functionName factor | factor `^` factor
private double parseExpression() throws ParseException {
double x = parseTerm();
for (; ; ) {
if (eat('+')) {
// addition
x += parseTerm();
}
else if (eat('-')) {
// subtraction
x -= parseTerm();
}
else return x;
}
}
private double parseTerm() throws ParseException {
double x = parseFactor();
for (; ; ) {
if (eat('*')) {
// multiplication
x *= parseFactor();
}
else if (eat('/')) {
// division
x /= parseFactor();
}
else return x;
}
}
private double parseFactor() throws ParseException {
if (eat('+')) {
// unary plus
return parseFactor();
}
if (eat('-')) {
// unary minus
return -parseFactor();
}
double x;
int startPos = this.pos;
if (eat('(')) {
// parentheses
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') {
// numbers
while ((ch >= '0' && ch <= '9') || ch == '.') {
nextChar();
}
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') {
// functions
while (ch >= 'a' && ch <= 'z') {
nextChar();
}
String func = str.substring(startPos, this.pos);
x = parseFactor();
x = switch (func) {
case "sqrt" -> Math.sqrt(x);
case "sin" -> Math.sin(Math.toRadians(x));
case "cos" -> Math.cos(Math.toRadians(x));
case "tan" -> Math.tan(Math.toRadians(x));
default -> throw new ParseException("Unknown function: " + func);
};
} else {
throw new ParseException("Unexpected: " + (char) ch);
}
if (eat('^')) {
// exponentiation
x = Math.pow(x, parseFactor());
}
return x;
}
public static class ParseException extends Exception {
public ParseException() {
}
public ParseException(String message) {
super(message);
}
public ParseException(String message, Throwable cause) {
super(message, cause);
}
public ParseException(Throwable cause) {
super(cause);
}
public ParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
}
@@ -12,10 +12,7 @@ import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.interactivemessenger.Log;
import me.wiefferink.interactivemessenger.processing.Message;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
@@ -25,6 +22,10 @@ import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
@@ -45,6 +46,7 @@ public class Utils {
private static Set<String> weeks;
private static Set<String> months;
private static Set<String> years;
private static ScriptEngine scriptEngine;
private static Map<Double, String> suffixes;
// Not used
@@ -154,8 +156,19 @@ public class Utils {
*
* @return Online players
*/
@SuppressWarnings("unchecked")
public static Collection<? extends Player> getOnlinePlayers() {
return Bukkit.getOnlinePlayers();
try {
Method onlinePlayerMethod = Server.class.getMethod("getOnlinePlayers");
if (onlinePlayerMethod.getReturnType().equals(Collection.class)) {
return ((Collection<? extends Player>) onlinePlayerMethod.invoke(Bukkit.getServer()));
} else {
return Arrays.asList((Player[]) onlinePlayerMethod.invoke(Bukkit.getServer()));
}
} catch (Exception ex) {
AreaShop.debug("getOnlinePlayers error: " + ex.getMessage());
}
return new HashSet<>();
}
/**
@@ -766,11 +779,16 @@ public class Utils {
return Double.parseDouble(input);
}
// Lazy init scriptEngine
if (scriptEngine == null) {
scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript");
}
// Evaluate expression
Object result;
try {
result = OperandParser.parse(input);
} catch (OperandParser.ParseException e) {
result = scriptEngine.eval(input);
} catch (ScriptException e) {
AreaShop.warn("Price of region", region.getName(), "is set with an invalid expression: '" + input + "', exception:", ExceptionUtils.getStackTrace(e));
return 99999999999.0; // High fallback for safety
}
@@ -810,15 +828,29 @@ public class Utils {
* @return the name of the player
*/
public static String toName(UUID uuid) {
if (uuid == null) {
return "";
} else {
if (uuid != null) {
String name = Bukkit.getOfflinePlayer(uuid).getName();
if (name != null) {
return name;
}
return "";
}
return "";
}
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;
}
/**
@@ -849,19 +881,20 @@ public class Utils {
if (batchSize < 1) {
throw new IllegalArgumentException("Invalid batch size!");
}
final Queue<E> cloned = new LinkedList<>(objects);
final List<E> cloned = new ArrayList<>(objects);
final CompletableFuture<Object> future = new CompletableFuture<>();
BukkitRunnable runnable = new BukkitRunnable() {
int lastIndex;
@Override
public void run() {
if (cloned.isEmpty()) {
if (lastIndex >= cloned.size() - 1) {
future.complete(true);
cancel();
return;
}
for (int i = 0; i < batchSize && !cloned.isEmpty(); i++) {
task.accept(cloned.remove());
}
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|%'
+276 -273
View File
@@ -1,273 +1,276 @@
name: AreaShop
main: me.wiefferink.areashop.AreaShop
version: "@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
-50
View File
@@ -1,50 +0,0 @@
plugins {
id 'java'
}
group = "me.wiefferink.areashop"
version = '2.6.5'
subprojects {
plugins.apply('java')
repositories {
mavenCentral()
maven {
name = 'paper-repo'
url = uri('https://papermc.io/repo/repository/maven-public/')
}
maven {
name = 'spigot-repo'
url = uri('https://hub.spigotmc.org/nexus/content/repositories/snapshots/')
}
maven {
name = 'jitpack'
url = uri('https://jitpack.io')
content {
includeGroupByRegex("com\\.github.*")
}
}
maven {
name = 'aikar-repo'
url = uri('https://repo.aikar.co/content/groups/aikar/')
}
maven {
name = 'enginehub-repo'
url = uri('https://maven.enginehub.org/repo/')
}
}
tasks.withType(JavaCompile) {
tasks.withType(JavaCompile) {
javaToolchains.compilerFor {
JavaLanguageVersion.of(16)
}
options.release.set(16)
options.encoding = 'UTF-8'
options.fork = true
options.deprecation = true
}
}
}
-2
View File
@@ -1,2 +0,0 @@
org.gradle.parallel=true
org.gradle.java.installations.auto-download=false
Binary file not shown.
-5
View File
@@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
-185
View File
@@ -1,185 +0,0 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
Vendored
-89
View File
@@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+53
View File
@@ -0,0 +1,53 @@
<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>
<artifactId>areashop-fawe-1-15</artifactId>
<packaging>jar</packaging>
<name>AreaShop FastAsyncWorldEdit 1.14+</name>
<version>latest</version>
<parent>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-parent</artifactId>
<version>1.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<repositories>
<repository>
<id>IS-Repo</id>
<url>https://mvn.intellectualsites.com/content/repositories/releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.14.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.intellectualsites.fawe</groupId>
<artifactId>FAWE-Bukkit</artifactId>
<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>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-interface</artifactId>
<version>latest</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,234 @@
package me.wiefferink.areashop.handlers.fawe.v1_15;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.object.FaweLimit;
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;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
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.worldguard.protection.regions.ProtectedRegion;
import com.sk89q.worldguard.protection.regions.RegionType;
import me.wiefferink.areashop.interfaces.AreaShopInterface;
import me.wiefferink.areashop.interfaces.GeneralRegionInterface;
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 java.io.*;
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
// This would not allow for shaping the schematic to a polygon region though
public class FastAsyncWorldEditHandler extends WorldEditInterface {
public FastAsyncWorldEditHandler(AreaShopInterface pluginInterface) {
super(pluginInterface);
}
@Override
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()) {
if (new File(rawFile.getAbsolutePath() + "." + formatOption.getPrimaryFileExtension())
.exists()) {
file = new File(
rawFile.getAbsolutePath() + "." + formatOption.getPrimaryFileExtension());
format = formatOption;
break;
}
}
if (file == null) {
pluginInterface.getLogger().info("Did not restore region " + regionInterface.getName()
+ ", schematic file does not exist: " + rawFile.getAbsolutePath());
return false;
}
pluginInterface.debugI("Trying to restore region", regionInterface.getName(), " from file",
file.getAbsolutePath(), "with format", format.getName());
com.sk89q.worldedit.world.World world = null;
if (regionInterface.getName() != null) {
world = BukkitAdapter.adapt(regionInterface.getWorld());
}
if (world == null) {
pluginInterface.getLogger().info(
"Did not restore region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return false;
}
final FaweLimit limit = new FaweLimit();
limit.MAX_CHANGES = pluginInterface.getConfig().getInt("maximumBlocks");
EditSession editSession = FaweAPI.getEditSessionBuilder(world).limit(limit).build();
editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE);
ProtectedRegion region = regionInterface.getRegion();
// Get the origin and size of the region
BlockVector3 origin = BlockVector3
.at(region.getMinimumPoint().getBlockX(), region.getMinimumPoint().getBlockY(),
region.getMinimumPoint().getBlockZ());
// Read the schematic and paste it into the world
try (Closer closer = Closer.create()) {
FileInputStream fis = closer.register(new FileInputStream(file));
BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(bis));
ClipboardReader reader = new FastSchematicReader(nbtStream);
LocalSession session =
new LocalSession(pluginInterface.getWorldEdit().getLocalConfiguration());
Clipboard clipboard = reader.read();
if (clipboard.getDimensions().getY() != regionInterface.getHeight()
|| clipboard.getDimensions().getX() != regionInterface.getWidth()
|| clipboard.getDimensions().getZ() != regionInterface.getDepth()) {
pluginInterface.getLogger().warning(
"Size of the region " + regionInterface.getName()
+ " is not the same as the schematic to restore!");
pluginInterface.debugI(
"schematic|region, x:" + clipboard.getDimensions().getX() + "|"
+ regionInterface.getWidth() + ", y:" + clipboard.getDimensions().getY()
+ "|" + regionInterface.getHeight() + ", z:" + clipboard.getDimensions()
.getZ() + "|" + regionInterface.getDepth());
}
clipboard.setOrigin(clipboard.getMinimumPoint());
ClipboardHolder clipboardHolder = new ClipboardHolder(clipboard);
session.setBlockChangeLimit(pluginInterface.getConfig().getInt("maximumBlocks"));
session.setClipboard(clipboardHolder);
// Build operation
BlockTransformExtent extent = new BlockTransformExtent(clipboard);
ForwardExtentCopy copy =
new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(),
editSession, origin);
copy.setTransform(clipboardHolder.getTransform());
// Mask to region (for polygon and other weird shaped regions)
// TODO make this more efficient (especially for polygon regions)
if (region.getType() != RegionType.CUBOID) {
copy.setSourceMask(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 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)
file = new File(file.getAbsolutePath() + "." + format.getPrimaryFileExtension());
pluginInterface.debugI("Trying to save region", regionInterface.getName(), " to file",
file.getAbsolutePath(), "with format", format.getName());
com.sk89q.worldedit.world.World world = null;
if (regionInterface.getWorld() != null) {
world = BukkitAdapter.adapt(regionInterface.getWorld());
}
if (world == null) {
pluginInterface.getLogger().warning(
"Did not save region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return false;
}
EditSession editSession =
pluginInterface.getWorldEdit().getWorldEdit().getEditSessionFactory()
.getEditSession(world, pluginInterface.getConfig().getInt("maximumBlocks"));
// 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(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
public WorldEditSelection getPlayerSelection(Player player) {
try {
Region region = pluginInterface.getWorldEdit().getSession(player)
.getSelection(BukkitAdapter.adapt(player.getWorld()));
return new WorldEditSelection(player.getWorld(),
BukkitAdapter.adapt(player.getWorld(), region.getMinimumPoint()),
BukkitAdapter.adapt(player.getWorld(), region.getMaximumPoint()));
} catch (IncompleteRegionException e) {
return null;
}
}
}
-16
View File
@@ -1,16 +0,0 @@
repositories {
maven {
name = 'IS-Repo'
url = uri('https://mvn.intellectualsites.com/content/repositories/releases/')
}
}
dependencies {
compileOnly project(':areashop-interface')
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
compileOnly ('com.intellectualsites.fawe:FAWE-Core:1.16-725')
compileOnly ('com.intellectualsites.fawe:FAWE-Bukkit:1.16-725')
compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.5'
}
description = 'AreaShop FastAsyncWorldEdit 1.16'
@@ -1,289 +0,0 @@
package me.wiefferink.areashop.handlers.fawe.v1_16;
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.boydti.fawe.util.EditSessionBuilder;
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.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.*;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
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;
import me.wiefferink.areashop.interfaces.GeneralRegionInterface;
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.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
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
// This would not allow for shaping the schematic to a polygon region though
public class FastAsyncWorldEditHandler extends WorldEditInterface {
public FastAsyncWorldEditHandler(AreaShopInterface pluginInterface) {
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) {
File file = null;
ClipboardFormat format = null;
for (ClipboardFormat formatOption : BuiltInClipboardFormat.values()) {
if (new File(rawFile.getAbsolutePath() + "." + formatOption.getPrimaryFileExtension())
.exists()) {
file = new File(
rawFile.getAbsolutePath() + "." + formatOption.getPrimaryFileExtension());
format = formatOption;
break;
}
}
if (file == null) {
pluginInterface.getLogger().info("Did not restore region " + regionInterface.getName()
+ ", schematic file does not exist: " + rawFile.getAbsolutePath());
return CompletableFuture.completedFuture(false);
}
pluginInterface.debugI("Trying to restore region", regionInterface.getName(), " from file",
file.getAbsolutePath(), "with format", format.getName());
com.sk89q.worldedit.world.World world = null;
if (regionInterface.getName() != null) {
world = BukkitAdapter.adapt(regionInterface.getWorld());
}
if (world == null) {
pluginInterface.getLogger().info(
"Did not restore region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return CompletableFuture.completedFuture(false);
}
final CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
final FaweLimit limit = new FaweLimit();
limit.MAX_CHANGES = pluginInterface.getConfig().getInt("maximumBlocks");
EditSession editSession = new EditSessionBuilder(world).limit(limit).relightMode(RelightMode.OPTIMAL).build();
editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE);
ProtectedRegion region = regionInterface.getRegion();
final RegionType regionType = region.getType();
// Get the origin and size of the region
final BlockVector3 origin = BlockVector3
.at(region.getMinimumPoint().getBlockX(), region.getMinimumPoint().getBlockY(),
region.getMinimumPoint().getBlockZ());
final RegionBoundMask mask = new RegionBoundMask(region);
final File finalFile = file;
final String regionName = regionInterface.getName();
final double regionWidth = regionInterface.getWidth(), regionHeight = regionInterface.getHeight(), regionDepth = regionInterface.getDepth();
final LocalSession session =
new LocalSession(pluginInterface.getWorldEdit().getLocalConfiguration());
FaweAPI.getTaskManager().async(() -> {
// Read the schematic and paste it into the world
try (Closer closer = Closer.create()) {
FileInputStream fis = closer.register(new FileInputStream(finalFile));
BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(bis));
ClipboardReader reader = new FastSchematicReader(nbtStream);
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
BlockTransformExtent extent = new BlockTransformExtent(clipboard);
ForwardExtentCopy copy =
new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(),
editSession, origin);
copy.setTransform(clipboardHolder.getTransform());
// Mask to region (for polygon and other weird shaped regions)
// TODO make this more efficient (especially for polygon regions)
if (regionType != RegionType.CUBOID) {
copy.setSourceMask(mask);
}
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
pluginInterface.getLogger().warning(
"exceeded the block limit while restoring schematic of " + regionInterface.getName()
+ ", limit in exception: " + e.getBlockLimit() + ", limit passed by AreaShop: "
+ pluginInterface.getConfig().getInt("maximumBlocks"));
CompletableFuture.completedFuture(false);
} catch (IOException e) {
pluginInterface.getLogger().warning(
"An error occured while restoring schematic of " + regionInterface.getName()
+ ", enable debug to see the complete stacktrace");
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
CompletableFuture.completedFuture(false);
} catch (Exception e) {
pluginInterface.getLogger()
.warning("crashed during restore of " + regionInterface.getName());
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
CompletableFuture.completedFuture(false);
}
editSession.flushQueue();
CompletableFuture.completedFuture(true);
});
return completableFuture;
}
@Override
public boolean restoreRegionBlocks(File rawFile, GeneralRegionInterface regionInterface) {
return restoreRegionBlocksAsync(rawFile, regionInterface).join();
}
@Override
@Beta
public CompletableFuture<Boolean> saveRegionBlocksAsync(File file, GeneralRegionInterface regionInterface) {
// TODO implement using the FastAsyncWorldEdit api to save async
ClipboardFormat format = BuiltInClipboardFormat.MINECRAFT_STRUCTURE;
// TODO allow selecting FAWE format in the config? (when enabled you cannot go back to vanilla WorldEdit easily)
file = new File(file.getAbsolutePath() + "." + format.getPrimaryFileExtension());
pluginInterface.debugI("Trying to save region", regionInterface.getName(), " to file",
file.getAbsolutePath(), "with format", format.getName());
com.sk89q.worldedit.world.World world = null;
if (regionInterface.getWorld() != null) {
world = BukkitAdapter.adapt(regionInterface.getWorld());
}
if (world == null) {
pluginInterface.getLogger().warning(
"Did not save region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return CompletableFuture.completedFuture(false);
}
final FaweLimit limit = new FaweLimit();
limit.MAX_CHANGES = pluginInterface.getConfig().getInt("maximumBlocks");
EditSession editSession = new EditSessionBuilder(world).relightMode(RelightMode.OPTIMAL).limit(limit).build();
final ProtectedRegion region = regionInterface.getRegion();
final BlockVector3 min = region.getMinimumPoint(), max = region.getMaximumPoint();
final String regionName = regionInterface.getName();
final World finalWorld = world;
final CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
final File finalFile = file;
FaweAPI.getTaskManager().async(() -> {
// Create a clipboard
CuboidRegion selection =
new CuboidRegion(finalWorld, min, max);
Clipboard clipboard = new MemoryOptimizedClipboard(selection);
clipboard.setOrigin(min);
ForwardExtentCopy copy = new ForwardExtentCopy(editSession,
selection.clone(), clipboard,
min);
try {
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
pluginInterface.getLogger().warning(
"Exceeded the block limit while saving schematic of " + regionName
+ ", limit in exception: " + e.getBlockLimit() + ", limit passed by AreaShop: "
+ pluginInterface.getConfig().getInt("maximumBlocks"));
completableFuture.complete(false);
}
try (Closer closer = Closer.create()) {
FileOutputStream fos = closer.register(new FileOutputStream(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();
}
@Override
public WorldEditSelection getPlayerSelection(Player player) {
try {
Region region = pluginInterface.getWorldEdit().getSession(player)
.getSelection(BukkitAdapter.adapt(player.getWorld()));
return new WorldEditSelection(player.getWorld(),
BukkitAdapter.adapt(player.getWorld(), region.getMinimumPoint()),
BukkitAdapter.adapt(player.getWorld(), region.getMaximumPoint()));
} catch (IncompleteRegionException e) {
return null;
}
}
}
-16
View File
@@ -1,16 +0,0 @@
repositories {
maven {
name = 'IS-Repo'
url = uri('https://mvn.intellectualsites.com/content/repositories/releases/')
}
}
dependencies {
compileOnly project(':areashop-interface')
compileOnly('org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT')
compileOnly("com.fastasyncworldedit:FAWE-Core:1.17-138")
compileOnly("com.fastasyncworldedit:FAWE-Bukkit:1.17-138")
compileOnly('com.sk89q.worldguard:worldguard-bukkit:7.0.5')
}
description = 'AreaShop FastAsyncWorldEdit 1.17'
@@ -1,290 +0,0 @@
package me.wiefferink.areashop.handlers.fawe.v1_17;
import com.fastasyncworldedit.core.FaweAPI;
import com.fastasyncworldedit.core.extent.clipboard.MemoryOptimizedClipboard;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReader;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.object.FaweLimit;
import com.fastasyncworldedit.core.util.EditSessionBuilder;
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.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.*;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
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;
import me.wiefferink.areashop.interfaces.GeneralRegionInterface;
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.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
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
// This would not allow for shaping the schematic to a polygon region though
public class FastAsyncWorldEditHandler extends WorldEditInterface {
public FastAsyncWorldEditHandler(AreaShopInterface pluginInterface) {
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) {
File file = null;
ClipboardFormat format = null;
for (ClipboardFormat formatOption : BuiltInClipboardFormat.values()) {
if (new File(rawFile.getAbsolutePath() + "." + formatOption.getPrimaryFileExtension())
.exists()) {
file = new File(
rawFile.getAbsolutePath() + "." + formatOption.getPrimaryFileExtension());
format = formatOption;
break;
}
}
if (file == null) {
pluginInterface.getLogger().info("Did not restore region " + regionInterface.getName()
+ ", schematic file does not exist: " + rawFile.getAbsolutePath());
return CompletableFuture.completedFuture(false);
}
pluginInterface.debugI("Trying to restore region", regionInterface.getName(), " from file",
file.getAbsolutePath(), "with format", format.getName());
com.sk89q.worldedit.world.World world = null;
if (regionInterface.getName() != null) {
world = BukkitAdapter.adapt(regionInterface.getWorld());
}
if (world == null) {
pluginInterface.getLogger().info(
"Did not restore region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return CompletableFuture.completedFuture(false);
}
final CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
final FaweLimit limit = new FaweLimit();
limit.MAX_CHANGES = pluginInterface.getConfig().getInt("maximumBlocks");
EditSession editSession = new EditSessionBuilder(world).limit(limit).relightMode(RelightMode.OPTIMAL).build();
editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE);
ProtectedRegion region = regionInterface.getRegion();
final RegionType regionType = region.getType();
// Get the origin and size of the region
final BlockVector3 origin = BlockVector3
.at(region.getMinimumPoint().getBlockX(), region.getMinimumPoint().getBlockY(),
region.getMinimumPoint().getBlockZ());
final RegionBoundMask mask = new RegionBoundMask(region);
final File finalFile = file;
final String regionName = regionInterface.getName();
final double regionWidth = regionInterface.getWidth(), regionHeight = regionInterface.getHeight(), regionDepth = regionInterface.getDepth();
final LocalSession session =
new LocalSession(pluginInterface.getWorldEdit().getLocalConfiguration());
FaweAPI.getTaskManager().async(() -> {
// Read the schematic and paste it into the world
try (Closer closer = Closer.create()) {
FileInputStream fis = closer.register(new FileInputStream(finalFile));
BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(bis));
ClipboardReader reader = new FastSchematicReader(nbtStream);
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
BlockTransformExtent extent = new BlockTransformExtent(clipboard);
ForwardExtentCopy copy =
new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(),
editSession, origin);
copy.setTransform(clipboardHolder.getTransform());
// Mask to region (for polygon and other weird shaped regions)
// TODO make this more efficient (especially for polygon regions)
if (regionType != RegionType.CUBOID) {
copy.setSourceMask(mask);
}
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
pluginInterface.getLogger().warning(
"exceeded the block limit while restoring schematic of " + regionInterface.getName()
+ ", limit in exception: " + e.getBlockLimit() + ", limit passed by AreaShop: "
+ pluginInterface.getConfig().getInt("maximumBlocks"));
CompletableFuture.completedFuture(false);
} catch (IOException e) {
pluginInterface.getLogger().warning(
"An error occured while restoring schematic of " + regionInterface.getName()
+ ", enable debug to see the complete stacktrace");
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
CompletableFuture.completedFuture(false);
} catch (Exception e) {
pluginInterface.getLogger()
.warning("crashed during restore of " + regionInterface.getName());
pluginInterface.debugI(ExceptionUtils.getStackTrace(e));
CompletableFuture.completedFuture(false);
}
editSession.flushQueue();
CompletableFuture.completedFuture(true);
});
return completableFuture;
}
@Override
public boolean restoreRegionBlocks(File rawFile, GeneralRegionInterface regionInterface) {
return restoreRegionBlocksAsync(rawFile, regionInterface).join();
}
@Override
@Beta
public CompletableFuture<Boolean> saveRegionBlocksAsync(File file, GeneralRegionInterface regionInterface) {
// TODO implement using the FastAsyncWorldEdit api to save async
ClipboardFormat format = BuiltInClipboardFormat.MINECRAFT_STRUCTURE;
// TODO allow selecting FAWE format in the config? (when enabled you cannot go back to vanilla WorldEdit easily)
file = new File(file.getAbsolutePath() + "." + format.getPrimaryFileExtension());
pluginInterface.debugI("Trying to save region", regionInterface.getName(), " to file",
file.getAbsolutePath(), "with format", format.getName());
com.sk89q.worldedit.world.World world = null;
if (regionInterface.getWorld() != null) {
world = BukkitAdapter.adapt(regionInterface.getWorld());
}
if (world == null) {
pluginInterface.getLogger().warning(
"Did not save region " + regionInterface.getName() + ", world not found: "
+ regionInterface.getWorldName());
return CompletableFuture.completedFuture(false);
}
final FaweLimit limit = new FaweLimit();
limit.MAX_CHANGES = pluginInterface.getConfig().getInt("maximumBlocks");
EditSession editSession = new EditSessionBuilder(world).relightMode(RelightMode.OPTIMAL).limit(limit).build();
final ProtectedRegion region = regionInterface.getRegion();
final BlockVector3 min = region.getMinimumPoint(), max = region.getMaximumPoint();
final String regionName = regionInterface.getName();
final World finalWorld = world;
final CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
final File finalFile = file;
FaweAPI.getTaskManager().async(() -> {
// Create a clipboard
CuboidRegion selection =
new CuboidRegion(finalWorld, min, max);
Clipboard clipboard = new MemoryOptimizedClipboard(selection);
clipboard.setOrigin(min);
ForwardExtentCopy copy = new ForwardExtentCopy(editSession,
selection.clone(), clipboard,
min);
try {
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
pluginInterface.getLogger().warning(
"Exceeded the block limit while saving schematic of " + regionName
+ ", limit in exception: " + e.getBlockLimit() + ", limit passed by AreaShop: "
+ pluginInterface.getConfig().getInt("maximumBlocks"));
completableFuture.complete(false);
}
try (Closer closer = Closer.create()) {
FileOutputStream fos = closer.register(new FileOutputStream(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();
}
@Override
public WorldEditSelection getPlayerSelection(Player player) {
try {
Region region = pluginInterface.getWorldEdit().getSession(player)
.getSelection(BukkitAdapter.adapt(player.getWorld()));
return new WorldEditSelection(player.getWorld(),
BukkitAdapter.adapt(player.getWorld(), region.getMinimumPoint()),
BukkitAdapter.adapt(player.getWorld(), region.getMaximumPoint()));
} catch (IncompleteRegionException e) {
return null;
}
}
}
-7
View File
@@ -1,7 +0,0 @@
dependencies {
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
compileOnly 'com.sk89q.worldedit:worldedit-bukkit:7.2.5-SNAPSHOT'
compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.5'
}
description = 'AreaShop Interface'
+39
View File
@@ -0,0 +1,39 @@
<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>
<artifactId>areashop-interface</artifactId>
<packaging>jar</packaging>
<name>AreaShop Interface</name>
<version>latest</version>
<parent>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-parent</artifactId>
<version>1.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.13.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-bukkit</artifactId>
<version>7.1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId>
<version>7.0.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -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();
@@ -1,27 +0,0 @@
package me.wiefferink.areashop.interfaces;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
public interface BlockBehaviourHelper {
default boolean canPlace(Location location, Material material) {
return canPlace(location, material.createBlockData());
}
boolean canPlace(Location location, BlockData blockData);
/**
* Check if a sign is valid.
*
* @param block The block
* @return Returns true if the sign can exist in that location, false otherwise.
*/
boolean isBlockValid(Block block);
boolean isBlockStateValid(BlockState blockState);
}
@@ -1,7 +0,0 @@
package me.wiefferink.areashop.interfaces;
public interface NMS {
BlockBehaviourHelper behaviourHelper();
}
@@ -51,9 +51,9 @@ public class RegionAccessSet {
* Get this access set as a list of player UUIDs.
* @return List of player UUIDs, first players already added by UUID, then players added by name, groups are not in the list
*/
@SuppressWarnings("deprecation")
public List<UUID> asUniqueIdList() {
List<UUID> result = new ArrayList<>(playerUniqueIds);
List<UUID> result = new ArrayList<>();
result.addAll(playerUniqueIds);
for(String playerName : playerNames) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName);
if (offlinePlayer.hasPlayedBefore()) {
@@ -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
@@ -9,7 +9,7 @@ public class WorldEditSelection {
private final Location maximum;
/**
* Create a WorldEditSelection.
* Craete a WorldEditSelection.
* @param world World the selection is in
* @param a Primary selection location
* @param b Secondary selection location
-8
View File
@@ -1,8 +0,0 @@
repositories {
mavenLocal()
}
dependencies {
compileOnly project(':areashop-interface')
compileOnly 'org.spigotmc:spigot:1.16.5-R0.1-SNAPSHOT'
}
@@ -1,46 +0,0 @@
package me.wiefferink.areashop.nms.v1_16_R3;
import me.wiefferink.areashop.interfaces.BlockBehaviourHelper;
import net.minecraft.server.v1_16_R3.BlockPosition;
import net.minecraft.server.v1_16_R3.IBlockData;
import net.minecraft.server.v1_16_R3.World;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_16_R3.block.CraftBlock;
import org.bukkit.craftbukkit.v1_16_R3.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_16_R3.block.data.CraftBlockData;
import java.util.Objects;
public class BlockBehaviourHelperImpl implements BlockBehaviourHelper {
@Override
public boolean canPlace(Location location, BlockData blockData) {
Objects.requireNonNull(location.getWorld(), "Null World!");
final BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ());
final CraftWorld craftWorld = (CraftWorld) location.getWorld();
final World world = craftWorld.getHandle();
final CraftBlockData craftBlockData = (CraftBlockData) blockData;
final IBlockData ibd = craftBlockData.getState();
return ibd.canPlace(world, blockPosition);
}
@Override
public boolean isBlockValid(Block block) {
final CraftBlock craftBlock = (CraftBlock) block;
final CraftWorld craftWorld = (CraftWorld) craftBlock.getWorld();
final World world = craftWorld.getHandle();
final IBlockData ibd = craftBlock.getNMS();
return ibd.canPlace(world, craftBlock.getPosition());
}
@Override
public boolean isBlockStateValid(BlockState blockState) {
final CraftBlockState craftBlockState = (CraftBlockState) blockState;
final IBlockData ibd = craftBlockState.getHandle();
return ibd.canPlace(craftBlockState.getBlock().getCraftWorld().getHandle(), craftBlockState.getPosition());
}
}
@@ -1,15 +0,0 @@
package me.wiefferink.areashop.nms.v1_16_R3;
import me.wiefferink.areashop.interfaces.BlockBehaviourHelper;
import me.wiefferink.areashop.interfaces.NMS;
public final class NMSImpl implements NMS {
private BlockBehaviourHelper signHelper = new BlockBehaviourHelperImpl();
@Override
public BlockBehaviourHelper behaviourHelper() {
return this.signHelper;
}
}
-8
View File
@@ -1,8 +0,0 @@
repositories {
mavenLocal()
}
dependencies {
compileOnly project(':areashop-interface')
compileOnly 'org.spigotmc:spigot:1.17.1-R0.1-SNAPSHOT'
}
@@ -1,46 +0,0 @@
package me.wiefferink.areashop.nms.v1_17_R1;
import me.wiefferink.areashop.interfaces.BlockBehaviourHelper;
import net.minecraft.core.BlockPosition;
import net.minecraft.world.level.World;
import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlock;
import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
import java.util.Objects;
public class BlockBehaviourHelperImpl implements BlockBehaviourHelper {
@Override
public boolean canPlace(Location location, BlockData blockData) {
Objects.requireNonNull(location.getWorld(), "Null World!");
final BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ());
final CraftWorld craftWorld = (CraftWorld) location.getWorld();
final World world = craftWorld.getHandle();
final CraftBlockData craftBlockData = (CraftBlockData) blockData;
final IBlockData ibd = craftBlockData.getState();
return ibd.canPlace(world, blockPosition);
}
@Override
public boolean isBlockValid(Block block) {
final CraftBlock craftBlock = (CraftBlock) block;
final CraftWorld craftWorld = (CraftWorld) craftBlock.getWorld();
final World world = craftWorld.getHandle();
final IBlockData ibd = craftBlock.getNMS();
return ibd.canPlace(world, craftBlock.getPosition());
}
@Override
public boolean isBlockStateValid(BlockState blockState) {
final CraftBlockState craftBlockState = (CraftBlockState) blockState;
final IBlockData ibd = craftBlockState.getHandle();
return ibd.canPlace(craftBlockState.getBlock().getCraftWorld().getHandle(), craftBlockState.getPosition());
}
}
@@ -1,15 +0,0 @@
package me.wiefferink.areashop.nms.v1_17_R1;
import me.wiefferink.areashop.interfaces.BlockBehaviourHelper;
import me.wiefferink.areashop.interfaces.NMS;
public final class NMSImpl implements NMS {
private BlockBehaviourHelper signHelper = new BlockBehaviourHelperImpl();
@Override
public BlockBehaviourHelper behaviourHelper() {
return this.signHelper;
}
}
-8
View File
@@ -1,8 +0,0 @@
dependencies {
implementation project(':areashop-interface')
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
compileOnly 'com.sk89q.worldedit:worldedit-bukkit:7.2.5'
compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.5'
}
description = 'AreaShop WorldEdit 7'
+53
View File
@@ -0,0 +1,53 @@
<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>
<artifactId>areashop-worldedit-7</artifactId>
<packaging>jar</packaging>
<name>AreaShop WorldEdit 7</name>
<version>latest</version>
<description>
World Edit compatibility handler
- Deprecated usages on 7.2.0
- Compiles against 7.0.1, 7.1.0-SNAPSHOT, 7.2.0-SNAPSHOT
</description>
<parent>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-parent</artifactId>
<version>1.0</version>
<relativePath>../../pom.xml</relativePath>
</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>
<version>7.0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId>
<version>7.0.4-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-interface</artifactId>
<version>latest</version>
<type>jar</type>
</dependency>
</dependencies>
</project>
@@ -46,14 +46,7 @@ public class WorldEditHandler extends WorldEditInterface {
@Override
public WorldEditSelection getPlayerSelection(Player player) {
try {
final LocalSession session = pluginInterface.getWorldEdit().getSession(player);
if (session == null) {
return null;
}
Region region = session.getSelection(BukkitAdapter.adapt(player.getWorld()));
if (region == null) {
return null;
}
Region region = pluginInterface.getWorldEdit().getSession(player).getSelection(BukkitAdapter.adapt(player.getWorld()));
return new WorldEditSelection(
player.getWorld(),
BukkitAdapter.adapt(player.getWorld(), region.getMinimumPoint()),
@@ -91,21 +84,18 @@ public class WorldEditHandler extends WorldEditInterface {
pluginInterface.getLogger().info("Did not restore region " + regionInterface.getName() + ", world not found: " + regionInterface.getWorldName());
return false;
}
EditSession editSession = pluginInterface.getWorldEdit()
.getWorldEdit()
.newEditSessionBuilder()
.world(world)
.maxBlocks(pluginInterface.getConfig().getInt("maximumBlocks"))
.build();
EditSession editSession = pluginInterface.getWorldEdit().getWorldEdit() // WARNING Deprecated in 7.2.0!
.getEditSessionFactory()
.getEditSession(world, pluginInterface.getConfig().getInt("maximumBlocks"));
ProtectedRegion region = regionInterface.getRegion();
// Get the origin and size of the region
BlockVector3 origin = BlockVector3.at(region.getMinimumPoint().getBlockX(), region.getMinimumPoint().getBlockY(), region.getMinimumPoint().getBlockZ());
// Read the schematic and paste it into the world
try(Closer closer = Closer.create();
try(Closer closer = Closer.create()) {
FileInputStream fis = closer.register(new FileInputStream(file));
BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
ClipboardReader reader = format.getReader(bis)) {
ClipboardReader reader = format.getReader(bis);
//WorldData worldData = world.getWorldData();
LocalSession session = new LocalSession(pluginInterface.getWorldEdit().getLocalConfiguration());
@@ -182,12 +172,9 @@ public class WorldEditHandler extends WorldEditInterface {
pluginInterface.getLogger().warning("Did not save region " + regionInterface.getName() + ", world not found: " + regionInterface.getWorldName());
return false;
}
EditSession editSession = pluginInterface.getWorldEdit()
.getWorldEdit()
.newEditSessionBuilder()
.world(world)
.maxBlocks(pluginInterface.getConfig().getInt("maximumBlocks"))
.build();
EditSession editSession = pluginInterface.getWorldEdit().getWorldEdit() // WARNING Deprecated in 7.2.0!
.getEditSessionFactory()
.getEditSession(world, pluginInterface.getConfig().getInt("maximumBlocks"));
// Create a clipboard
CuboidRegion selection = new CuboidRegion(world, regionInterface.getRegion().getMinimumPoint(), regionInterface.getRegion().getMaximumPoint());
@@ -201,9 +188,10 @@ public class WorldEditHandler extends WorldEditInterface {
return false;
}
try(Closer closer = Closer.create(); FileOutputStream fos = closer.register(new FileOutputStream(file));
try(Closer closer = Closer.create()) {
FileOutputStream fos = closer.register(new FileOutputStream(file));
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
ClipboardWriter writer = closer.register(format.getWriter(bos));) {
ClipboardWriter writer = closer.register(format.getWriter(bos));
writer.write(clipboard);
} catch(IOException e) {
pluginInterface.getLogger().warning("An error occured while saving schematic of " + regionInterface.getName() + ", enable debug to see the complete stacktrace");
-8
View File
@@ -1,8 +0,0 @@
dependencies {
implementation project(':areashop-interface')
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
compileOnly 'com.sk89q.worldedit:worldedit-bukkit:7.2.5'
compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.5'
}
description = 'AreaShop WorldGuard 7'
+45
View File
@@ -0,0 +1,45 @@
<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>
<artifactId>areashop-worldguard-7</artifactId>
<packaging>jar</packaging>
<name>AreaShop WorldGuard 7</name>
<version>latest</version>
<parent>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-parent</artifactId>
<version>1.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.13.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-bukkit</artifactId>
<version>7.2.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId>
<version>7.0.4-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-interface</artifactId>
<version>latest</version>
</dependency>
</dependencies>
</project>
+84
View File
@@ -0,0 +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>
<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/md5sha256/AreaShop/issues</url>
<system>GitHub Issues</system>
</issueManagement>
<!--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>
<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>
<!-- FAWE Compat -->
<module>hooks/areashop-fawe-1-15</module>
<!-- World Edit Compat -->
<module>hooks/areashop-worldedit-7</module>
<!-- World Guard Compat -->
<module>hooks/areashop-worldguard-7</module>
<module>AreaShop</module>
</modules>
<build>
<defaultGoal>clean package</defaultGoal>
</build>
</project>
-17
View File
@@ -1,17 +0,0 @@
rootProject.name = 'areashop-parent'
include(':areashop-worldguard-7')
include(':areashop-interface')
include(':areashop-fawe-1-16')
include(':areashop-fawe-1-17')
include(':areashop-worldedit-7')
include(':areashop-nms-1-16-5')
include(':areashop-nms-1-17')
include(':areashop')
project(":areashop-worldguard-7").projectDir = file('hooks/areashop-worldguard-7')
project(":areashop-interface").projectDir = file('hooks/areashop-interface')
project(":areashop-fawe-1-16").projectDir = file('hooks/areashop-fawe-1-16')
project(":areashop-fawe-1-17").projectDir = file('hooks/areashop-fawe-1-17')
project(":areashop-worldedit-7").projectDir = file('hooks/areashop-worldedit-7')
project(":areashop-nms-1-16-5").projectDir = file('hooks/areashop-nms-1-16-5')
project(":areashop-nms-1-17").projectDir = file('hooks/areashop-nms-1-17')
project(":areashop").projectDir = file('AreaShop')