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
163 changed files with 19644 additions and 14106 deletions

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
---
name: Bug report
about: Report a problem in the plugin
title: ''
labels: 'NEW, Type: Bug'
assignees: ''
---
**Current behavior**
<!--- Tell what happens currently and/or show the stacktrace if an error occured -->
**Expected behavior**
<!--- Tell what you expected to happen instead -->
**Steps to reproduce**
<!--- Exactly show how this bug can be triggered -->
1.
2.
3.
4.
**Your environment**
<!--- Indicate which versions you use -->
<!--- Attention: "LATEST" or "NEWEST" are invalid answers! -->
<!--- Use '/version' for the Platform version and '/version <plugin>' for plugin versions -->
* Server platform (Bukkit, Spigot, etc.):
* Server version:
* AreaShop version:
* WorldEdit version:
* WorldGuard version:
* Vault version:
**Your config files**
<!--- Attach a '.zip' file of the '/plugins/AreaShop' and '/plugins/WorldGuard' directories to show your configuration setup -->
+15
View File
@@ -0,0 +1,15 @@
---
name: Feature request
about: Suggest an idea
title: ''
labels: 'NEW, Type: Feature'
assignees: ''
---
**Describe the problem you are trying to solve with this feature request**
<!-- When I know the problem you are trying to solve I can help you to think about the best solution -->
**Describe the solution you'd like**
<!-- Describe the config settings, commands and other features that should be added -->
@@ -0,0 +1,31 @@
---
name: Support question
about: Ask help with the setup or usage of the plugin
title: ''
labels: 'NEW, Type: Support'
assignees: ''
---
**Current behavior**
<!--- Tell what happens currently -->
**Desired behavior**
<!--- Tell what you want to happen instead -->
**Your environment**
<!--- Indicate which versions you use -->
<!--- Attention: "LATEST" or "NEWEST" are invalid answers! -->
<!--- Use '/version' for the Platform version and '/version <plugin>' for plugin versions -->
* Server platform (Bukkit, Spigot, etc.):
* Server version:
* AreaShop version:
* WorldEdit version:
* WorldGuard version:
* Vault version:
**Your config files**
<!--- Attach a '.zip' file of the '/plugins/AreaShop' and '/plugins/WorldGuard' directories to show your configuration setup -->
+32
View File
@@ -0,0 +1,32 @@
# 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
name: Java CI with Maven
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
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
+4 -12
View File
@@ -1,7 +1,6 @@
# Maven stuff
/target
target
*.pydevproject
.project
.metadata
bin/
@@ -15,19 +14,12 @@ local.properties
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
# IntelliJ IDEA
*.iml
.idea
dependency-reduced-pom.xml
dependency-reduced-pom.xml
desktop.ini
-1
View File
@@ -1 +0,0 @@
/target
+300 -230
View File
@@ -1,230 +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>
<artifactId>areashop</artifactId>
<packaging>jar</packaging>
<name>AreaShop</name>
<version>2.3.0</version> <!-- Main plugin version -->
<parent>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-parent</artifactId>
<version>parent</version>
</parent>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.7.9-R0.2</version>
<type>jar</type>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-worldguard-5</artifactId>
<version>WorldGuard-5</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-worldguard-6</artifactId>
<version>WorldGuard-6</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-worldguard-6_1_3</artifactId>
<version>WorldGuard-6_1_3</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-interface</artifactId>
<version>AreaShopInterface</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-worldedit-5</artifactId>
<version>WorldEdit-5</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>me.wiefferink</groupId>
<artifactId>areashop-worldedit-6</artifactId>
<version>WorldEdit-6</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.sk89q</groupId>
<artifactId>worldedit</artifactId>
<version>6.0.0-SNAPSHOT</version>
<scope>provided</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.sk89q</groupId>
<artifactId>worldguard</artifactId>
<version>6.0.0-SNAPSHOT</version>
<scope>provided</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>net.milkbowl.vault</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.4</version>
<scope>provided</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.0.0</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>2.10.4</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>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
</plugin>
</plugins>
</build>
</project>
<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>
File diff suppressed because it is too large. Load diff
@@ -1,746 +0,0 @@
package me.wiefferink.areashop;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.bukkit.selections.CuboidSelection;
import com.sk89q.worldedit.bukkit.selections.Selection;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.messages.Message;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import org.bukkit.*;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
public class Utils {
// Not used
private Utils() {}
private static YamlConfiguration config;
private static ArrayList<String> identifiers;
private static ArrayList<String> seconds;
private static ArrayList<String> minutes;
private static ArrayList<String> hours;
private static ArrayList<String> days;
private static ArrayList<String> weeks;
private static ArrayList<String> months;
private static ArrayList<String> years;
public static void initialize(YamlConfiguration pluginConfig) {
config = pluginConfig;
// Setup individual identifiers
seconds = new ArrayList<>(config.getStringList("seconds"));
minutes = new ArrayList<>(config.getStringList("minutes"));
hours = new ArrayList<>(config.getStringList("hours"));
days = new ArrayList<>(config.getStringList("days"));
weeks = new ArrayList<>(config.getStringList("weeks"));
months = new ArrayList<>(config.getStringList("months"));
years = new ArrayList<>(config.getStringList("years"));
// Setup all time identifiers
identifiers = new ArrayList<>();
identifiers.addAll(seconds);
identifiers.addAll(minutes);
identifiers.addAll(hours);
identifiers.addAll(days);
identifiers.addAll(weeks);
identifiers.addAll(months);
identifiers.addAll(years);
}
/**
* Create a message with a list of parts
* @param replacements The parts to use
* @param messagePart The message to use for the parts
* @return A Message object containing the parts combined into one message
*/
public static Message combinedMessage(Collection<?> replacements, String messagePart) {
return combinedMessage(replacements, messagePart, ", ");
}
/**
* Create a message with a list of parts
* @param replacements The parts to use
* @param messagePart The message to use for the parts
* @param combiner The string to use as combiner
* @return A Message object containing the parts combined into one message
*/
public static Message combinedMessage(Collection<?> replacements, String messagePart, String combiner) {
Message result = Message.none();
boolean first = true;
for(Object part : replacements) {
if(first) {
first = false;
} else {
result.append(combiner);
}
result.append(Message.fromKey(messagePart).replacements(part));
}
return result;
}
/**
* Gets the online players
* Provides backwards compatibility for 1.7- where it returns an array
* @return Online players
*/
@SuppressWarnings("unchecked")
public static Collection<? extends Player> 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<>();
}
/**
* Create a map from a location, to save it in the config
* @param location The location to transform
* @param setPitchYaw true to save the pitch and yaw, otherwise false
* @return The map with the location values
*/
public static ConfigurationSection locationToConfig(Location location, boolean setPitchYaw) {
if(location == null) {
return null;
}
ConfigurationSection result = new YamlConfiguration();
result.set("world", location.getWorld().getName());
result.set("x", location.getX());
result.set("y", location.getY());
result.set("z", location.getZ());
if(setPitchYaw) {
result.set("yaw", Float.toString(location.getYaw()));
result.set("pitch", Float.toString(location.getPitch()));
}
return result;
}
/**
* Create a map from a location, to save it in the config (without pitch and yaw)
* @param location The location to transform
* @return The map with the location values
*/
public static ConfigurationSection locationToConfig(Location location) {
return locationToConfig(location, false);
}
/**
* Create a location from a map, reconstruction from the config values
* @param config The config section to reconstruct from
* @return The location
*/
public static Location configToLocation(ConfigurationSection config) {
if(config == null
|| !config.isString("world")
|| !config.isDouble("x")
|| !config.isDouble("y")
|| !config.isDouble("z")
|| Bukkit.getWorld(config.getString("world")) == null) {
return null;
}
Location result = new Location(
Bukkit.getWorld(config.getString("world")),
config.getDouble("x"),
config.getDouble("y"),
config.getDouble("z"));
if(config.isString("yaw") && config.isString("pitch")) {
result.setPitch(Float.parseFloat(config.getString("pitch")));
result.setYaw(Float.parseFloat(config.getString("yaw")));
}
return result;
}
/**
* Create a comma-separated list
* @param input Collection of object which should be concatenated with comma's in between
* @return Innput object concatenated with comma's in between
*/
public static String createCommaSeparatedList(Collection<?> input) {
String result = "";
boolean first = true;
for(Object object : input) {
if(object != null) {
if(first) {
first = false;
result += object.toString();
} else {
result += ", " + object.toString();
}
}
}
return result;
}
/**
* Convert milliseconds to ticks
* @param milliseconds Milliseconds to convert
* @return milliseconds divided by 50 (20 ticks per second)
*/
public static long millisToTicks(long milliseconds) {
return milliseconds/50;
}
/**
* Convert milliseconds to a human readable format
* @param milliseconds The amount of milliseconds to convert
* @return A formatted string based on the language file
*/
public static String millisToHumanFormat(long milliseconds) {
long timeLeft = milliseconds+500;
// To seconds
timeLeft = timeLeft/1000;
if(timeLeft <= 0) {
return Message.fromKey("timeleft-ended").getPlain();
} else if(timeLeft == 1) {
return Message.fromKey("timeleft-second").replacements(timeLeft).getPlain();
} else if(timeLeft <= 120) {
return Message.fromKey("timeleft-seconds").replacements(timeLeft).getPlain();
}
// To minutes
timeLeft = timeLeft/60;
if(timeLeft <= 120) {
return Message.fromKey("timeleft-minutes").replacements(timeLeft).getPlain();
}
// To hours
timeLeft = timeLeft/60;
if(timeLeft <= 48) {
return Message.fromKey("timeleft-hours").replacements(timeLeft).getPlain();
}
// To days
timeLeft = timeLeft/24;
if(timeLeft <= 60) {
return Message.fromKey("timeleft-days").replacements(timeLeft).getPlain();
}
// To months
timeLeft = timeLeft/30;
if(timeLeft <= 24) {
return Message.fromKey("timeleft-months").replacements(timeLeft).getPlain();
}
// To years
timeLeft = timeLeft/12;
return Message.fromKey("timeleft-years").replacements(timeLeft).getPlain();
}
private static final BlockFace[] facings = {BlockFace.NORTH, BlockFace.NORTH_EAST, BlockFace.EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH, BlockFace.SOUTH_WEST, BlockFace.WEST, BlockFace.NORTH_WEST};
/**
* Get the facing direction based on the yaw
* @param yaw The horizontal angle that for example the player is looking
* @return The Block Face of the angle
*/
public static BlockFace yawToFacing(float yaw) {
return facings[Math.round(yaw / 45f) & 0x7];
}
// ======================================================================
// Methods to get WorldGuard or AreaShop regions by location or selection
// ======================================================================
/**
* Get all AreaShop regions intersecting with a WorldEdit selection
* @param selection The selection to check
* @return A list with all the AreaShop regions intersecting with the selection
*/
public static List<GeneralRegion> getASRegionsInSelection(Selection selection) {
ArrayList<GeneralRegion> result = new ArrayList<>();
for(ProtectedRegion region : getWERegionsInSelection(selection)) {
GeneralRegion asRegion = AreaShop.getInstance().getFileManager().getRegion(region.getId());
if(asRegion != null) {
result.add(asRegion);
}
}
return result;
}
/**
* Get all AreaShop regions containing a location
* @param location The location to check
* @return A list with all the AreaShop regions that contain the location
*/
public static List<GeneralRegion> getASRegionsByLocation(Location location) {
Selection selection = new CuboidSelection(location.getWorld(), location, location);
return getASRegionsInSelection(selection);
}
/**
* Get all WorldGuard regions intersecting with a WorldEdit selection
* @param selection The selection to check
* @return A list with all the WorldGuard regions intersecting with the selection
*/
public static List<ProtectedRegion> getWERegionsInSelection(Selection selection) {
// Get all regions inside or intersecting with the WorldEdit selection of the player
World world = selection.getWorld();
RegionManager regionManager = AreaShop.getInstance().getWorldGuard().getRegionManager(world);
ArrayList<ProtectedRegion> result = new ArrayList<>();
Location selectionMin = selection.getMinimumPoint();
Location selectionMax = selection.getMaximumPoint();
for(ProtectedRegion region : regionManager.getRegions().values()) {
BlockVector regionMin = region.getMinimumPoint();
BlockVector regionMax = region.getMaximumPoint();
if(
( // x part, resolves to true if the selection and region overlap anywhere on the x-axis
(regionMin.getBlockX() <= selectionMax.getBlockX() && regionMin.getBlockX() >= selectionMin.getBlockX())
|| (regionMax.getBlockX() <= selectionMax.getBlockX() && regionMax.getBlockX() >= selectionMin.getBlockX())
|| (selectionMin.getBlockX() >= regionMin.getBlockX() && selectionMin.getBlockX() <= regionMax.getBlockX())
|| (selectionMax.getBlockX() >= regionMin.getBlockX() && selectionMax.getBlockX() <= regionMax.getBlockX())
) && ( // Y part, resolves to true if the selection and region overlap anywhere on the y-axis
(regionMin.getBlockY() <= selectionMax.getBlockY() && regionMin.getBlockY() >= selectionMin.getBlockY())
|| (regionMax.getBlockY() <= selectionMax.getBlockY() && regionMax.getBlockY() >= selectionMin.getBlockY())
|| (selectionMin.getBlockY() >= regionMin.getBlockY() && selectionMin.getBlockY() <= regionMax.getBlockY())
|| (selectionMax.getBlockY() >= regionMin.getBlockY() && selectionMax.getBlockY() <= regionMax.getBlockY())
) && ( // Z part, resolves to true if the selection and region overlap anywhere on the z-axis
(regionMin.getBlockZ() <= selectionMax.getBlockZ() && regionMin.getBlockZ() >= selectionMin.getBlockZ())
|| (regionMax.getBlockZ() <= selectionMax.getBlockZ() && regionMax.getBlockZ() >= selectionMin.getBlockZ())
|| (selectionMin.getBlockZ() >= regionMin.getBlockZ() && selectionMin.getBlockZ() <= regionMax.getBlockZ())
|| (selectionMax.getBlockZ() >= regionMin.getBlockZ() && selectionMax.getBlockZ() <= regionMax.getBlockZ())
)
) {
result.add(region);
}
}
return result;
}
/**
* Get a list of regions around a location
* - Returns highest priority, child instead of parent regions
* @param location The location to check for regions
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
*/
public static List<ProtectedRegion> getApplicableRegions(Location location) {
List<ProtectedRegion> result = new ArrayList<>();
Set<ProtectedRegion> regions = AreaShop.getInstance().getWorldGuardHandler().getApplicableRegionsSet(location);
if(regions != null) {
boolean first = true;
for(ProtectedRegion pr : regions) {
if(first) {
result.add(pr);
first = false;
} else {
if(pr.getPriority() > result.get(0).getPriority()) {
result.clear();
result.add(pr);
} else if(pr.getParent() != null && pr.getParent().equals(result.get(0))) {
result.clear();
result.add(pr);
} else {
result.add(pr);
}
}
}
}
return result;
}
/**
* Get the most important rental AreaShop regions
* - Returns highest priority, child instead of parent regions
* @param location The location to check for regions
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
*/
public static List<RentRegion> getApplicableRentRegions(Location location) {
List<RentRegion> result = new ArrayList<>();
for(GeneralRegion region : getApplicableASRegions(location, GeneralRegion.RegionType.RENT)) {
result.add((RentRegion)region);
}
return result;
}
/**
* Get the most important buy AreaShop regions
* - Returns highest priority, child instead of parent regions
* @param location The location to check for regions
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
*/
public static List<BuyRegion> getApplicableBuyRegions(Location location) {
List<BuyRegion> result = new ArrayList<>();
for(GeneralRegion region : getApplicableASRegions(location, GeneralRegion.RegionType.BUY)) {
result.add((BuyRegion)region);
}
return result;
}
/**
* Get the most important AreaShop regions
* - Returns highest priority, child instead of parent regions
* @param location The location to check for regions
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
*/
public static List<GeneralRegion> getAllApplicableRegions(Location location) {
return getApplicableASRegions(location, null);
}
/**
* Get the most important AreaShop regions
* - Returns highest priority, child instead of parent regions
* @param location The location to check for regions
* @param type The type of regions to look for, null for all
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
*/
public static List<GeneralRegion> getApplicableASRegions(Location location, GeneralRegion.RegionType type) {
List<GeneralRegion> result = new ArrayList<>();
Set<ProtectedRegion> regions = AreaShop.getInstance().getWorldGuardHandler().getApplicableRegionsSet(location);
if(regions != null) {
List<GeneralRegion> candidates = new ArrayList<>();
for(ProtectedRegion pr : regions) {
GeneralRegion region = AreaShop.getInstance().getFileManager().getRegion(pr.getId());
if(region != null && (
(type == GeneralRegion.RegionType.RENT && region.isRentRegion())
|| (type == GeneralRegion.RegionType.BUY && region.isBuyRegion())
|| type == null)) {
candidates.add(region);
}
}
boolean first = true;
for(GeneralRegion region : candidates) {
if(region == null) {
AreaShop.debug("skipped null region");
continue;
}
if(first) {
result.add(region);
first = false;
} else {
if(region.getRegion().getPriority() > result.get(0).getRegion().getPriority()) {
result.clear();
result.add(region);
} else if(region.getRegion().getParent() != null && region.getRegion().getParent().equals(result.get(0).getRegion())) {
result.clear();
result.add(region);
} else {
result.add(region);
}
}
}
}
return new ArrayList<>(result);
}
/**
* Convert color and formatting codes to bukkit values
* @param input Start string with color and formatting codes in it
* @return String with the color and formatting codes in the bukkit format
*/
public static String applyColors(String input) {
String result = null;
if(input != null) {
result = ChatColor.translateAlternateColorCodes('&', input);
}
return result;
}
/**
* Format the currency amount with the characters before and after
* @param amount Amount of money to format
* @return Currency character format string
*/
public static String formatCurrency(double amount) {
String before = config.getString("moneyCharacter");
before = before.replace(AreaShop.currencyEuro, "\u20ac");
String after = config.getString("moneyCharacterAfter");
after = after.replace(AreaShop.currencyEuro, "\u20ac");
String result;
// Check for infinite and NaN
if(Double.isInfinite(amount)) {
result = "\u221E"; // Infinite symbol
} else if(Double.isNaN(amount)) {
result = "NaN";
} else {
// Add metric
double metricAbove = config.getDouble("metricSuffixesAbove");
if(metricAbove != -1 && amount >= metricAbove) {
if(amount >= 1000000000000000000000000.0) {
amount = amount/1000000000000000000000000.0;
after = "Y"+after;
} else if(amount >= 1000000000000000000000.0) {
amount = amount/1000000000000000000000.0;
after = "Z"+after;
} else if(amount >= 1000000000000000000.0) {
amount = amount/1000000000000000000.0;
after = "E"+after;
} else if(amount >= 1000000000000000.0) {
amount = amount/1000000000000000.0;
after = "P"+after;
} else if(amount >= 1000000000000.0) {
amount = amount/1000000000000.0;
after = "T"+after;
} else if(amount >= 1000000000.0) {
amount = amount/1000000000.0;
after = "G"+after;
} else if(amount >= 1000000.0) {
amount = amount/1000000.0;
after = "M"+after;
} else if(amount >= 1000.0) {
amount = amount/1000.0;
after = "k"+after;
}
BigDecimal bigDecimal = new BigDecimal(amount);
if(bigDecimal.toString().contains(".")) {
int frontLength = bigDecimal.toString().substring(0, bigDecimal.toString().indexOf('.')).length();
bigDecimal = bigDecimal.setScale(config.getInt("fractionalNumbers")+(3-frontLength), RoundingMode.HALF_UP);
}
result = bigDecimal.toString();
} else {
BigDecimal bigDecimal = new BigDecimal(amount);
bigDecimal = bigDecimal.setScale(config.getInt("fractionalNumbers"), RoundingMode.HALF_UP);
amount = bigDecimal.doubleValue();
result = bigDecimal.toString();
if(config.getBoolean("hideEmptyFractionalPart") && (amount%1.0) == 0.0 && result.contains(".")) {
result = result.substring(0, result.indexOf('.'));
}
}
}
result = result.replace(".", config.getString("decimalMark"));
return before+result+after;
}
/**
* Checks if the string is a correct time period
* @param time String that has to be checked
* @return true if format is correct, false if not
*/
public static boolean checkTimeFormat(String time) {
// Check if the string is not empty and check the length
if(time == null || time.length() <= 1 || time.indexOf(' ') == -1 || time.indexOf(' ') >= (time.length()-1)) {
return false;
}
// Check if the suffix is one of these values
String suffix = time.substring(time.indexOf(' ')+1, time.length());
if(!identifiers.contains(suffix)) {
return false;
}
// check if the part before the space is a number
String prefix = time.substring(0, (time.indexOf(' ')));
return prefix.matches("\\d+");
}
/**
* Methode to tranlate a duration string to a millisecond value
* @param duration The duration string
* @return The duration in milliseconds translated from the durationstring, or if it is invalid then 0
*/
public static long durationStringToLong(String duration) {
if(duration == null) {
return 0;
} else if(duration.equalsIgnoreCase("disabled") || duration.equalsIgnoreCase("unlimited")) {
return -1;
} else if(duration.indexOf(' ') == -1) {
return 0;
}
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(0);
String durationString = duration.substring(duration.indexOf(' ')+1, duration.length());
int durationInt = 0;
try {
durationInt = Integer.parseInt(duration.substring(0, duration.indexOf(' ')));
} catch(NumberFormatException exception) {
// No Number found, add zero
}
if(seconds.contains(durationString)) {
calendar.add(Calendar.SECOND, durationInt);
} else if(minutes.contains(durationString)) {
calendar.add(Calendar.MINUTE, durationInt);
} else if(hours.contains(durationString)) {
calendar.add(Calendar.HOUR, durationInt);
} else if(days.contains(durationString)) {
calendar.add(Calendar.DAY_OF_MONTH, durationInt);
} else if(weeks.contains(durationString)) {
calendar.add(Calendar.DAY_OF_MONTH, durationInt*7);
} else if(months.contains(durationString)) {
calendar.add(Calendar.MONTH, durationInt);
} else if(years.contains(durationString)) {
calendar.add(Calendar.YEAR, durationInt);
}
return calendar.getTimeInMillis();
}
// LEGACY TIME INPUT CONVERSION
/**
* Get setting from config that could be only a number indicating seconds
* or a string indicating a duration string
* @param path Path of the setting to read
* @return milliseconds that the setting indicates
*/
public static long getDurationFromSecondsOrString(String path) {
if(config.isLong(path) || config.isInt(path)) {
long setting = config.getLong(path);
if(setting != -1) {
setting = setting*1000;
}
return setting;
} else {
return durationStringToLong(config.getString(path));
}
}
/**
* Get setting from config that could be only a number indicating minutes
* or a string indicating a duration string
* @param path Path of the setting to read
* @return milliseconds that the setting indicates
*/
public static long getDurationFromMinutesOrString(String path) {
if(config.isLong(path) || config.isInt(path)) {
long setting = config.getLong(path);
if(setting != -1) {
setting = setting*60*1000;
}
return setting;
} else {
return durationStringToLong(config.getString(path));
}
}
/**
* Parse a time setting that could be minutes or a duration string
* @param input The string to parse
* @return milliseconds that the string indicates
*/
public static long getDurationFromMinutesOrStringInput(String input) {
long number;
try {
number = Long.parseLong(input);
if(number != -1) {
number = number*60*1000;
}
return number;
} catch(NumberFormatException e) {
return durationStringToLong(input);
}
}
/**
* Get a start of the message with a maximum length
* @param message The message
* @param maximumLength The maximum length to return
* @return The start of the message with at most maximumLength characters
*/
public static String getMessageStart(Message message, int maximumLength) {
String messageStart = "";
for(int i = 0; i < message.getRaw().size() && messageStart.length() < maximumLength; i++) {
messageStart += message.getRaw().get(i).substring(0, Math.min(maximumLength, message.getRaw().get(i).length()));
}
return messageStart.substring(0, Math.min(maximumLength, messageStart.length()));
}
/**
* Parse a time setting that could be seconds or a duration string
* @param input The string to parse
* @return seconds that the string indicates
*/
public static long getDurationFromSecondsOrStringInput(String input) {
long number;
try {
number = Long.parseLong(input);
if(number != -1) {
number = number*1000;
}
return number;
} catch(NumberFormatException e) {
return durationStringToLong(input);
}
}
/**
* Check if an input is numeric
* @param input The input to check
* @return true if the input is numeric, otherwise false
*/
public static boolean isNumeric(String input) {
try {
Integer.parseInt(input);
return true;
} catch(NumberFormatException ignored) {
return false;
}
}
// NAME <-> UUID CONVERSION
/**
* Conversion to name by uuid
* @param uuid The uuid in string format
* @return the name of the player
*/
public static String toName(String uuid) {
String result = "";
if(uuid != null) {
try {
UUID parsed = UUID.fromString(uuid);
result = toName(parsed);
} catch(IllegalArgumentException e) {
// Incorrect UUID
}
}
return result;
}
/**
* Conversion to name by uuid object
* @param uuid The uuid in string format
* @return the name of the player
*/
public static String toName(UUID uuid) {
if(uuid == null) {
return "";
} else {
String name = Bukkit.getOfflinePlayer(uuid).getName();
if(name != null) {
return name;
}
return "";
}
}
/**
* Conversion from name to uuid
* @param name The name of the player
* @return The uuid of the player
*/
@SuppressWarnings("deprecation") // Fake deprecation by Bukkit to inform developers, method will stay
public static String toUUID(String name) {
if(name == null) {
return null;
} else {
return Bukkit.getOfflinePlayer(name).getUniqueId().toString();
}
}
}
@@ -1,37 +1,43 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldedit.bukkit.selections.Selection;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.events.ask.BuyingRegionEvent;
import me.wiefferink.areashop.events.ask.RentingRegionEvent;
import me.wiefferink.areashop.events.notify.BoughtRegionEvent;
import me.wiefferink.areashop.events.notify.RentedRegionEvent;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.managers.FileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import java.util.*;
import java.util.stream.Collectors;
public class AddCommand extends CommandAreaShop {
public AddCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop add";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.createrent") || target.hasPermission("areashop.createbuy")) {
if(target.hasPermission("areashop.createrent")
|| target.hasPermission("areashop.createrent.member")
|| target.hasPermission("areashop.createrent.owner")
|| target.hasPermission("areashop.createbuy")
|| target.hasPermission("areashop.createbuy.member")
|| target.hasPermission("areashop.createbuy.owner")) {
return "help-add";
}
return null;
@@ -39,22 +45,22 @@ public class AddCommand extends CommandAreaShop {
@Override
public void execute(final CommandSender sender, final String[] args) {
if( !sender.hasPermission("areashop.createrent")
if(!sender.hasPermission("areashop.createrent")
&& !sender.hasPermission("areashop.createrent.member")
&& !sender.hasPermission("areashop.createrent.owner")
&& !sender.hasPermission("areashop.createbuy")
&& !sender.hasPermission("areashop.createbuy.member")
&& !sender.hasPermission("areashop.createbuy.owner")) {
plugin.message(sender, "add-noPermission");
return;
}
if(args.length < 2 || args[1] == null || (!"rent".equals(args[1].toLowerCase()) && !"buy".equals(args[1].toLowerCase()))) {
if(args.length < 2 || args[1] == null || (!"rent".equalsIgnoreCase(args[1]) && !"buy".equalsIgnoreCase(args[1]))) {
plugin.message(sender, "add-help");
return;
}
List<ProtectedRegion> regions = new ArrayList<>();
Map<String, ProtectedRegion> regions = new HashMap<>();
World world;
Player player = null;
if(sender instanceof Player) {
@@ -64,15 +70,15 @@ public class AddCommand extends CommandAreaShop {
if(player == null) {
plugin.message(sender, "cmd-weOnlyByPlayer");
return;
}
Selection selection = plugin.getWorldEdit().getSelection(player);
}
WorldEditSelection selection = plugin.getWorldEditHandler().getPlayerSelection(player);
if(selection == null) {
plugin.message(player, "cmd-noSelection");
return;
}
world = selection.getWorld();
regions = Utils.getWERegionsInSelection(selection);
if(regions.size() == 0) {
regions = Utils.getWorldEditRegionsInSelection(selection).stream().collect(Collectors.toMap(ProtectedRegion::getId, region -> region));
if(regions.isEmpty()) {
plugin.message(player, "cmd-noWERegionsFound");
return;
}
@@ -99,113 +105,187 @@ public class AddCommand extends CommandAreaShop {
}
}
}
ProtectedRegion region = plugin.getWorldGuard().getRegionManager(world).getRegion(args[2]);
ProtectedRegion region = plugin.getRegionManager(world).getRegion(args[2]);
if(region == null) {
plugin.message(sender, "add-noRegion", args[2]);
plugin.message(sender, "cmd-noRegion", args[2]);
return;
}
regions.add(region);
regions.put(args[2], region);
}
final boolean isRent = "rent".equals(args[1].toLowerCase());
final List<ProtectedRegion> finalRegions = regions;
final boolean isRent = "rent".equalsIgnoreCase(args[1]);
final Player finalPlayer = player;
final World finalWorld = world;
AreaShop.debug("Starting add task with " + regions.size() + " regions");
new BukkitRunnable() {
private int current = 0;
private TreeSet<GeneralRegion> regionsSuccess = new TreeSet<>();
private TreeSet<GeneralRegion> regionsAlready = new TreeSet<>();
private TreeSet<String> namesBlacklisted = new TreeSet<>();
private TreeSet<String> namesNoPermission = new TreeSet<>();
private TreeSet<String> namesExternal = new TreeSet<>();
@Override
public void run() {
for(int i=0; i<plugin.getConfig().getInt("adding.regionsPerTick"); i++) {
if(current < finalRegions.size()) {
ProtectedRegion region = finalRegions.get(current);
// Determine if the player is an owner or member of the region
boolean isMember = finalPlayer != null && plugin.getWorldGuardHandler().containsMember(region, finalPlayer.getUniqueId());
boolean isOwner = finalPlayer != null && plugin.getWorldGuardHandler().containsMember(region, finalPlayer.getUniqueId());
String type;
if(isRent) {
type = "rent";
} else {
type = "buy";
TreeSet<GeneralRegion> regionsSuccess = new TreeSet<>();
TreeSet<GeneralRegion> regionsAlready = new TreeSet<>();
TreeSet<GeneralRegion> regionsAlreadyOtherWorld = new TreeSet<>();
TreeSet<GeneralRegion> regionsRentCancelled = new TreeSet<>(); // Denied by an event listener
TreeSet<GeneralRegion> regionsBuyCancelled = new TreeSet<>(); // Denied by an event listener
TreeSet<String> namesBlacklisted = new TreeSet<>();
TreeSet<String> namesNoPermission = new TreeSet<>();
TreeSet<String> namesAddCancelled = new TreeSet<>(); // Denied by an event listener
Utils.runAsBatches(regions.entrySet(),
plugin.getConfig().getInt("adding.regionsPerTick"),
(regionEntry) -> {
String regionName = regionEntry.getKey();
ProtectedRegion region = regionEntry.getValue();
// Determine if the player is an owner or member of the region
boolean isMember = finalPlayer != null && plugin.getWorldGuardHandler().containsMember(region, finalPlayer.getUniqueId());
boolean isOwner = finalPlayer != null && plugin.getWorldGuardHandler().containsOwner(region, finalPlayer.getUniqueId());
String type;
if(isRent) {
type = "rent";
} else {
type = "buy";
}
FileManager.AddResult result = plugin.getFileManager().checkRegionAdd(sender, region, world, isRent ? GeneralRegion.RegionType.RENT : GeneralRegion.RegionType.BUY);
if(result == FileManager.AddResult.ALREADYADDED) {
regionsAlready.add(plugin.getFileManager().getRegion(regionName));
} else if(result == FileManager.AddResult.ALREADYADDEDOTHERWORLD) {
regionsAlreadyOtherWorld.add(plugin.getFileManager().getRegion(regionName));
} else if(result == FileManager.AddResult.BLACKLISTED) {
namesBlacklisted.add(regionName);
} else if(result == FileManager.AddResult.NOPERMISSION) {
namesNoPermission.add(regionName);
} else {
// Check if the player should be landlord
boolean landlord = (!sender.hasPermission("areashop.create" + type)
&& ((sender.hasPermission("areashop.create" + type + ".owner") && isOwner)
|| (sender.hasPermission("areashop.create" + type + ".member") && isMember)));
List<UUID> existing = new ArrayList<>();
existing.addAll(plugin.getWorldGuardHandler().getOwners(region).asUniqueIdList());
existing.addAll(plugin.getWorldGuardHandler().getMembers(region).asUniqueIdList());
AreaShop.debug("regionAddLandlordStatus:", regionName,
"landlord:", landlord,
"existing:", existing,
"isMember:", isMember,
"isOwner:", isOwner,
"createPermission:", sender.hasPermission("areashop.create" + type),
"ownerPermission:", sender.hasPermission("areashop.create" + type + ".owner"),
"memberPermission:", sender.hasPermission("areashop.create" + type + ".member"));
if(isRent) {
RentRegion rent = new RentRegion(regionName, world);
// Set landlord
if(landlord) {
rent.setLandlord(finalPlayer.getUniqueId(), finalPlayer.getName());
}
FileManager.AddResult result = plugin.getFileManager().checkRegionAdd(sender, region, isRent ? GeneralRegion.RegionType.RENT : GeneralRegion.RegionType.BUY);
if(result == FileManager.AddResult.ALREADYADDED) {
regionsAlready.add(plugin.getFileManager().getRegion(region.getId()));
} else if(result == FileManager.AddResult.BLACKLISTED) {
namesBlacklisted.add(region.getId());
} else if(result == FileManager.AddResult.NOPERMISSION) {
namesNoPermission.add(region.getId());
} else {
// Check if the player should be landlord
boolean landlord = (!sender.hasPermission("areashop.create" + type)
&& ((sender.hasPermission("areashop.create" + type + ".owner") && isOwner)
|| (sender.hasPermission("areashop.create" + type + ".member") && isMember)));
if(isRent) {
RentRegion rent = new RentRegion(plugin, region.getId(), finalWorld);
// Set landlord
if(landlord) {
rent.setLandlord(finalPlayer.getUniqueId(), finalPlayer.getName());
}
// Run commands
rent.runEventCommands(GeneralRegion.RegionEvent.CREATED, true);
plugin.getFileManager().addRent(rent);
rent.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
rent.update();
AddingRegionEvent event = plugin.getFileManager().addRegion(rent);
if (event.isCancelled()) {
namesAddCancelled.add(rent.getName());
return;
}
rent.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
rent.update();
// Run commands
rent.runEventCommands(GeneralRegion.RegionEvent.CREATED, false);
regionsSuccess.add(rent);
// Add existing owners/members if any
if(!landlord && !existing.isEmpty()) {
UUID rentBy = existing.remove(0);
OfflinePlayer rentByPlayer = Bukkit.getOfflinePlayer(rentBy);
RentingRegionEvent rentingRegionEvent = new RentingRegionEvent(rent, rentByPlayer, false);
Bukkit.getPluginManager().callEvent(rentingRegionEvent);
if(rentingRegionEvent.isCancelled()) {
regionsRentCancelled.add(rent);
} else {
BuyRegion buy = new BuyRegion(plugin, region.getId(), finalWorld);
// Set landlord
if(landlord) {
buy.setLandlord(finalPlayer.getUniqueId(), finalPlayer.getName());
// Add values to the rent and send it to FileManager
rent.setRentedUntil(Calendar.getInstance().getTimeInMillis() + rent.getDuration());
rent.setRenter(rentBy);
rent.updateLastActiveTime();
// Fire schematic event and updated times extended
rent.handleSchematicEvent(GeneralRegion.RegionEvent.RENTED);
// Add others as friends
for(UUID friend : existing) {
rent.getFriendsFeature().addFriend(friend, null);
}
// Run commands
buy.runEventCommands(GeneralRegion.RegionEvent.CREATED, true);
plugin.getFileManager().addBuy(buy);
buy.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
buy.update();
// Run commands
buy.runEventCommands(GeneralRegion.RegionEvent.CREATED, false);
regionsSuccess.add(buy);
rent.notifyAndUpdate(new RentedRegionEvent(rent, false));
}
}
current++;
regionsSuccess.add(rent);
} else {
BuyRegion buy = new BuyRegion(regionName, world);
// Set landlord
if(landlord) {
buy.setLandlord(finalPlayer.getUniqueId(), finalPlayer.getName());
}
AddingRegionEvent event = plugin.getFileManager().addRegion(buy);
if (event.isCancelled()) {
namesAddCancelled.add(buy.getName());
return;
}
buy.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
buy.update();
// Add existing owners/members if any
if(!landlord && !existing.isEmpty()) {
UUID buyBy = existing.remove(0);
OfflinePlayer buyByPlayer = Bukkit.getOfflinePlayer(buyBy);
BuyingRegionEvent buyingRegionEvent = new BuyingRegionEvent(buy, buyByPlayer);
Bukkit.getPluginManager().callEvent(buyingRegionEvent);
if(buyingRegionEvent.isCancelled()) {
regionsBuyCancelled.add(buy);
} else {
// Set the owner
buy.setBuyer(buyBy);
buy.updateLastActiveTime();
// Update everything
buy.handleSchematicEvent(GeneralRegion.RegionEvent.BOUGHT);
// Add others as friends
for (UUID friend : existing) {
buy.getFriendsFeature().addFriend(friend, null);
}
buy.notifyAndUpdate(new BoughtRegionEvent(buy));
}
}
regionsSuccess.add(buy);
}
}
if(current >= finalRegions.size()) {
if(!regionsSuccess.isEmpty()) {
plugin.message(sender, "add-success", args[1], Utils.combinedMessage(regionsSuccess, "region"));
}
if(!regionsAlready.isEmpty()) {
plugin.message(sender, "add-failed", Utils.combinedMessage(regionsAlready, "region"));
}
if(!namesBlacklisted.isEmpty()) {
plugin.message(sender, "add-blacklisted", Utils.createCommaSeparatedList(namesBlacklisted));
}
if(!namesNoPermission.isEmpty()) {
plugin.message(sender, "add-noPermissionRegions", Utils.createCommaSeparatedList(namesNoPermission));
plugin.message(sender, "add-noPermissionOwnerMember");
}
this.cancel();
}, false).thenAccept(
(success) -> {
if(!regionsSuccess.isEmpty()) {
plugin.message(sender, "add-success", args[1], Utils.combinedMessage(regionsSuccess, "region"));
}
}
}.runTaskTimer(plugin, 1, 1);
if(!regionsAlready.isEmpty()) {
plugin.message(sender, "add-failed", Utils.combinedMessage(regionsAlready, "region"));
}
if(!regionsAlreadyOtherWorld.isEmpty()) {
plugin.message(sender, "add-failedOtherWorld", Utils.combinedMessage(regionsAlreadyOtherWorld, "region"));
}
if(!regionsRentCancelled.isEmpty()) {
plugin.message(sender, "add-rentCancelled", Utils.combinedMessage(regionsRentCancelled, "region"));
}
if(!regionsBuyCancelled.isEmpty()) {
plugin.message(sender, "add-buyCancelled", Utils.combinedMessage(regionsBuyCancelled, "region"));
}
if(!namesBlacklisted.isEmpty()) {
plugin.message(sender, "add-blacklisted", Utils.createCommaSeparatedList(namesBlacklisted));
}
if(!namesNoPermission.isEmpty()) {
plugin.message(sender, "add-noPermissionRegions", Utils.createCommaSeparatedList(namesNoPermission));
plugin.message(sender, "add-noPermissionOwnerMember");
}
if(!namesAddCancelled.isEmpty()) {
plugin.message(sender, "add-rentCancelled", Utils.createCommaSeparatedList(namesAddCancelled));
}
});
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
List<String> result = new LinkedList<>();
if(toComplete == 2) {
if(sender.hasPermission("areashop.createrent")) {
result.add("rent");
@@ -217,7 +297,7 @@ public class AddCommand extends CommandAreaShop {
if(sender instanceof Player) {
Player player = (Player)sender;
if(sender.hasPermission("areashop.createrent") || sender.hasPermission("areashop.createbuy")) {
for(ProtectedRegion region : plugin.getWorldGuard().getRegionManager(player.getWorld()).getRegions().values()) {
for(ProtectedRegion region : plugin.getRegionManager(player.getWorld()).getRegions().values()) {
result.add(region.getId());
}
}
@@ -1,10 +1,9 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
@@ -15,10 +14,6 @@ import java.util.List;
public class AddfriendCommand extends CommandAreaShop {
public AddfriendCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop addfriend";
@@ -31,7 +26,7 @@ public class AddfriendCommand extends CommandAreaShop {
}
return null;
}
@SuppressWarnings("deprecation")
@Override
public void execute(CommandSender sender, String[] args) {
@@ -39,7 +34,7 @@ public class AddfriendCommand extends CommandAreaShop {
plugin.message(sender, "addfriend-noPermission");
return;
}
if(args.length < 2) {
plugin.message(sender, "addfriend-help");
return;
@@ -47,13 +42,13 @@ public class AddfriendCommand extends CommandAreaShop {
GeneralRegion region;
if(args.length <= 2) {
if (sender instanceof Player) {
if(sender instanceof Player) {
// get the region by location
List<GeneralRegion> regions = Utils.getAllApplicableRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
@@ -62,7 +57,7 @@ public class AddfriendCommand extends CommandAreaShop {
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
} else {
region = plugin.getFileManager().getRegion(args[2]);
if(region == null) {
@@ -71,13 +66,13 @@ public class AddfriendCommand extends CommandAreaShop {
}
}
if(sender.hasPermission("areashop.addfriendall")) {
if((region.isRentRegion() && !((RentRegion)region).isRented())
|| (region.isBuyRegion() && !((BuyRegion)region).isSold())) {
if((region instanceof RentRegion && !((RentRegion)region).isRented())
|| (region instanceof BuyRegion && !((BuyRegion)region).isSold())) {
plugin.message(sender, "addfriend-noOwner", region);
return;
}
}
OfflinePlayer friend = Bukkit.getOfflinePlayer(args[1]);
if(friend.getLastPlayed() == 0 && !plugin.getConfig().getBoolean("addFriendNotExistingPlayers")) {
if(friend.getLastPlayed() == 0 && !friend.isOnline() && !plugin.getConfig().getBoolean("addFriendNotExistingPlayers")) {
plugin.message(sender, "addfriend-notVisited", args[1], region);
return;
}
@@ -97,7 +92,7 @@ public class AddfriendCommand extends CommandAreaShop {
if(sender.hasPermission("areashop.addfriend") && sender instanceof Player) {
if(region.isOwner((Player)sender)) {
OfflinePlayer friend = Bukkit.getOfflinePlayer(args[1]);
if(friend.getLastPlayed() == 0 && !plugin.getConfig().getBoolean("addFriendNotExistingPlayers")) {
if(friend.getLastPlayed() == 0 && !friend.isOnline() && !plugin.getConfig().getBoolean("addFriendNotExistingPlayers")) {
plugin.message(sender, "addfriend-notVisited", args[1], region);
return;
}
@@ -122,7 +117,7 @@ public class AddfriendCommand extends CommandAreaShop {
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -1,15 +1,18 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldedit.bukkit.selections.CuboidSelection;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.features.SignsFeature;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Sign;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.material.Sign;
import org.bukkit.util.BlockIterator;
import java.util.ArrayList;
@@ -18,104 +21,108 @@ import java.util.Set;
public class AddsignCommand extends CommandAreaShop {
public AddsignCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop addsign";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.addsign")) {
return "help-addsign";
}
return null;
}
@Override
public String getCommandStart() {
return "areashop addsign";
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.addsign")) {
plugin.message(sender, "addsign-noPermission");
return;
}
if (!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
Player player = (Player)sender;
@Override
public String getHelp(CommandSender target) {
if (target.hasPermission("areashop.addsign")) {
return "help-addsign";
}
return null;
}
// Get the sign
Block block = null;
BlockIterator blockIterator = new BlockIterator(player, 100);
while(blockIterator.hasNext() && block == null) {
Block next = blockIterator.next();
if(next.getType() != Material.AIR) {
block = next;
}
}
if(block == null || !(block.getType() == Material.WALL_SIGN || block.getType() == Material.SIGN_POST)) {
plugin.message(sender, "addsign-noSign");
return;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!sender.hasPermission("areashop.addsign")) {
plugin.message(sender, "addsign-noPermission");
return;
}
if (!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
Player player = (Player) sender;
GeneralRegion region;
if(args.length > 1) {
// Get region by argument
region = plugin.getFileManager().getRegion(args[1]);
if(region == null) {
plugin.message(sender, "cmd-notRegistered", args[1]);
return;
}
} else {
// Get region by sign position
List<GeneralRegion> regions = Utils.getASRegionsInSelection(new CuboidSelection(block.getWorld(), block.getLocation(), block.getLocation()));
if(regions.isEmpty()) {
plugin.message(sender, "addsign-noRegions");
return;
} else if(regions.size() > 1) {
plugin.message(sender, "addsign-couldNotDetect", regions.get(0).getName(), regions.get(1).getName());
return;
}
region = regions.get(0);
}
Sign sign = (Sign)block.getState().getData();
String profile = null;
if(args.length > 2) {
profile = args[2];
Set<String> profiles = plugin.getConfig().getConfigurationSection("signProfiles").getKeys(false);
if(!profiles.contains(profile)) {
plugin.message(sender, "addsign-wrongProfile", Utils.createCommaSeparatedList(profiles), region);
return;
}
}
SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if(regionSign != null) {
plugin.message(sender, "addsign-alreadyRegistered", regionSign.getRegion());
return;
}
// Get the sign
Block block = null;
BlockIterator blockIterator = new BlockIterator(player, 100);
while (blockIterator.hasNext() && block == null) {
Block next = blockIterator.next();
if (next.getType() != Material.AIR) {
block = next;
}
}
if (block == null || !Materials.isSign(block.getType())) {
plugin.message(sender, "addsign-noSign");
return;
}
region.getSignsFeature().addSign(block.getLocation(), block.getType(), sign.getFacing(), profile);
if(profile == null) {
plugin.message(sender, "addsign-success", region);
} else {
plugin.message(sender, "addsign-successProfile", profile, region);
}
region.update();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result.addAll(plugin.getFileManager().getRegionNames());
} else if(toComplete == 3) {
result.addAll(plugin.getConfig().getStringList("signProfiles"));
}
return result;
}
GeneralRegion region;
if (args.length > 1) {
// Get region by argument
region = plugin.getFileManager().getRegion(args[1]);
if (region == null) {
plugin.message(sender, "cmd-notRegistered", args[1]);
return;
}
} else {
// Get region by sign position
List<GeneralRegion> regions = Utils.getImportantRegions(block.getLocation());
if (regions.isEmpty()) {
plugin.message(sender, "addsign-noRegions");
return;
} else if (regions.size() > 1) {
plugin.message(sender, "addsign-couldNotDetect", regions.get(0).getName(), regions.get(1).getName());
return;
}
region = regions.get(0);
}
String profile = null;
if (args.length > 2) {
profile = args[2];
Set<String> profiles = plugin.getConfig().getConfigurationSection("signProfiles").getKeys(false);
if (!profiles.contains(profile)) {
plugin.message(sender, "addsign-wrongProfile", Utils.createCommaSeparatedList(profiles), region);
return;
}
}
RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if (regionSign != null) {
plugin.message(sender, "addsign-alreadyRegistered", regionSign.getRegion());
return;
}
final BlockData data = block.getBlockData();
final BlockFace rotation;
if (data instanceof Sign) {
rotation = ((Sign) data).getRotation();
} else if (data instanceof WallSign) {
rotation = ((WallSign) data).getFacing();
} else {
rotation = null;
}
region.getSignsFeature().addSign(block.getLocation(), block.getType(), rotation, profile);
if (profile == null) {
plugin.message(sender, "addsign-success", region);
} else {
plugin.message(sender, "addsign-successProfile", profile, region);
}
region.update();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if (toComplete == 2) {
result.addAll(plugin.getFileManager().getRegionNames());
} else if (toComplete == 3) {
result.addAll(plugin.getConfig().getStringList("signProfiles"));
}
return result;
}
}
@@ -1,8 +1,7 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -11,10 +10,6 @@ import java.util.List;
public class BuyCommand extends CommandAreaShop {
public BuyCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop buy";
@@ -27,14 +22,14 @@ public class BuyCommand extends CommandAreaShop {
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.buy")) {
plugin.message(sender, "buy-noPermission");
return;
}
if (!(sender instanceof Player)) {
if(!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
@@ -42,16 +37,16 @@ public class BuyCommand extends CommandAreaShop {
if(args.length > 1 && args[1] != null) {
BuyRegion region = plugin.getFileManager().getBuy(args[1]);
if(region == null) {
plugin.message(player, "buy-notBuyable");
plugin.message(player, "buy-notBuyable", args[1]);
} else {
region.buy(player);
}
} else {
// get the region by location
List<BuyRegion> regions = Utils.getApplicableBuyRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<BuyRegion> regions = Utils.getImportantBuyRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
} else {
regions.get(0).buy(player);
@@ -1,68 +1,102 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.interactivemessenger.processing.Message;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Abstract class for generalising command classes
* Abstract class for generalising command classes.
*/
public abstract class CommandAreaShop {
AreaShop plugin;
/**
* Save the instance of the main plugin class
* @param plugin The main plugin
*/
public CommandAreaShop(AreaShop plugin) {
this.plugin = plugin;
final AreaShop plugin = AreaShop.getInstance();
private final Map<String, CommandTime> lastUsed;
public CommandAreaShop() {
lastUsed = new HashMap<>();
}
/**
* Check if this Command instance can execute the given command and arguments.
* @param command The command to check for execution
* @param args The arguments to check
* @return true if it can execute the command, false otherwise
*/
public boolean canExecute(Command command, String[] args) {
String commandString = command.getName();
for(String argument : args) {
commandString += " " + argument;
}
String commandString = command.getName() + " " + StringUtils.join(args, " ");
if(commandString.length() > getCommandStart().length()) {
return commandString.toLowerCase().startsWith(getCommandStart().toLowerCase() + " ");
}
return commandString.toLowerCase().startsWith(getCommandStart().toLowerCase());
}
/**
* Get a list of string to complete a command with (raw list, not matching ones not filtered out)
* Get a list of string to complete a command with (raw list, not matching ones not filtered out).
* @param toComplete The number of the argument that has to be completed
* @param start The already given start of the command
* @param sender The CommandSender that wants to tab complete
* @param start The already given start of the command
* @param sender The CommandSender that wants to tab complete
* @return A collection with all the possibilities for argument to complete
*/
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
return new ArrayList<>();
}
/**
* Get the argument that comes after the base command that this command reacts to
* Get the argument that comes after the base command that this command reacts to.
* @return The string that should be in front of the command for this class to act
*/
public abstract String getCommandStart();
/**
* Returns the correct help string key to be used on the help page
* Returns the correct help string key to be used on the help page.
* @param target The CommandSender that the help message is for
* @return The help message key according to the permissions of the reciever
*/
public abstract String getHelp(CommandSender target);
/**
* Execute a (sub)command if the conditions are met
* Execute a (sub)command if the conditions are met.
* @param sender The commandSender that executed the command
* @param args The arguments that are given
* @param args The arguments that are given
*/
public abstract void execute(CommandSender sender, String[] args);
/**
* Confirm a command.
* @param sender To confirm it for, or send a message to confirm
* @param args Command args
* @param message Message to send when confirmation is required
* @return true if confirmed, false if confirmation is required
*/
public boolean confirm(CommandSender sender, String[] args, Message message) {
String command = "/" + getCommandStart() + " " + StringUtils.join(args, " ", 1, args.length);
long now = System.currentTimeMillis();
CommandTime last = lastUsed.get(sender.getName());
if(last != null && last.command.equalsIgnoreCase(command) && last.time > (now - 1000 * 60)) {
return true;
}
message.prefix().append(Message.fromKey("confirm-yes").replacements(command)).send(sender);
lastUsed.put(sender.getName(), new CommandTime(command, now));
return false;
}
private static class CommandTime {
public final String command;
public final long time;
CommandTime(String command, long time) {
this.command = command;
this.time = time;
}
}
}
@@ -1,11 +1,11 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldedit.bukkit.selections.Selection;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.events.ask.DeletingRegionEvent;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -15,18 +15,14 @@ import java.util.TreeSet;
public class DelCommand extends CommandAreaShop {
public DelCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop del";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.destroyrent") || target.hasPermission("areashop.destroybuy")) {
if(target.hasPermission("areashop.destroyrent") || target.hasPermission("areashop.destroybuy") || target.hasPermission("areashop.destroyrent.landlord") || target.hasPermission("areashop.destroybuy.landlord")) {
return "help-del";
}
return null;
@@ -34,9 +30,9 @@ public class DelCommand extends CommandAreaShop {
@Override
public void execute(CommandSender sender, String[] args) {
if( !sender.hasPermission("areashop.destroybuy")
if(!sender.hasPermission("areashop.destroybuy")
&& !sender.hasPermission("areashop.destroybuy.landlord")
&& !sender.hasPermission("areashop.destroyrent")
&& !sender.hasPermission("areashop.destroyrent.landlord")) {
plugin.message(sender, "del-noPermission");
@@ -49,43 +45,51 @@ public class DelCommand extends CommandAreaShop {
return;
}
Player player = (Player)sender;
Selection selection = plugin.getWorldEdit().getSelection(player);
WorldEditSelection selection = plugin.getWorldEditHandler().getPlayerSelection(player);
if(selection == null) {
plugin.message(player, "cmd-noSelection");
return;
}
List<GeneralRegion> regions = Utils.getASRegionsInSelection(selection);
if(regions == null || regions.size() == 0) {
}
List<GeneralRegion> regions = Utils.getRegionsInSelection(selection);
if(regions == null || regions.isEmpty()) {
plugin.message(player, "cmd-noRegionsFound");
return;
}
// Start removing the regions that he has permission for
ArrayList<String> namesSuccess = new ArrayList<>();
TreeSet<GeneralRegion> namesFailed = new TreeSet<>();
TreeSet<GeneralRegion> regionsFailed = new TreeSet<>();
TreeSet<GeneralRegion> regionsCancelled = new TreeSet<>();
for(GeneralRegion region : regions) {
boolean isLandlord = region.isLandlord(((Player)sender).getUniqueId());
if(region.isRentRegion()) {
if(region instanceof RentRegion) {
if(!sender.hasPermission("areashop.destroyrent") && !(isLandlord && sender.hasPermission("areashop.destroyrent.landlord"))) {
namesFailed.add(region);
} else {
plugin.getFileManager().removeRent((RentRegion)region, true);
namesSuccess.add(region.getName());
}
} else if(region.isBuyRegion()) {
regionsFailed.add(region);
continue;
}
} else if(region instanceof BuyRegion) {
if(!sender.hasPermission("areashop.destroybuy") && !(isLandlord && sender.hasPermission("areashop.destroybuy.landlord"))) {
namesFailed.add(region);
} else {
plugin.getFileManager().removeBuy((BuyRegion)region, true);
namesSuccess.add(region.getName());
regionsFailed.add(region);
continue;
}
}
DeletingRegionEvent event = plugin.getFileManager().deleteRegion(region, true);
if (event.isCancelled()) {
regionsCancelled.add(region);
} else {
namesSuccess.add(region.getName());
}
}
// send messages
if(namesSuccess.size() != 0) {
// Send messages
if(!namesSuccess.isEmpty()) {
plugin.message(sender, "del-success", Utils.createCommaSeparatedList(namesSuccess));
}
if(namesFailed.size() != 0) {
plugin.message(sender, "del-failed", Utils.combinedMessage(namesFailed, "region"));
if(!regionsFailed.isEmpty()) {
plugin.message(sender, "del-failed", Utils.combinedMessage(regionsFailed, "region"));
}
if(!regionsCancelled.isEmpty()) {
plugin.message(sender, "del-cancelled", Utils.combinedMessage(regionsCancelled, "region"));
}
} else {
GeneralRegion region = plugin.getFileManager().getRegion(args[1]);
@@ -94,26 +98,34 @@ public class DelCommand extends CommandAreaShop {
return;
}
boolean isLandlord = sender instanceof Player && region.isLandlord(((Player)sender).getUniqueId());
if(region.isRentRegion()) {
if(region instanceof RentRegion) {
// Remove the rent if the player has permission
if(sender.hasPermission("areashop.destroyrent") || (isLandlord && sender.hasPermission("areashop.destroyrent.landlord"))) {
plugin.getFileManager().removeRent((RentRegion)region, true);
plugin.message(sender, "destroy-successRent", region);
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.isBuyRegion()) {
} else if(region instanceof BuyRegion) {
// Remove the buy if the player has permission
if(sender.hasPermission("areashop.destroybuy") || (isLandlord && sender.hasPermission("areashop.destroybuy.landlord"))) {
plugin.getFileManager().removeBuy((BuyRegion)region, true);
plugin.message(sender, "destroy-successBuy", region);
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);
}
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
@@ -1,10 +1,9 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
@@ -15,10 +14,6 @@ import java.util.List;
public class DelfriendCommand extends CommandAreaShop {
public DelfriendCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop delfriend";
@@ -33,7 +28,7 @@ public class DelfriendCommand extends CommandAreaShop {
}
/**
* Check if a person can remove friends
* Check if a person can remove friends.
* @param person The person to check
* @param region The region to check for
* @return true if the person can remove friends, otherwise false
@@ -48,27 +43,27 @@ public class DelfriendCommand extends CommandAreaShop {
}
return false;
}
@SuppressWarnings("deprecation")
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.delfriend") && !sender.hasPermission("areashop.delfriendall")) {
plugin.message(sender, "delfriend-noPermission");
return;
}
}
if(args.length < 2) {
plugin.message(sender, "delfriend-help");
return;
}
GeneralRegion region;
if(args.length <= 2) {
if (sender instanceof Player) {
if(sender instanceof Player) {
// get the region by location
List<GeneralRegion> regions = Utils.getAllApplicableRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
@@ -77,7 +72,7 @@ public class DelfriendCommand extends CommandAreaShop {
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
} else {
region = plugin.getFileManager().getRegion(args[2]);
if(region == null) {
@@ -86,11 +81,11 @@ public class DelfriendCommand extends CommandAreaShop {
}
}
if(sender.hasPermission("areashop.delfriendall")) {
if((region.isRentRegion() && !((RentRegion)region).isRented())
|| (region.isBuyRegion() && !((BuyRegion)region).isSold())) {
if((region instanceof RentRegion && !((RentRegion)region).isRented())
|| (region instanceof BuyRegion && !((BuyRegion)region).isSold())) {
plugin.message(sender, "delfriend-noOwner", region);
return;
}
}
OfflinePlayer friend = Bukkit.getOfflinePlayer(args[1]);
if(!region.getFriendsFeature().getFriends().contains(friend.getUniqueId())) {
plugin.message(sender, "delfriend-notAdded", friend.getName(), region);
@@ -120,7 +115,7 @@ public class DelfriendCommand extends CommandAreaShop {
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -1,7 +1,8 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.SignsFeature;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.tools.Materials;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
@@ -13,15 +14,11 @@ import java.util.List;
public class DelsignCommand extends CommandAreaShop {
public DelsignCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop delsign";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.delsign")) {
@@ -36,10 +33,10 @@ public class DelsignCommand extends CommandAreaShop {
plugin.message(sender, "delsign-noPermission");
return;
}
if (!(sender instanceof Player)) {
if(!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
}
Player player = (Player)sender;
// Get the sign
@@ -51,11 +48,11 @@ public class DelsignCommand extends CommandAreaShop {
block = next;
}
}
if(block == null || !(block.getType() == Material.WALL_SIGN || block.getType() == Material.SIGN_POST)) {
if(block == null || !Materials.isSign(block.getType())) {
plugin.message(sender, "delsign-noSign");
return;
}
SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if(regionSign == null) {
plugin.message(sender, "delsign-noRegion");
return;
@@ -63,7 +60,7 @@ public class DelsignCommand extends CommandAreaShop {
plugin.message(sender, "delsign-success", regionSign.getRegion());
regionSign.remove();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
return new ArrayList<>();
@@ -1,29 +1,25 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.messages.Message;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class FindCommand extends CommandAreaShop {
public FindCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop find";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.find")) {
@@ -37,11 +33,11 @@ public class FindCommand extends CommandAreaShop {
if(!sender.hasPermission("areashop.find")) {
plugin.message(sender, "find-noPermission");
return;
}
if (!(sender instanceof Player)) {
}
if(!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
}
if(args.length <= 1 || args[1] == null || (!args[1].equalsIgnoreCase("buy") && !args[1].equalsIgnoreCase("rent"))) {
plugin.message(sender, "find-help");
return;
@@ -72,31 +68,36 @@ public class FindCommand extends CommandAreaShop {
return;
}
}
// Find buy regions
if(args[1].equalsIgnoreCase("buy")) {
List<BuyRegion> regions = plugin.getFileManager().getBuys();
List<BuyRegion> results = new ArrayList<>();
for(BuyRegion region : regions) {
if(!region.isSold()
&& ((region.getPrice() <= balance && !maxPriceSet) || (region.getPrice() <= maxPrice && maxPriceSet))
&& (group == null || group.isMember(region))) {
if(!region.isSold()
&& ((region.getPrice() <= balance && !maxPriceSet) || (region.getPrice() <= maxPrice && maxPriceSet))
&& (group == null || group.isMember(region))
&& (region.getBooleanSetting("general.findCrossWorld") || player.getWorld().equals(region.getWorld()))) {
results.add(region);
}
}
if(!results.isEmpty()) {
// Draw a random one
BuyRegion region = results.get(new Random().nextInt(results.size()));
Message onlyInGroup = Message.none();
BuyRegion region = results.get(ThreadLocalRandom.current().nextInt(results.size()));
Message onlyInGroup = Message.empty();
if(group != null) {
onlyInGroup = Message.fromKey("find-onlyInGroup").replacements(args[3]);
}
// Teleport
if(maxPriceSet) {
plugin.message(player, "find-successMax", "buy", Utils.formatCurrency(maxPrice), onlyInGroup, region);
} else {
plugin.message(player, "find-success", "buy", Utils.formatCurrency(balance), onlyInGroup, region);
}
region.teleportPlayer(player, region.getBooleanSetting("general.findTeleportToSign"), false);
region.getTeleportFeature().teleportPlayer(player, region.getBooleanSetting("general.findTeleportToSign"), false);
} else {
Message onlyInGroup = Message.none();
Message onlyInGroup = Message.empty();
if(group != null) {
onlyInGroup = Message.fromKey("find-onlyInGroup").replacements(args[3]);
}
@@ -106,31 +107,37 @@ public class FindCommand extends CommandAreaShop {
plugin.message(player, "find-noneFound", "buy", Utils.formatCurrency(balance), onlyInGroup);
}
}
} else {
}
// 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()
if(!region.isRented()
&& ((region.getPrice() <= balance && !maxPriceSet) || (region.getPrice() <= maxPrice && maxPriceSet))
&& (group == null || group.isMember(region))) {
&& (group == null || group.isMember(region))
&& (region.getBooleanSetting("general.findCrossWorld") || player.getWorld().equals(region.getWorld()))) {
results.add(region);
}
}
if(!results.isEmpty()) {
// Draw a random one
RentRegion region = results.get(new Random().nextInt(results.size()));
Message onlyInGroup = Message.none();
RentRegion region = results.get(ThreadLocalRandom.current().nextInt(results.size()));
Message onlyInGroup = Message.empty();
if(group != null) {
onlyInGroup = Message.fromKey("find-onlyInGroup").replacements(args[3]);
}
// Teleport
if(maxPriceSet) {
plugin.message(player, "find-successMax", "rent", Utils.formatCurrency(maxPrice), onlyInGroup, region);
} else {
plugin.message(player, "find-success", "rent", Utils.formatCurrency(balance), onlyInGroup, region);
}
region.teleportPlayer(player, region.getBooleanSetting("general.findTeleportToSign"), false);
region.getTeleportFeature().teleportPlayer(player, region.getBooleanSetting("general.findTeleportToSign"), false);
} else {
Message onlyInGroup = Message.none();
Message onlyInGroup = Message.empty();
if(group != null) {
onlyInGroup = Message.fromKey("find-onlyInGroup").replacements(args[3]);
}
@@ -143,7 +150,7 @@ public class FindCommand extends CommandAreaShop {
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -1,10 +1,9 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldedit.bukkit.selections.Selection;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -14,15 +13,11 @@ import java.util.TreeSet;
public class GroupaddCommand extends CommandAreaShop {
public GroupaddCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop groupadd";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.groupadd")) {
@@ -36,11 +31,11 @@ public class GroupaddCommand extends CommandAreaShop {
if(!sender.hasPermission("areashop.groupadd")) {
plugin.message(sender, "groupadd-noPermission");
return;
}
}
if(args.length < 2 || args[1] == null) {
plugin.message(sender, "groupadd-help");
return;
}
}
RegionGroup group = plugin.getFileManager().getGroup(args[1]);
if(group == null) {
group = new RegionGroup(plugin, args[1]);
@@ -52,13 +47,13 @@ public class GroupaddCommand extends CommandAreaShop {
return;
}
Player player = (Player)sender;
Selection selection = plugin.getWorldEdit().getSelection(player);
WorldEditSelection selection = plugin.getWorldEditHandler().getPlayerSelection(player);
if(selection == null) {
plugin.message(player, "cmd-noSelection");
return;
}
List<GeneralRegion> regions = Utils.getASRegionsInSelection(selection);
if(regions.size() == 0) {
List<GeneralRegion> regions = Utils.getRegionsInSelection(selection);
if(regions.isEmpty()) {
plugin.message(player, "cmd-noRegionsFound");
return;
}
@@ -71,10 +66,10 @@ public class GroupaddCommand extends CommandAreaShop {
regionsFailed.add(region);
}
}
if(regionsSuccess.size() != 0) {
if(!regionsSuccess.isEmpty()) {
plugin.message(player, "groupadd-weSuccess", group.getName(), Utils.combinedMessage(regionsSuccess, "region"));
}
if(regionsFailed.size() != 0) {
if(!regionsFailed.isEmpty()) {
plugin.message(player, "groupadd-weFailed", group.getName(), Utils.combinedMessage(regionsFailed, "region"));
}
// Update all regions, this does it in a task, updating them without lag
@@ -93,14 +88,14 @@ public class GroupaddCommand extends CommandAreaShop {
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result = plugin.getFileManager().getGroupNames();
} else if(toComplete == 3) {
result = plugin.getFileManager().getRegionNames();
result = plugin.getFileManager().getRegionNames();
}
return result;
}
@@ -1,10 +1,9 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldedit.bukkit.selections.Selection;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -14,15 +13,11 @@ import java.util.TreeSet;
public class GroupdelCommand extends CommandAreaShop {
public GroupdelCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop groupdel";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.groupdel")) {
@@ -36,29 +31,30 @@ public class GroupdelCommand extends CommandAreaShop {
if(!sender.hasPermission("areashop.groupdel")) {
plugin.message(sender, "groupdel-noPermission");
return;
}
}
if(args.length < 2 || args[1] == null) {
plugin.message(sender, "groupdel-help");
return;
}
}
RegionGroup group = plugin.getFileManager().getGroup(args[1]);
if(group == null) {
group = new RegionGroup(plugin, args[1]);
plugin.getFileManager().addGroup(group);
plugin.message(sender, "groupdel-wrongGroup", args[1]);
return;
}
if(args.length == 2) {
if(!(sender instanceof Player)) {
plugin.message(sender, "cmd-weOnlyByPlayer");
return;
}
Player player = (Player)sender;
Selection selection = plugin.getWorldEdit().getSelection(player);
WorldEditSelection selection = plugin.getWorldEditHandler().getPlayerSelection(player);
if(selection == null) {
plugin.message(player, "cmd-noSelection");
return;
}
List<GeneralRegion> regions = Utils.getASRegionsInSelection(selection);
if(regions.size() == 0) {
List<GeneralRegion> regions = Utils.getRegionsInSelection(selection);
if(regions.isEmpty()) {
plugin.message(player, "cmd-noRegionsFound");
return;
}
@@ -71,10 +67,10 @@ public class GroupdelCommand extends CommandAreaShop {
regionsFailed.add(region);
}
}
if(regionsSuccess.size() != 0) {
if(!regionsSuccess.isEmpty()) {
plugin.message(player, "groupdel-weSuccess", group.getName(), Utils.combinedMessage(regionsSuccess, "region"));
}
if(regionsFailed.size() != 0) {
if(!regionsFailed.isEmpty()) {
plugin.message(player, "groupdel-weFailed", group.getName(), Utils.combinedMessage(regionsFailed, "region"));
}
// Update all regions, this does it in a task, updating them without lag
@@ -85,7 +81,7 @@ public class GroupdelCommand extends CommandAreaShop {
if(region == null) {
plugin.message(sender, "cmd-notRegistered", args[2]);
return;
}
}
if(group.removeMember(region)) {
region.update();
plugin.message(sender, "groupdel-success", group.getName(), group.getMembers().size(), region);
@@ -94,12 +90,12 @@ public class GroupdelCommand extends CommandAreaShop {
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result = plugin.getFileManager().getGroupNames();
result = plugin.getFileManager().getGroupNames();
} else if(toComplete == 3) {
result = plugin.getFileManager().getRegionNames();
}
@@ -1,24 +1,20 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class GroupinfoCommand extends CommandAreaShop {
public GroupinfoCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop groupinfo";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.groupinfo")) {
@@ -33,24 +29,24 @@ public class GroupinfoCommand extends CommandAreaShop {
if(!sender.hasPermission("areashop.groupinfo")) {
plugin.message(sender, "groupinfo-noPermission");
return;
}
}
if(args.length < 2 || args[1] == null) {
plugin.message(sender, "groupinfo-help");
return;
}
}
RegionGroup group = plugin.getFileManager().getGroup(args[1]);
if(group == null) {
plugin.message(sender, "groupinfo-noGroup", args[1]);
return;
}
List<String> members = group.getMembers();
if(members.size() == 0) {
Set<String> members = group.getMembers();
if(members.isEmpty()) {
plugin.message(sender, "groupinfo-noMembers", group.getName());
} else {
plugin.message(sender, "groupinfo-members", group.getName(), Utils.createCommaSeparatedList(members));
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
@@ -1,7 +1,6 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
@@ -9,15 +8,11 @@ import java.util.List;
public class GrouplistCommand extends CommandAreaShop {
public GrouplistCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop grouplist";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.grouplist")) {
@@ -33,13 +28,13 @@ public class GrouplistCommand extends CommandAreaShop {
return;
}
List<String> groups = plugin.getFileManager().getGroupNames();
if(groups.size() == 0) {
if(groups.isEmpty()) {
plugin.message(sender, "grouplist-noGroups");
} else {
plugin.message(sender, "grouplist-success", Utils.createCommaSeparatedList(groups));
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
return new ArrayList<>();
@@ -1,19 +1,14 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import org.bukkit.command.CommandSender;
public class HelpCommand extends CommandAreaShop {
public HelpCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop help";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.help")) {
@@ -0,0 +1,79 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
public class ImportCommand extends CommandAreaShop {
/* RegionGroup priority usage:
0: Settings from /config.yml
1: Settings from /worlds/<world>/config.yml
2: Settings from /worlds/<world>/parent-regions.yml (if their priority is set it is added to this value)
*/
@Override
public String getCommandStart() {
return "areashop import";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.import")) {
return "help-import";
}
return null;
}
// TODO:
// - Landlord?
// - Friends
// - Region flags?
// - Settings from the 'permissions' section in RegionForSale/config.yml?
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.import")) {
plugin.message(sender, "import-noPermission");
return;
}
if(args.length < 2) {
plugin.message(sender, "import-help");
return;
}
if(!"RegionForSale".equalsIgnoreCase(args[1])) {
plugin.message(sender, "import-wrongSource");
return;
}
if(!confirm(sender, args, Message.fromKey("import-confirm"))) {
return;
}
new ImportJob(sender);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result.add("RegionForSale");
}
return result;
}
}
@@ -0,0 +1,479 @@
package me.wiefferink.areashop.commands;
import com.google.common.base.Charsets;
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.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.regions.RentRegion;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ImportJob {
private final AreaShop plugin;
private final CommandSender sender;
/**
* Create and execute the import.
* @param sender CommandSender that should receive progress updates
*/
public ImportJob(CommandSender sender) {
this.sender = sender;
this.plugin = AreaShop.getInstance();
execute();
}
/**
* Execute the job.
*/
private void execute() {
// Check for RegionForSale data
File regionForSaleFolder = new File(plugin.getDataFolder().getParentFile().getAbsolutePath(), "RegionForSale");
if(!regionForSaleFolder.exists()) {
message("import-noPluginFolder", regionForSaleFolder.getName());
return;
}
File worldsFolder = new File(regionForSaleFolder.getAbsolutePath(), "worlds");
if(!worldsFolder.exists()) {
message("import-noWorldsFolder");
return;
}
File[] worldFolders = worldsFolder.listFiles();
if(worldFolders == null) {
message("import-noWorldsFolder");
return;
}
// Import data for each world
message("import-start");
// Group with settings for all imported regions
RegionGroup regionForSaleGroup = new RegionGroup(plugin, "RegionForSale");
plugin.getFileManager().addGroup(regionForSaleGroup);
// Import /RegionForSale/config.yml settings
File regionForSaleConfigFile = new File(regionForSaleFolder.getAbsolutePath(), "config.yml");
YamlConfiguration regionForSaleConfig = loadConfiguration(regionForSaleConfigFile);
if(regionForSaleConfig == null) {
messageNoPrefix("import-loadConfigFailed", regionForSaleConfigFile.getAbsolutePath());
regionForSaleConfig = new YamlConfiguration();
} else {
importRegionSettings(regionForSaleConfig, regionForSaleGroup.getSettings(), null, false);
regionForSaleGroup.setSetting("priority", 0);
}
// Import /RegionForSale/general.yml settings
File regionForSaleGeneralFile = new File(regionForSaleFolder.getAbsolutePath(), "config.yml");
YamlConfiguration regionForSaleGeneral = loadConfiguration(regionForSaleConfigFile);
if(regionForSaleGeneral == null) {
messageNoPrefix("import-loadConfigFailed", regionForSaleGeneralFile.getAbsolutePath());
} else {
// Collection interval of RegionForSale maps to rent duration
String duration = "1 day";
if(regionForSaleGeneral.isLong("interval.collect_money")) {
duration = minutesToString(regionForSaleGeneral.getLong("interval.collect_money"));
}
regionForSaleGroup.setSetting("rent.duration", duration);
// Global economy account has an effect close to landlord in AreaShop
if(regionForSaleGeneral.isString("global_econ_account")) {
regionForSaleGroup.setSetting("general.landlordName", regionForSaleGeneral.getString("global_econ_account"));
}
}
regionForSaleGroup.saveRequired();
////////// Handle defaults of RegionForSale
// Set autoExtend, to keep the same behavior as RegionForSale had
regionForSaleGroup.setSetting("rent.autoExtend", true);
// Import regions from each world
for(File worldFolder : worldFolders) {
// Skip files
if(!worldFolder.isDirectory() || worldFolder.isHidden()) {
continue;
}
messageNoPrefix("import-doWorld", worldFolder.getName());
// Get the Bukkit world
World world = Bukkit.getWorld(worldFolder.getName());
if(world == null) {
messageNoPrefix("import-noBukkitWorld");
continue;
}
// Get the WorldGuard RegionManager
RegionManager regionManager = plugin.getRegionManager(world);
if(regionManager == null) {
messageNoPrefix("import-noRegionManger");
continue;
}
// Load the /worlds/<world>/regions.yml file
File regionsFile = new File(worldFolder.getAbsolutePath(), "regions.yml");
YamlConfiguration regions = loadConfiguration(regionsFile);
if(regions == null) {
messageNoPrefix("import-loadRegionsFailed", regionsFile.getAbsolutePath());
continue;
}
// Load /worlds/<world>/config.yml file
File worldConfigFile = new File(worldFolder.getAbsolutePath(), "config.yml");
YamlConfiguration worldConfig = loadConfiguration(worldConfigFile);
if(worldConfig == null) {
messageNoPrefix("import-loadWorldConfigFailed", worldConfigFile.getAbsolutePath());
// Simply skip importing the settings, since this is not really fatal
worldConfig = new YamlConfiguration();
} else {
// RegionGroup with all world settings
RegionGroup worldGroup = new RegionGroup(plugin, "RegionForSale-" + worldFolder.getName());
importRegionSettings(worldConfig, worldGroup.getSettings(), null, false);
worldGroup.setSetting("priority", 1);
worldGroup.addWorld(worldFolder.getName());
plugin.getFileManager().addGroup(regionForSaleGroup);
worldGroup.saveRequired();
}
// Create groups to hold settings of /worlds/<world>/parent-regions.yml
File parentRegionsFile = new File(worldFolder.getAbsolutePath(), "parent-regions.yml");
YamlConfiguration parentRegions = loadConfiguration(parentRegionsFile);
if(parentRegions == null) {
messageNoPrefix("import-loadParentRegionsFailed", parentRegionsFile.getAbsolutePath());
// Non-fatal, so just continue
} else {
for(String parentRegionName : parentRegions.getKeys(false)) {
// Get WorldGuard region
ProtectedRegion worldGuardRegion = regionManager.getRegion(parentRegionName);
if(worldGuardRegion == null) {
messageNoPrefix("import-noWorldGuardRegionParent", parentRegionName);
continue;
}
// Get settings section
ConfigurationSection parentRegionSection = parentRegions.getConfigurationSection(parentRegionName);
if(parentRegionSection == null) {
messageNoPrefix("import-improperParentRegion", parentRegionName);
continue;
}
// Skip if it does not have any settings
if(parentRegionSection.getKeys(false).isEmpty()) {
continue;
}
// Import parent region settings into a RegionGroup
RegionGroup parentRegionGroup = new RegionGroup(plugin, "RegionForSale-" + worldFolder.getName() + "-" + parentRegionName);
importRegionSettings(parentRegionSection, parentRegionGroup.getSettings(), null, false);
parentRegionGroup.setSetting("priority", 2 + parentRegionSection.getLong("info.priority", 0));
parentRegionGroup.saveRequired();
// TODO add all regions that are contained in this parent region
// Utils.getWorldEditRegionsInSelection()
}
}
// Read and import regions
for(String regionKey : regions.getKeys(false)) {
GeneralRegion existingRegion = plugin.getFileManager().getRegion(regionKey);
if(existingRegion != null) {
if(world.getName().equalsIgnoreCase(existingRegion.getWorldName())) {
messageNoPrefix("import-alreadyAdded", regionKey);
} else {
messageNoPrefix("import-alreadyAddedOtherWorld", regionKey, existingRegion.getWorldName(), world.getName());
}
continue;
}
ConfigurationSection regionSection = regions.getConfigurationSection(regionKey);
if(regionSection == null) {
messageNoPrefix("import-invalidRegionSection", regionKey);
continue;
}
// Get WorldGuard region
ProtectedRegion worldGuardRegion = regionManager.getRegion(regionKey);
if(worldGuardRegion == null) {
messageNoPrefix("import-noWorldGuardRegion", regionKey);
continue;
}
String owner = regionSection.getString("info.owner", null);
boolean isBought = regionSection.getBoolean("info.is-bought");
// TODO: should also take into config settings of parent regions
boolean rentable = regionSection.getBoolean("economic-settings.rentable", worldConfig.getBoolean("economic-settings.rentable", regionForSaleConfig.getBoolean("economic-settings.rentable")));
boolean buyable = regionSection.getBoolean("economic-settings.buyable", worldConfig.getBoolean("economic-settings.buyable", regionForSaleConfig.getBoolean("economic-settings.buyable")));
// Can be bought and rented, import as buy
if(buyable && rentable) {
messageNoPrefix("import-buyAndRent", regionKey);
}
// Cannot be bought or rented, skip
if(!buyable && !rentable && owner == null) {
messageNoPrefix("import-noBuyAndNoRent", regionKey);
continue;
}
// Create region
GeneralRegion region;
if(rentable || (owner != null && !isBought)) {
region = new RentRegion(regionKey, world);
} else {
region = new BuyRegion(regionKey, world);
}
AddingRegionEvent event = plugin.getFileManager().addRegion(region);
if (event.isCancelled()) {
messageNoPrefix("general-cancelled", event.getReason());
continue;
}
// Import settings
importRegionSettings(regionSection, region.getConfig(), region, !buyable && !rentable);
region.getConfig().set("general.importedFrom", "RegionForSale");
// Get existing owners and members
List<UUID> existing = new ArrayList<>();
if(owner != null) {
@SuppressWarnings("deprecation")
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(owner);
if(offlinePlayer.hasPlayedBefore()) {
existing.add(offlinePlayer.getUniqueId());
}
}
for(UUID uuid : plugin.getWorldGuardHandler().getOwners(worldGuardRegion).asUniqueIdList()) {
if(!existing.contains(uuid)) {
existing.add(uuid);
}
}
for(UUID uuid : plugin.getWorldGuardHandler().getMembers(worldGuardRegion).asUniqueIdList()) {
if(!existing.contains(uuid)) {
existing.add(uuid);
}
}
// First owner (or if none, the first member) will be the renter/buyer
if(!existing.isEmpty()) {
region.setOwner(existing.remove(0));
}
// Add others as friends
for(UUID friend : existing) {
region.getFriendsFeature().addFriend(friend, null);
}
region.saveRequired();
messageNoPrefix("import-imported", regionKey);
}
}
// Update all regions
plugin.getFileManager().updateAllRegions(sender);
// Write all imported regions and settings to disk
plugin.getFileManager().saveRequiredFiles();
}
/**
* Load a YamlConfiguration from disk using UTF-8 encoding.
* @param from File to read the configuration from
* @return YamlConfiguration if the file exists and got read wihout problems, otherwise null
*/
private YamlConfiguration loadConfiguration(File from) {
try(
InputStreamReader reader = new InputStreamReader(new FileInputStream(from), Charsets.UTF_8)
) {
return YamlConfiguration.loadConfiguration(reader);
} catch(IOException e) {
return null;
}
}
/**
* Import region specific settings from a RegionForSale source to an AreaShop target ConfigurationSection.
* @param from RegionForSale config section that specifies region settings
* @param to AreaShop config section that specifies region settings
* @param region GeneralRegion to copy settings to, or null if doing generic settings
* @param permanent Region cannot be rented or bought, disables some features
*/
private void importRegionSettings(ConfigurationSection from, ConfigurationSection to, GeneralRegion region, boolean permanent) {
// Maximum rental time, TODO check if this is actually the same
if(from.isLong("permissions.max-rent-time")) {
to.set("rent.maxRentTime", minutesToString(from.getLong("permissions.max-rent-time")));
}
// Region rebuild
if(from.getBoolean("region-rebuilding.auto-rebuild")) {
to.set("general.enableRestore", true);
}
// Get price settings
String unit = from.getString("economic-settings.unit-type");
String rentPrice = from.getString("economic-settings.cost-per-unit.rent");
String buyPrice = from.getString("economic-settings.cost-per-unit.buy");
String sellPrice = from.getString("economic-settings.cost-per-unit.selling-price");
// TODO: There is no easy way to import this, setup eventCommandsProfile?
// String taxes = from.getString("economic-settings.cost-per-unit.taxes");
// Determine unit and add that to the price
String unitSuffix = "";
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
}
// Apply settings
if(rentPrice != null) {
to.set("rent.price", rentPrice + unitSuffix);
}
if(buyPrice != null) {
to.set("buy.price", buyPrice + unitSuffix);
if(sellPrice != null) {
try {
double buyPriceAmount = Double.parseDouble(buyPrice);
double sellPriceAmount = Double.parseDouble(sellPrice);
to.set("buy.moneyBack", sellPriceAmount / buyPriceAmount * 100);
} catch(NumberFormatException e) {
// There is not always a region here for the message, should probably indicate something though
message("import-moneyBackFailed", buyPrice, sellPrice);
}
}
}
// Apply permanent region settings
if(permanent) {
to.set("buy.resellDisabled", true);
to.set("buy.sellDisabled", true);
to.set("general.countForLimits", false);
}
// Set rented until
if(from.isLong("info.last-withdrawal")
&& region instanceof RentRegion) {
RentRegion rentRegion = (RentRegion)region;
long lastWithdrawal = from.getLong("info.last-withdrawal");
// Because the rental duration is already imported into the region and its parents this should be correct
rentRegion.setRentedUntil(lastWithdrawal + rentRegion.getDuration());
}
// Import signs (list of strings like "297, 71, -22")
if(from.isList("info.signs") && region != null) {
for(String signLocation : from.getStringList("info.signs")) {
String[] locationParts = signLocation.split(", ");
if(locationParts.length != 3) {
message("import-invalidSignLocation", region.getName(), signLocation);
continue;
}
// Parse the location
Location location;
try {
location = new Location(region.getWorld(), Double.parseDouble(locationParts[0]), Double.parseDouble(locationParts[1]), Double.parseDouble(locationParts[2]));
} catch(NumberFormatException e) {
message("import-invalidSignLocation", region.getName(), signLocation);
continue;
}
// Check if this location is already added to a region
RegionSign regionSign = SignsFeature.getSignByLocation(location);
if(regionSign != null) {
if(!regionSign.getRegion().equals(region)) {
message("import-signAlreadyAdded", region.getName(), signLocation, regionSign.getRegion().getName());
}
continue;
}
// SignType and Facing will be written when the sign is updated later
region.getSignsFeature().addSign(location, null, null, null);
}
}
}
private static class TimeUnit {
public final long minutes;
public final String identifier;
TimeUnit(long minutes, String identifier) {
this.minutes = minutes;
this.identifier = identifier;
}
}
private static final List<TimeUnit> timeUnitLookup = new ArrayList<TimeUnit>() {
{
add(new TimeUnit(60 * 24 * 30 * 12, "year"));
add(new TimeUnit(60 * 24 * 30, "month"));
add(new TimeUnit(60 * 24, "day"));
add(new TimeUnit(60, "hour"));
}
};
/**
* Convert minutes to a human-readable string.
* @param minutes Value to convert
* @return String that represents the same length of time in a readable format, like "1 day", "5 minutes", "3 months"
*/
private String minutesToString(long minutes) {
// If the specified number of minutes can map nicely to a higher unit, use that one
String resultUnit = "minute";
long resultValue = minutes;
for(TimeUnit unit : timeUnitLookup) {
long result = minutes / unit.minutes;
if(resultValue * unit.minutes == minutes) {
resultUnit = unit.identifier;
resultValue = result;
break;
}
}
return resultValue + " " + resultUnit + (resultValue == 1 ? "" : "s");
}
/**
* Send a message to a target without a prefix.
* @param key The key of the language string
* @param replacements The replacements to insert in the message
*/
public void messageNoPrefix(String key, Object... replacements) {
plugin.messageNoPrefix(sender, key, replacements);
if(!(sender instanceof ConsoleCommandSender)) {
plugin.messageNoPrefix(Bukkit.getConsoleSender(), key, replacements);
}
}
/**
* Send a message to a target, prefixed by the default chat prefix.
* @param key The key of the language string
* @param replacements The replacements to insert in the message
*/
public void message(String key, Object... replacements) {
plugin.message(sender, key, replacements);
if(!(sender instanceof ConsoleCommandSender)) {
plugin.message(Bukkit.getConsoleSender(), key, replacements);
}
}
}
@@ -1,24 +1,21 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.messages.Message;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.*;
// TODO fix '/as info' help message hitting replacement limit (improve depth tracking?)
// TODO add info about autoExtend
public class InfoCommand extends CommandAreaShop {
public InfoCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop info";
@@ -33,16 +30,17 @@ public class InfoCommand extends CommandAreaShop {
}
/**
* Display a page of a list of regions
* @param sender The CommandSender to send the messages to
* @param regions The regions to display
* Display a page of a list of regions.
* @param sender The CommandSender to send the messages to
* @param regions The regions to display
* @param filterGroup The group to filter the regions by
* @param keyHeader The header to print above the page
* @param pageInput The page number, if any
* @param baseCommand The command to execute for next/previous page
* @param keyHeader The header to print above the page
* @param pageInput The page number, if any
* @param baseCommand The command to execute for next/previous page (/areashop will be added)
*/
private void showSortedPagedList(CommandSender sender, List<? extends GeneralRegion> regions, RegionGroup filterGroup, String keyHeader, String pageInput, String baseCommand) {
int ITEMS_PER_PAGE = 18;
int maximumItems = 20;
int itemsPerPage = maximumItems - 2;
int page = 1;
if(pageInput != null && Utils.isNumeric(pageInput)) {
try {
@@ -53,100 +51,99 @@ public class InfoCommand extends CommandAreaShop {
}
}
if(filterGroup != null) {
for(Iterator<? extends GeneralRegion> it = regions.iterator(); it.hasNext(); ) {
if(!filterGroup.isMember(it.next())) {
it.remove();
}
}
regions.removeIf(generalRegion -> !filterGroup.isMember(generalRegion));
}
if(regions.isEmpty()) {
plugin.message(sender, "info-noRegions");
} else {
long start = Calendar.getInstance().getTimeInMillis();
// First sort by type, then by name
Collections.sort(regions, new Comparator<GeneralRegion>() {
@Override
public int compare(GeneralRegion one, GeneralRegion two) {
int typeCompare = getTypeOrder(two).compareTo(getTypeOrder(one));
if(typeCompare != 0) {
return typeCompare;
} else {
return one.getName().compareTo(two.getName());
}
regions.sort((one, two) -> {
int typeCompare = Integer.compare(getTypeOrder(two), getTypeOrder(one));
if(typeCompare != 0) {
return typeCompare;
} else {
return one.getName().compareTo(two.getName());
}
});
// Header
Message limitedToGroup = Message.none();
Message limitedToGroup = Message.empty();
if(filterGroup != null) {
limitedToGroup = Message.fromKey("info-limitedToGroup").replacements(filterGroup.getName());
}
plugin.message(sender, keyHeader, limitedToGroup);
// Page entries
int totalPages = (int)Math.ceil(regions.size()/(double)ITEMS_PER_PAGE); // Clip page to correct boundaries, not much need to tell the user
if(regions.size() == ITEMS_PER_PAGE+1) { // 19 total items is mapped to 1 page of 19
ITEMS_PER_PAGE++;
int totalPages = (int)Math.ceil(regions.size() / (double)itemsPerPage); // Clip page to correct boundaries, not much need to tell the user
if(regions.size() == itemsPerPage + 1) { // 19 total items is mapped to 1 page of 19
itemsPerPage++;
totalPages = 1;
}
page = Math.max(1, Math.min(totalPages, page));
for(int i = (page-1)*ITEMS_PER_PAGE; i < page*ITEMS_PER_PAGE && i < regions.size(); i++) {
int linesPrinted = 1; // header
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);
plugin.messageNoPrefix(sender, "info-entry" + state, region);
linesPrinted++;
}
Message footer = Message.none();
Message footer = Message.empty();
// Previous button
if(page > 1) {
footer.append(Message.fromKey("info-pagePrevious").replacements(baseCommand+" "+(page-1)));
footer.append(Message.fromKey("info-pagePrevious").replacements(baseCommand + " " + (page - 1)));
} else {
footer.append(Message.fromKey("info-pageNoPrevious"));
}
// Page status
if(totalPages > 1) {
String pageString = ""+page;
for(int i = pageString.length(); i < (totalPages+"").length(); i++) {
pageString = "0"+pageString;
StringBuilder pageString = new StringBuilder("" + page);
for(int i = pageString.length(); i < (totalPages + "").length(); i++) {
pageString.insert(0, "0");
}
footer.append(Message.fromKey("info-pageStatus").replacements(page, totalPages));
if(page < totalPages) {
footer.append(Message.fromKey("info-pageNext").replacements(baseCommand+" "+(page+1)));
footer.append(Message.fromKey("info-pageNext").replacements(baseCommand + " " + (page + 1)));
} else {
footer.append(Message.fromKey("info-pageNoNext"));
}
// Fill up space if the page is not full (aligns header nicely)
for(int i = linesPrinted; i < maximumItems - 1; i++) {
sender.sendMessage(" ");
}
footer.send(sender);
long end = Calendar.getInstance().getTimeInMillis();
}
}
}
/**
* Get an integer to order by type, usable for Comparators
* Get an integer to order by type, usable for Comparators.
* @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;
@@ -155,7 +152,7 @@ public class InfoCommand extends CommandAreaShop {
}
}
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.info")) {
@@ -179,61 +176,41 @@ public class InfoCommand extends CommandAreaShop {
}
}
}
// All regions
if(args[1].equalsIgnoreCase("all")) {
showSortedPagedList(sender, plugin.getFileManager().getRegions(), filterGroup, "info-allHeader", (args.length > 2 ? args[2] : null), "/areashop info all");
showSortedPagedList(sender, plugin.getFileManager().getRegions(), filterGroup, "info-allHeader", (args.length > 2 ? args[2] : null), "info all");
}
// Rented regions
else if(args[1].equalsIgnoreCase("rented")) {
List<RentRegion> regions = plugin.getFileManager().getRents();
for(Iterator<RentRegion> it = regions.iterator(); it.hasNext(); ) {
if(!it.next().isRented()) {
it.remove();
}
}
showSortedPagedList(sender, regions, filterGroup, "info-rentedHeader", (args.length > 2 ? args[2] : null), "/areashop info rented");
regions.removeIf(RentRegion::isAvailable);
showSortedPagedList(sender, regions, filterGroup, "info-rentedHeader", (args.length > 2 ? args[2] : null), "info rented");
}
// Forrent regions
else if(args[1].equalsIgnoreCase("forrent")) {
List<RentRegion> regions = plugin.getFileManager().getRents();
for(Iterator<RentRegion> it = regions.iterator(); it.hasNext(); ) {
if(it.next().isRented()) {
it.remove();
}
}
showSortedPagedList(sender, regions, filterGroup, "info-forrentHeader", (args.length > 2 ? args[2] : null), "/areashop info forrent");
regions.removeIf(RentRegion::isRented);
showSortedPagedList(sender, regions, filterGroup, "info-forrentHeader", (args.length > 2 ? args[2] : null), "info forrent");
}
// Sold regions
else if(args[1].equalsIgnoreCase("sold")) {
List<BuyRegion> regions = plugin.getFileManager().getBuys();
for(Iterator<BuyRegion> it = regions.iterator(); it.hasNext(); ) {
if(!it.next().isSold()) {
it.remove();
}
}
showSortedPagedList(sender, regions, filterGroup, "info-soldHeader", (args.length > 2 ? args[2] : null), "/areashop info sold");
regions.removeIf(BuyRegion::isAvailable);
showSortedPagedList(sender, regions, filterGroup, "info-soldHeader", (args.length > 2 ? args[2] : null), "info sold");
}
// Forsale regions
else if(args[1].equalsIgnoreCase("forsale")) {
List<BuyRegion> regions = plugin.getFileManager().getBuys();
for(Iterator<BuyRegion> it = regions.iterator(); it.hasNext(); ) {
if(it.next().isSold()) {
it.remove();
}
}
showSortedPagedList(sender, regions, filterGroup, "info-forsaleHeader", (args.length > 2 ? args[2] : null), "/areashop info forsale");
regions.removeIf(BuyRegion::isSold);
showSortedPagedList(sender, regions, filterGroup, "info-forsaleHeader", (args.length > 2 ? args[2] : null), "info forsale");
}
// Reselling regions
else if(args[1].equalsIgnoreCase("reselling")) {
List<BuyRegion> regions = plugin.getFileManager().getBuys();
for(Iterator<BuyRegion> it = regions.iterator(); it.hasNext(); ) {
if(!it.next().isInResellingMode()) {
it.remove();
}
}
showSortedPagedList(sender, regions, filterGroup, "info-resellingHeader", (args.length > 2 ? args[2] : null), "/areashop info reselling");
regions.removeIf(region -> !region.isInResellingMode());
showSortedPagedList(sender, regions, filterGroup, "info-resellingHeader", (args.length > 2 ? args[2] : null), "info reselling");
}
// List of regions without a group
@@ -245,245 +222,216 @@ public class InfoCommand extends CommandAreaShop {
if(regions.isEmpty()) {
plugin.message(sender, "info-nogroupNone");
} else {
showSortedPagedList(sender, regions, filterGroup, "info-nogroupHeader", (args.length > 2 ? args[2] : null), "/areashop info nogroup");
showSortedPagedList(sender, regions, filterGroup, "info-nogroupHeader", (args.length > 2 ? args[2] : null), "info nogroup");
}
}
// 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.getAllApplicableRegions(((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).isRentRegion()) {
rent = (RentRegion)regions.get(0);
} else if(regions.get(0).isBuyRegion()) {
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);
}
}
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
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-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));
}
} else {
plugin.messageNoPrefix(sender, "info-regionCanBeRented", 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);
}
}
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, buy)) {
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));
}
} else {
plugin.messageNoPrefix(sender, "info-regionCanBeRented", 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);
}
}
if(rent.getMaxRentTime() != -1) {
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 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(".");
tp.send(sender);
tp.append(", ");
}
// 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());
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);
}
if(!signLocations.isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionSigns", signLocations.toArray());
}
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);
}
// Groups
if(sender.hasPermission("areashop.groupinfo") && !rent.getGroupNames().isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionGroups", Utils.createCommaSeparatedList(rent.getGroupNames()));
}
// Restoring
if(rent.isRestoreEnabled()) {
if(sender.hasPermission("areashop.setrestore")) {
plugin.messageNoPrefix(sender, "info-regionRestoringRent", rent, Message.fromKey("info-regionRestoringProfile").replacements(rent.getRestoreProfile()));
} else {
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(SellCommand.canUse(sender, 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));
}
// Friends
if(!buy.getFriendsFeature().getFriendNames().isEmpty()) {
String messagePart = "info-friend";
if(DelfriendCommand.canUse(sender, buy)) {
messagePart = "info-friendRemove";
}
} else {
plugin.messageNoPrefix(sender, "info-regionCanBeBought", buy);
plugin.messageNoPrefix(sender, "info-regionFriends", buy, Utils.combinedMessage(buy.getFriendsFeature().getFriendNames(), messagePart));
}
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", signLocations.toArray());
}
// Groups
if(sender.hasPermission("areashop.groupinfo") && !buy.getGroupNames().isEmpty()) {
plugin.messageNoPrefix(sender, "info-regionGroups", Utils.createCommaSeparatedList(buy.getGroupNames()));
}
// Restoring
if(buy.isRestoreEnabled()) {
if(sender.hasPermission("areashop.setrestore")) {
plugin.messageNoPrefix(sender, "info-regionRestoringBuy", buy, Message.fromKey("info-regionRestoringProfile").replacements(buy.getRestoreProfile()));
} else {
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-regionCanBeBought", buy);
}
} else {
plugin.message(sender, "info-regionHelp");
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");
}
} else {
plugin.message(sender, "info-help");
}
}
}
/**
* Create a comma-space separated list from a collection of strings
* @param list The collection with strings
* @return A comma-space separated list of the strings in the collection
*/
public String createCommaString(Collection<String> list) {
String result = "";
boolean first = true;
for(String part : list) {
if(first) {
result += part;
first = false;
} else {
result += ", " + part;
}
}
return result;
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
@@ -1,25 +1,22 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.messages.Message;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
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 {
public LinksignsCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop linksigns";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.linksigns")) {
@@ -33,12 +30,12 @@ public class LinksignsCommand extends CommandAreaShop {
if(!sender.hasPermission("areashop.linksigns")) {
plugin.message(sender, "linksigns-noPermission");
return;
}
if (!(sender instanceof Player)) {
}
if(!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
Player player = (Player)sender;
if(plugin.getSignlinkerManager().isInSignLinkMode(player)) {
plugin.getSignlinkerManager().exitSignLinkMode(player);
@@ -47,23 +44,23 @@ public class LinksignsCommand extends CommandAreaShop {
String profile = null;
if(args.length > 1) {
profile = args[1];
Set<String> profiles = plugin.getConfig().getConfigurationSection("signProfiles").getKeys(false);
if(!profiles.contains(profile)) {
ArrayList<String> message = new ArrayList<>();
for(String p : profiles) {
if(!message.isEmpty()) {
message.add(", ");
ConfigurationSection signProfilesSection = plugin.getConfig().getConfigurationSection("signProfiles");
if(signProfilesSection != null) {
Set<String> profiles = signProfilesSection.getKeys(false);
if(!profiles.contains(profile)) {
StringJoiner joiner = new StringJoiner(", ");
for(String p : profiles) {
Message.fromKey("addsign-profile").replacements(p).get().forEach(joiner::add);
}
message.addAll(Message.fromKey("addsign-profile").replacements(p).get());
plugin.message(sender, "addsign-wrongProfile", joiner.toString());
return;
}
plugin.message(sender, "addsign-wrongProfile", Message.fromList(message));
return;
}
}
plugin.getSignlinkerManager().enterSignLinkMode(player, profile);
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
@@ -1,23 +1,21 @@
package me.wiefferink.areashop.commands;
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.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MeCommand extends CommandAreaShop {
public MeCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop me";
@@ -30,7 +28,7 @@ public class MeCommand extends CommandAreaShop {
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.me")) {
@@ -73,7 +71,7 @@ public class MeCommand extends CommandAreaShop {
// Get the regions the player is added as friend
Set<GeneralRegion> friendRegions = new HashSet<>();
for(GeneralRegion region : plugin.getFileManager().getRegions()) {
if(region.getFriendsFeature().getFriends() != null && region.getFriendsFeature().getFriends().contains(player.getUniqueId())) {
if(region.getFriendsFeature().getFriends().contains(player.getUniqueId())) {
friendRegions.add(region);
}
}
@@ -101,11 +99,20 @@ public class MeCommand extends CommandAreaShop {
if(!foundSome) {
plugin.message(sender, "me-nothing", player.getName());
} else {
plugin.messageNoPrefix(sender, "me-clickHint");
}
}
public static void show() {
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
if(toComplete == 2) {
for(Player player : Utils.getOnlinePlayers()) {
result.add(player.getName());
}
}
return result;
}
}
@@ -1,8 +1,7 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.messages.Message;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
@@ -13,10 +12,6 @@ import java.util.List;
public class MessageCommand extends CommandAreaShop {
public MessageCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop message";
@@ -24,9 +19,7 @@ public class MessageCommand extends CommandAreaShop {
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.message")) {
return "help-message";
}
// Internal command, no need to show in the help list
return null;
}
@@ -48,9 +41,9 @@ public class MessageCommand extends CommandAreaShop {
return;
}
String[] messageArgs = new String[args.length-2];
System.arraycopy(args, 2, messageArgs, 0, args.length-2);
String message = StringUtils.join(messageArgs);
String[] messageArgs = new String[args.length - 2];
System.arraycopy(args, 2, messageArgs, 0, args.length - 2);
String message = StringUtils.join(messageArgs, " ");
Message.fromString(message).send(player);
}
@@ -0,0 +1,7 @@
# Commands
This module contains all AreaShop commands.
* `CommandAreaShop` is extended by all other classes, and implements some general functionality and defines which methods should be implemented.
* Other classes implement specific sub-commands, supply their help messages and tab completion.
* `CommandManager` class in the `managers` package handles incoming commands and dispatches them to the classes in this package.
@@ -1,19 +1,14 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import org.bukkit.command.CommandSender;
public class ReloadCommand extends CommandAreaShop {
public ReloadCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop reload";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.reload")) {
@@ -1,8 +1,7 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -11,10 +10,6 @@ import java.util.List;
public class RentCommand extends CommandAreaShop {
public RentCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop rent";
@@ -27,38 +22,38 @@ public class RentCommand extends CommandAreaShop {
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.rent")) {
plugin.message(sender, "rent-noPermission");
return;
}
if (!(sender instanceof Player)) {
if(!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
}
Player player = (Player)sender;
if(args.length > 1 && args[1] != null) {
RentRegion rent = plugin.getFileManager().getRent(args[1]);
if(rent == null) {
plugin.message(sender, "rent-notRentable");
plugin.message(sender, "rent-notRentable", args[1]);
} else {
rent.rent(player);
}
} else {
// get the region by location
List<RentRegion> regions = Utils.getApplicableRentRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<RentRegion> regions = Utils.getImportantRentRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
} else {
regions.get(0).rent(player);
}
}
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -1,8 +1,7 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -11,10 +10,6 @@ import java.util.List;
public class ResellCommand extends CommandAreaShop {
public ResellCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop resell";
@@ -27,14 +22,14 @@ public class ResellCommand extends CommandAreaShop {
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.resell") && !sender.hasPermission("areashop.resellall")) {
plugin.message(sender, "resell-noPermissionOther");
return;
}
if(args.length <= 1) {
plugin.message(sender, "resell-help");
return;
@@ -46,15 +41,21 @@ public class ResellCommand extends CommandAreaShop {
plugin.message(sender, "resell-wrongPrice", args[1]);
return;
}
if(price < 0) {
plugin.message(sender, "resell-wrongPrice", args[1]);
return;
}
BuyRegion buy;
if(args.length <= 2) {
if (sender instanceof Player) {
if(sender instanceof Player) {
// get the region by location
List<BuyRegion> regions = Utils.getApplicableBuyRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<BuyRegion> regions = Utils.getImportantBuyRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
@@ -63,7 +64,7 @@ public class ResellCommand extends CommandAreaShop {
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
} else {
buy = plugin.getFileManager().getBuy(args[2]);
if(buy == null) {
@@ -84,18 +85,24 @@ 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)) {
buy.enableReselling(price);
buy.update();
plugin.message(sender, "resell-success", buy);
} else {
if(!buy.isTransactionHolder((Player)sender)) {
plugin.message(sender, "resell-noPermissionOther", buy);
return;
}
if(buy.getBooleanSetting("buy.resellDisabled")) {
plugin.message(sender, "resell-disabled", buy);
return;
}
buy.enableReselling(price);
buy.update();
plugin.message(sender, "resell-success", buy);
} else {
plugin.message(sender, "resell-noPermission", buy);
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -1,8 +1,9 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
@@ -11,10 +12,6 @@ import java.util.List;
public class SchematiceventCommand extends CommandAreaShop {
public SchematiceventCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop schemevent";
@@ -27,18 +24,18 @@ public class SchematiceventCommand extends CommandAreaShop {
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.schematicevents")) {
plugin.message(sender, "schemevent-noPermission");
return;
}
if(args.length < 3 || args[1] == null || args[2] == null) {
plugin.message(sender, "schemevent-help");
return;
}
}
GeneralRegion region = plugin.getFileManager().getRegion(args[1]);
if(region == null) {
plugin.message(sender, "cmd-notRegistered", args[1]);
@@ -53,7 +50,7 @@ public class SchematiceventCommand extends CommandAreaShop {
try {
event = GeneralRegion.RegionEvent.valueOf(args[2].toUpperCase());
} catch(IllegalArgumentException e) {
exception = true;
exception = true;
}
// Check for a totally wrong event or a non matching event
if(exception) {
@@ -62,8 +59,8 @@ public class SchematiceventCommand extends CommandAreaShop {
values.add(value.getValue().toLowerCase());
}
plugin.message(sender, "schemevent-wrongEvent", args[2], Utils.createCommaSeparatedList(values), region);
return;
}
return;
}
region.handleSchematicEvent(event);
region.update();
plugin.message(sender, "schemevent-success", args[2], region);
@@ -77,9 +74,9 @@ public class SchematiceventCommand extends CommandAreaShop {
} else if(toComplete == 3) {
GeneralRegion region = plugin.getFileManager().getRegion(start[2]);
if(region != null) {
if(region.isRentRegion()) {
if(region instanceof RentRegion) {
result.addAll(Arrays.asList("created", "deleted", "rented", "unrented"));
} else if(region.isBuyRegion()) {
} else if(region instanceof BuyRegion) {
result.addAll(Arrays.asList("created", "deleted", "bought", "sold"));
}
}
@@ -1,9 +1,9 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
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;
@@ -12,90 +12,87 @@ import java.util.List;
public class SellCommand extends CommandAreaShop {
public SellCommand(AreaShop plugin) {
super(plugin);
}
@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 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.getApplicableBuyRegions(((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 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 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;
}
}
@@ -1,8 +1,7 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -11,15 +10,11 @@ import java.util.List;
public class SetdurationCommand extends CommandAreaShop {
public SetdurationCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop setduration";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.setduration")) {
@@ -44,13 +39,13 @@ public class SetdurationCommand extends CommandAreaShop {
}
RentRegion rent;
if(args.length <= regionArgument) {
if (sender instanceof Player) {
if(sender instanceof Player) {
// get the region by location
List<RentRegion> regions = Utils.getApplicableRentRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<RentRegion> regions = Utils.getImportantRentRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
@@ -59,7 +54,7 @@ public class SetdurationCommand extends CommandAreaShop {
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
} else {
rent = plugin.getFileManager().getRent(args[regionArgument]);
}
@@ -70,7 +65,7 @@ public class SetdurationCommand extends CommandAreaShop {
if(!sender.hasPermission("areashop.setduration") && !(sender instanceof Player && rent.isLandlord(((Player)sender).getUniqueId()))) {
plugin.message(sender, "setduration-noLandlord", rent);
return;
}
}
if("default".equalsIgnoreCase(args[1]) || "reset".equalsIgnoreCase(args[1])) {
rent.setDuration(null);
rent.update();
@@ -83,26 +78,26 @@ public class SetdurationCommand extends CommandAreaShop {
plugin.message(sender, "setduration-wrongAmount", args[1], rent);
return;
}
if(!Utils.checkTimeFormat(args[1]+" "+args[2])) {
plugin.message(sender, "setduration-wrongFormat", args[1]+" "+args[2], rent);
if(!Utils.checkTimeFormat(args[1] + " " + args[2])) {
plugin.message(sender, "setduration-wrongFormat", args[1] + " " + args[2], rent);
return;
}
rent.setDuration(args[1]+" "+args[2]);
}
rent.setDuration(args[1] + " " + args[2]);
rent.update();
plugin.message(sender, "setduration-success", rent);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 2) {
result = plugin.getFileManager().getRentNames();
} else if(toComplete == 4) {
if(toComplete == 3) {
result.addAll(plugin.getConfig().getStringList("minutes"));
result.addAll(plugin.getConfig().getStringList("hours"));
result.addAll(plugin.getConfig().getStringList("days"));
result.addAll(plugin.getConfig().getStringList("months"));
result.addAll(plugin.getConfig().getStringList("years"));
} else if(toComplete == 4) {
result = plugin.getFileManager().getRentNames();
}
return result;
}
@@ -1,8 +1,7 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
@@ -13,15 +12,11 @@ import java.util.List;
public class SetlandlordCommand extends CommandAreaShop {
public SetlandlordCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop setlandlord";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.setlandlord")) {
@@ -35,7 +30,7 @@ public class SetlandlordCommand extends CommandAreaShop {
if(!sender.hasPermission("areashop.setlandlord")) {
plugin.message(sender, "setlandlord-noPermission");
return;
}
}
if(args.length < 2) {
plugin.message(sender, "setlandlord-help");
return;
@@ -44,13 +39,13 @@ public class SetlandlordCommand extends CommandAreaShop {
OfflinePlayer player = Bukkit.getOfflinePlayer(args[1]);
GeneralRegion region;
if(args.length < 3) {
if (sender instanceof Player) {
if(sender instanceof Player) {
// get the region by location
List<GeneralRegion> regions = Utils.getAllApplicableRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
@@ -59,14 +54,14 @@ public class SetlandlordCommand extends CommandAreaShop {
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
} else {
region = plugin.getFileManager().getRegion(args[2]);
}
}
if(region == null) {
plugin.message(sender, "cmd-notRegistered", args[2]);
return;
}
}
String playerName = player.getName();
if(playerName == null || playerName.isEmpty()) {
playerName = args[1];
@@ -75,7 +70,7 @@ public class SetlandlordCommand extends CommandAreaShop {
region.update();
plugin.message(sender, "setlandlord-success", playerName, region);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -1,10 +1,10 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
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;
import org.bukkit.command.CommandSender;
@@ -17,10 +17,6 @@ import java.util.UUID;
public class SetownerCommand extends CommandAreaShop {
public SetownerCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop setowner";
@@ -33,7 +29,7 @@ public class SetownerCommand extends CommandAreaShop {
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.setownerrent") && !sender.hasPermission("areashop.setownerbuy")) {
@@ -44,15 +40,15 @@ public class SetownerCommand extends CommandAreaShop {
if(args.length < 2) {
plugin.message(sender, "setowner-help");
return;
}
}
if(args.length == 2) {
if (sender instanceof Player) {
if(sender instanceof Player) {
// get the region by location
List<GeneralRegion> regions = Utils.getAllApplicableRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
@@ -69,59 +65,54 @@ public class SetownerCommand extends CommandAreaShop {
plugin.message(sender, "setowner-notRegistered");
return;
}
if(region.isRentRegion() && !sender.hasPermission("areashop.setownerrent")) {
if(region instanceof RentRegion && !sender.hasPermission("areashop.setownerrent")) {
plugin.message(sender, "setowner-noPermissionRent", region);
return;
}
if(region.isBuyRegion() && !sender.hasPermission("areashop.setownerbuy")) {
if(region instanceof BuyRegion && !sender.hasPermission("areashop.setownerbuy")) {
plugin.message(sender, "setowner-noPermissionBuy", region);
return;
}
UUID uuid = null;
@SuppressWarnings("deprecation")
OfflinePlayer player = Bukkit.getOfflinePlayer(args[1]);
if(player != null) {
if(player.hasPlayedBefore()) {
uuid = player.getUniqueId();
}
if(uuid == null) {
plugin.message(sender, "setowner-noPlayer", args[1], region);
return;
}
if(region.isRentRegion()) {
}
if(region instanceof RentRegion) {
RentRegion rent = (RentRegion)region;
if(rent.isRenter(uuid)) {
if(rent.isTransactionHolder(uuid)) {
// extend
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(rent.getRentedUntil() + rent.getDuration());
rent.setRentedUntil(calendar.getTimeInMillis());
rent.setRenter(uuid);
rent.update();
rent.setRentedUntil(rent.getRentedUntil() + rent.getDuration());
plugin.message(sender, "setowner-succesRentExtend", region);
} else {
// change
Calendar calendar = Calendar.getInstance();
long current = calendar.getTimeInMillis();
if(rent.isRented()) {
current = rent.getRentedUntil();
if(!rent.isRented()) {
rent.setRentedUntil(System.currentTimeMillis() + rent.getDuration());
}
calendar.setTimeInMillis(current + rent.getDuration());
rent.setRentedUntil(calendar.getTimeInMillis());
rent.setRenter(uuid);
rent.update();
plugin.message(sender, "setowner-succesRent", region);
}
rent.setTransactionHolder(uuid);
}
if(region.isBuyRegion()) {
if(region instanceof BuyRegion) {
BuyRegion buy = (BuyRegion)region;
buy.setBuyer(uuid);
buy.update();
buy.setTransactionHolder(uuid);
plugin.message(sender, "setowner-succesBuy", region);
}
if (region instanceof TransactionalRegion) {
region.getFriendsFeature().deleteFriend(((TransactionalRegion) region).getTransactionHolder(), null);
}
region.update();
region.saveRequired();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -1,10 +1,9 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -13,15 +12,11 @@ import java.util.List;
public class SetpriceCommand extends CommandAreaShop {
public SetpriceCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop setprice";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.setprice")) {
@@ -35,20 +30,20 @@ public class SetpriceCommand extends CommandAreaShop {
if(!sender.hasPermission("areashop.setprice") && (!sender.hasPermission("areashop.setprice.landlord") && sender instanceof Player)) {
plugin.message(sender, "setprice-noPermission");
return;
}
}
if(args.length < 2 || args[1] == null) {
plugin.message(sender, "setprice-help");
return;
}
GeneralRegion region;
if(args.length < 3) {
if (sender instanceof Player) {
if(sender instanceof Player) {
// get the region by location
List<GeneralRegion> regions = Utils.getAllApplicableRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
@@ -57,10 +52,10 @@ public class SetpriceCommand extends CommandAreaShop {
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
} else {
region = plugin.getFileManager().getRegion(args[2]);
}
}
if(region == null) {
plugin.message(sender, "setprice-notRegistered", args[2]);
return;
@@ -70,10 +65,10 @@ public class SetpriceCommand extends CommandAreaShop {
return;
}
if("default".equalsIgnoreCase(args[1]) || "reset".equalsIgnoreCase(args[1])) {
if(region.isRentRegion()) {
((RentRegion)region).removePrice();
} else if(region.isBuyRegion()) {
((BuyRegion)region).removePrice();
if(region instanceof RentRegion) {
((RentRegion)region).setPrice(0);
} else if(region instanceof BuyRegion) {
((BuyRegion)region).setPrice(0);
}
region.update();
plugin.message(sender, "setprice-successRemoved", region);
@@ -86,21 +81,21 @@ public class SetpriceCommand extends CommandAreaShop {
plugin.message(sender, "setprice-wrongPrice", args[1], region);
return;
}
if(region.isRentRegion()) {
if(region instanceof RentRegion) {
((RentRegion)region).setPrice(price);
plugin.message(sender, "setprice-successRent", region);
} else if(region.isBuyRegion()) {
} else if(region instanceof BuyRegion) {
((BuyRegion)region).setPrice(price);
plugin.message(sender, "setprice-successBuy", region);
}
region.update();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
if(toComplete == 3) {
result = plugin.getFileManager().getRegionNames();
result = plugin.getFileManager().getRegionNames();
}
return result;
}
@@ -1,6 +1,5 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.command.CommandSender;
@@ -9,15 +8,11 @@ import java.util.List;
public class SetrestoreCommand extends CommandAreaShop {
public SetrestoreCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop setrestore";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.setrestore")) {
@@ -50,7 +45,7 @@ public class SetrestoreCommand extends CommandAreaShop {
region.setRestoreSetting(value);
String valueString = "general";
if(value != null) {
valueString = value+"";
valueString = value + "";
}
if(args.length > 3) {
region.setSchematicProfile(args[3]);
@@ -60,7 +55,7 @@ public class SetrestoreCommand extends CommandAreaShop {
}
region.update();
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
@@ -1,11 +1,10 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -14,25 +13,21 @@ import java.util.List;
public class SetteleportCommand extends CommandAreaShop {
public SetteleportCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop settp";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.teleportall") || target.hasPermission("areashop.teleport")) {
if(target.hasPermission("areashop.setteleportall") || target.hasPermission("areashop.setteleport")) {
return "help-setteleport";
}
return null;
}
/**
* Check if a person can set the teleport location of the region
* Check if a person can set the teleport location of the region.
* @param person The person to check
* @param region The region to check for
* @return true if the person can set the teleport location, otherwise false
@@ -52,7 +47,7 @@ public class SetteleportCommand extends CommandAreaShop {
plugin.message(sender, "setteleport-noPermission");
return;
}
if (!(sender instanceof Player)) {
if(!(sender instanceof Player)) {
plugin.message(sender, "onlyByPlayer");
return;
}
@@ -60,7 +55,7 @@ public class SetteleportCommand extends CommandAreaShop {
GeneralRegion region;
if(args.length < 2) {
// get the region by location
List<GeneralRegion> regions = Utils.getAllApplicableRegions(((Player)sender).getLocation());
List<GeneralRegion> regions = Utils.getImportantRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
@@ -75,12 +70,12 @@ public class SetteleportCommand extends CommandAreaShop {
}
boolean owner;
if(region == null) {
plugin.message(player, "setteleport-noRentOrBuy", args[1]);
return;
}
if(region.isRentRegion()) {
if(region instanceof RentRegion) {
owner = player.getUniqueId().equals(((RentRegion)region).getRenter());
} else {
owner = player.getUniqueId().equals(((BuyRegion)region).getBuyer());
@@ -95,7 +90,7 @@ public class SetteleportCommand extends CommandAreaShop {
ProtectedRegion wgRegion = region.getRegion();
if(args.length > 2 && args[2] != null && (args[2].equalsIgnoreCase("reset") || args[2].equalsIgnoreCase("yes") || args[2].equalsIgnoreCase("true"))) {
region.setTeleport(null);
region.getTeleportFeature().setTeleport(null);
region.update();
plugin.message(player, "setteleport-reset", region);
return;
@@ -104,11 +99,11 @@ public class SetteleportCommand extends CommandAreaShop {
plugin.message(player, "setteleport-notInside", region);
return;
}
region.setTeleport(player.getLocation());
region.getTeleportFeature().setTeleport(player.getLocation());
region.update();
plugin.message(player, "setteleport-success", region);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -1,36 +1,33 @@
package me.wiefferink.areashop.commands;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.bukkit.selections.Selection;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.messages.Message;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.interfaces.WorldEditSelection;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RegionGroup;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
public class StackCommand extends CommandAreaShop {
public StackCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop stack";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.stack")) {
@@ -47,7 +44,7 @@ public class StackCommand extends CommandAreaShop {
return;
}
// Only from ingame
if (!(sender instanceof Player)) {
if(!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
@@ -82,11 +79,11 @@ public class StackCommand extends CommandAreaShop {
return;
}
// Get WorldEdit selection
final Selection selection = plugin.getWorldEdit().getSelection(player);
final WorldEditSelection selection = plugin.getWorldEditHandler().getPlayerSelection(player);
if(selection == null) {
plugin.message(player, "stack-noSelection");
return;
}
}
// Get or create group
RegionGroup group = null;
if(args.length > 5) {
@@ -98,112 +95,149 @@ public class StackCommand extends CommandAreaShop {
}
// Get facing of the player (must be clearly one of the four directions to make sure it is no mistake)
BlockFace facing = Utils.yawToFacing(player.getLocation().getYaw());
if(!(facing == BlockFace.NORTH || facing == BlockFace.EAST || facing == BlockFace.SOUTH || facing == BlockFace.WEST)) {
if(player.getLocation().getPitch() > 45) {
facing = BlockFace.DOWN;
} else if(player.getLocation().getPitch() < -45) {
facing = BlockFace.UP;
}
if(!(facing == BlockFace.NORTH || facing == BlockFace.EAST || facing == BlockFace.SOUTH || facing == BlockFace.WEST || facing == BlockFace.UP || facing == BlockFace.DOWN)) {
plugin.message(player, "stack-unclearDirection", facing.toString().toLowerCase().replace('_', '-'));
return;
}
final Location shift = new Location(selection.getWorld(), 0, 0, 0);
Vector shift = new Vector(0, 0, 0);
if(facing == BlockFace.SOUTH) {
shift.setZ(-selection.getLength() - gap);
shift = shift.setZ(-selection.getLength() - gap);
} else if(facing == BlockFace.WEST) {
shift.setX(selection.getWidth() + gap);
shift = shift.setX(selection.getWidth() + gap);
} else if(facing == BlockFace.NORTH) {
shift.setZ(selection.getLength() + gap);
shift = shift.setZ(selection.getLength() + gap);
} else if(facing == BlockFace.EAST) {
shift.setX(-selection.getWidth() - gap);
shift = shift.setX(-selection.getWidth() - gap);
} else if(facing == BlockFace.DOWN) {
shift = shift.setY(-selection.getHeight() - gap);
} else if(facing == BlockFace.UP) {
shift = shift.setY(selection.getHeight() + gap);
}
AreaShop.debug(" calculated shift vector: " + shift + ", with facing=" + facing);
// Create regions and add them to AreaShop
final String namePrefix = args[3];
final String nameTemplate = args[3];
final int regionsPerTick = plugin.getConfig().getInt("adding.regionsPerTick");
final boolean rentRegions = "rent".equalsIgnoreCase(args[4]);
final int amount = tempAmount;
final RegionGroup finalGroup = group;
final Vector finalShift = shift;
String type;
if(rentRegions) {
type = "rent";
} else {
type = "buy";
}
Message groupsMessage = Message.none();
Message groupsMessage = Message.empty();
if(group != null) {
groupsMessage = Message.fromKey("stack-addToGroup").replacements(group.getName());
}
plugin.message(player, "stack-accepted", amount, type, gap, namePrefix, groupsMessage);
plugin.message(player, "stack-addStart", amount, regionsPerTick*20);
}
plugin.message(player, "stack-accepted", amount, type, gap, nameTemplate, groupsMessage);
plugin.message(player, "stack-addStart", amount, regionsPerTick * 20);
Location minimumLocation = selection.getMinimumLocation();
Vector minimumVector = new Vector(minimumLocation.getX(), minimumLocation.getY(), minimumLocation.getZ());
Location maximumLocation = selection.getMaximumLocation();
Vector maximumVector = new Vector(maximumLocation.getX(), maximumLocation.getY(), maximumLocation.getZ());
new BukkitRunnable() {
private int current = 0;
private RegionManager manager = AreaShop.getInstance().getWorldGuard().getRegionManager(selection.getWorld());
private int current = -1;
private final RegionManager manager = AreaShop.getInstance().getRegionManager(selection.getWorld());
private int counter = 1;
private int tooLow = 0;
private int tooHigh = 0;
@Override
public void run() {
for(int i=0; i<regionsPerTick; i++) {
for(int i = 0; i < regionsPerTick; i++) {
current++;
if(current < amount) {
// Create the region name
String counterName = counter+"";
int minimumLength = plugin.getConfig().getInt("stackRegionNumberLength");
while(counterName.length() < minimumLength) {
counterName = "0"+counterName;
}
String regionName = namePrefix + counterName;
String regionName = countToName(nameTemplate, counter);
while(manager.getRegion(regionName) != null || AreaShop.getInstance().getFileManager().getRegion(regionName) != null) {
counter++;
counterName = counter+"";
minimumLength = plugin.getConfig().getInt("stackRegionNumberLength");
while(counterName.length() < minimumLength) {
counterName = "0"+counterName;
}
regionName = namePrefix + counterName;
regionName = countToName(nameTemplate, counter);
}
// Add the region to WorldGuard (at startposition shifted by the number of this region times the blocks it should shift)
ProtectedCuboidRegion region = new ProtectedCuboidRegion(regionName,
new BlockVector(
selection.getMinimumPoint().getBlockX() + shift.getBlockX()*current,
selection.getMinimumPoint().getBlockY() + shift.getBlockY()*current,
selection.getMinimumPoint().getBlockZ() + shift.getBlockZ()*current
),
new BlockVector(
selection.getMaximumPoint().getBlockX() + shift.getBlockX()*current,
selection.getMaximumPoint().getBlockY() + shift.getBlockY()*current,
selection.getMaximumPoint().getBlockZ() + shift.getBlockZ()*current
)
);
manager.addRegion(region);
// Add the region to AreaShop
if(rentRegions) {
RentRegion rent = new RentRegion(plugin, region.getId(), selection.getWorld());
if(finalGroup != null) {
finalGroup.addMember(rent);
}
rent.runEventCommands(GeneralRegion.RegionEvent.CREATED, true);
plugin.getFileManager().addRent(rent);
rent.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
rent.runEventCommands(GeneralRegion.RegionEvent.CREATED, false);
rent.update();
} else {
BuyRegion buy = new BuyRegion(plugin, region.getId(), selection.getWorld());
if(finalGroup != null) {
finalGroup.addMember(buy);
}
buy.runEventCommands(GeneralRegion.RegionEvent.CREATED, true);
plugin.getFileManager().addBuy(buy);
buy.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
buy.runEventCommands(GeneralRegion.RegionEvent.CREATED, false);
buy.update();
Vector minimum = minimumVector.clone().add(finalShift.clone().multiply(current));
Vector maximum = maximumVector.clone().add(finalShift.clone().multiply(current));
// Check for out of bounds
if(minimum.getBlockY() < 0) {
tooLow++;
continue;
} else if(maximum.getBlockY() > 256) {
tooHigh++;
continue;
}
current++;
}
ProtectedCuboidRegion region = plugin.getWorldGuardHandler().createCuboidRegion(regionName, minimum,maximum);
manager.addRegion(region);
// Add the region to AreaShop
GeneralRegion newRegion;
if(rentRegions) {
newRegion = new RentRegion(regionName, selection.getWorld());
} else {
newRegion = new BuyRegion(regionName, selection.getWorld());
}
if(finalGroup != null) {
finalGroup.addMember(newRegion);
}
AddingRegionEvent event = plugin.getFileManager().addRegion(newRegion);
if (event.isCancelled()) {
plugin.message(player, "general-cancelled", event.getReason());
continue;
}
newRegion.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
newRegion.update();
}
}
if(current >= amount) {
if(player.isOnline()) {
plugin.message(player, "stack-addComplete");
int added = amount - tooLow - tooHigh;
Message wrong = Message.empty();
if(tooHigh > 0) {
wrong.append(Message.fromKey("stack-tooHigh").replacements(tooHigh));
}
if(tooLow > 0) {
wrong.append(Message.fromKey("stack-tooLow").replacements(tooLow));
}
plugin.message(player, "stack-addComplete", added, wrong);
}
this.cancel();
}
}
}.runTaskTimer(plugin, 1, 1);
}
/**
* Build a name from a count, with the right length.
* @param template Template to put the name in (# to put the count there, otherwise count is appended)
* @param count Number to use
* @return name with prepended 0's
*/
private String countToName(String template, int count) {
StringBuilder counterName = new StringBuilder().append(count);
int minimumLength = plugin.getConfig().getInt("stackRegionNumberLength");
while(counterName.length() < minimumLength) {
counterName.insert(0, "0");
}
if(template.contains("#")) {
return template.replace("#", counterName);
} else {
return template + counterName;
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
List<String> result = new ArrayList<>();
@@ -1,8 +1,7 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -11,10 +10,6 @@ import java.util.List;
public class StopresellCommand extends CommandAreaShop {
public StopresellCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop stopresell";
@@ -27,7 +22,7 @@ public class StopresellCommand extends CommandAreaShop {
}
return null;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.stopresell") && !sender.hasPermission("areashop.stopresellall")) {
@@ -37,13 +32,13 @@ public class StopresellCommand extends CommandAreaShop {
BuyRegion buy;
if(args.length <= 1) {
if (sender instanceof Player) {
if(sender instanceof Player) {
// get the region by location
List<BuyRegion> regions = Utils.getApplicableBuyRegions(((Player) sender).getLocation());
if (regions.isEmpty()) {
List<BuyRegion> regions = Utils.getImportantBuyRegions(((Player)sender).getLocation());
if(regions.isEmpty()) {
plugin.message(sender, "cmd-noRegionsAtLocation");
return;
} else if (regions.size() > 1) {
} else if(regions.size() > 1) {
plugin.message(sender, "cmd-moreRegionsAtLocation");
return;
} else {
@@ -52,7 +47,7 @@ public class StopresellCommand extends CommandAreaShop {
} else {
plugin.message(sender, "cmd-automaticRegionOnlyByPlayer");
return;
}
}
} else {
buy = plugin.getFileManager().getBuy(args[1]);
if(buy == null) {
@@ -82,9 +77,9 @@ public class StopresellCommand extends CommandAreaShop {
}
} else {
plugin.message(sender, "stopresell-noPermission", buy);
}
}
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -1,6 +1,5 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -10,15 +9,11 @@ import java.util.List;
public class TeleportCommand extends CommandAreaShop {
public TeleportCommand(AreaShop plugin) {
super(plugin);
}
@Override
public String getCommandStart() {
return "areashop tp";
}
@Override
public String getHelp(CommandSender target) {
if(target.hasPermission("areashop.teleportall") || target.hasPermission("areashop.teleport")) {
@@ -28,7 +23,7 @@ public class TeleportCommand extends CommandAreaShop {
}
/**
* Check if a person can teleport to the region (assuming he is not teleporting to a sign)
* Check if a person can teleport to the region (assuming he is not teleporting to a sign).
* @param person The person to check
* @param region The region to check for
* @return true if the person can teleport to it, otherwise false
@@ -40,19 +35,20 @@ public class TeleportCommand extends CommandAreaShop {
Player player = (Player)person;
return player.hasPermission("areashop.teleportall")
|| region.isOwner(player) && player.hasPermission("areashop.teleport")
|| region.isAvailable() && player.hasPermission("areashop.teleportavailable")
|| region.getFriendsFeature().getFriends().contains(player.getUniqueId()) && player.hasPermission("areashop.teleportfriend");
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("areashop.teleport") && !sender.hasPermission("areashop.teleportall") && !sender.hasPermission("areashop.teleportsign") && !sender.hasPermission("areashop.teleportsignall") && !sender.hasPermission("areashop.teleportfriend") && !sender.hasPermission("teleportfriendsign")) {
if(!sender.hasPermission("areashop.teleport") && !sender.hasPermission("areashop.teleportall") && !sender.hasPermission("areashop.teleportavailable") && !sender.hasPermission("areashop.teleportavailablesign") && !sender.hasPermission("areashop.teleportsign") && !sender.hasPermission("areashop.teleportsignall") && !sender.hasPermission("areashop.teleportfriend") && !sender.hasPermission("teleportfriendsign")) {
plugin.message(sender, "teleport-noPermission");
return;
}
if (!(sender instanceof Player)) {
if(!(sender instanceof Player)) {
plugin.message(sender, "cmd-onlyByPlayer");
return;
}
}
if(args.length <= 1 || args[1] == null) {
plugin.message(sender, "teleport-help");
return;
@@ -63,14 +59,15 @@ public class TeleportCommand extends CommandAreaShop {
plugin.message(player, "teleport-noRentOrBuy", args[1]);
return;
}
if(args.length >= 3 && (args[2].equalsIgnoreCase("sign") || args[2].equalsIgnoreCase("yes") || args[2].equalsIgnoreCase("true"))) {
region.teleportPlayer(player, true);
} else {
region.teleportPlayer(player, false);
}
boolean toSign = args.length >= 3 && (
args[2].equalsIgnoreCase("sign")
|| args[2].equalsIgnoreCase("yes")
|| args[2].equalsIgnoreCase("true")
);
region.getTeleportFeature().teleportPlayer(player, toSign, true);
}
@Override
public List<String> getTabCompleteList(int toComplete, String[] start, CommandSender sender) {
ArrayList<String> result = new ArrayList<>();
@@ -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;
}
}
@@ -1,9 +1,9 @@
package me.wiefferink.areashop.commands;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
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;
@@ -12,90 +12,87 @@ import java.util.List;
public class UnrentCommand extends CommandAreaShop {
public UnrentCommand(AreaShop plugin) {
super(plugin);
}
@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.getApplicableRentRegions(((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 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;
}
}
@@ -1,25 +1,16 @@
package me.wiefferink.areashop.events;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class CancellableRegionEvent<T> extends RegionEvent<T> {
public class CancellableAreaShopEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
private String reason;
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
public CancellableRegionEvent(T region) {
super(region);
}
/**
* Cancel the event from happening
* Cancel the event from happening.
* @param reason The reason of cancelling, used for display to the user, should end with a dot
*/
public void cancel(String reason) {
@@ -28,7 +19,7 @@ public class CancellableAreaShopEvent extends Event {
}
/**
* Let the event continue, possible overwriting a cancel() call from another plugin
* Let the event continue, possible overwriting a cancel() call from another plugin.
*/
public void allow() {
this.cancelled = false;
@@ -36,7 +27,7 @@ public class CancellableAreaShopEvent extends Event {
}
/**
* Check if the event has been cancelled
* Check if the event has been cancelled.
* @return true if the event has been cancelled, otherwise false
*/
public boolean isCancelled() {
@@ -44,10 +35,11 @@ public class CancellableAreaShopEvent extends Event {
}
/**
* Get the reason why this event is cancelled
* Get the reason why this event is cancelled.
* @return null if there is no reason or the event is not cancelled, otherwise a string
*/
public String getReason() {
return reason;
}
}
@@ -0,0 +1,7 @@
package me.wiefferink.areashop.events;
public class NotifyRegionEvent<T> extends RegionEvent<T> {
public NotifyRegionEvent(T region) {
super(region);
}
}
@@ -0,0 +1,7 @@
# Events
This package contains event classes that allow other plugins to listen to events about AreaShop regions.
* `RegionEvent` class represents a generic event.
* `CancellableRegionEvent` represents an event that has a region and can be cancelled.
* `NotifyRegionEvent` represents events that are to inform only, and cannot be cancelled anymore (an `CancellableRegionEvent` would be sent first).
@@ -3,16 +3,29 @@ package me.wiefferink.areashop.events;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class NotifyAreaShopEvent extends Event {
public class RegionEvent<T> extends Event {
protected T region;
private static final HandlerList handlers = new HandlerList();
public RegionEvent(T region) {
this.region = region;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
// Required by Bukkit/Spigot
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Get the region of this event.
* @return The region the event is about
*/
public T getRegion() {
return region;
}
}
@@ -0,0 +1,19 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when a region has been added to AreaShop.
*/
public class AddingRegionEvent extends CancellableRegionEvent<GeneralRegion> {
/**
* Constructor.
* @param region The region that has been added
*/
public AddingRegionEvent(GeneralRegion region) {
super(region);
}
}
@@ -1,40 +1,31 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableAreaShopEvent;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
import org.bukkit.entity.Player;
import org.bukkit.OfflinePlayer;
/**
* Broadcasted when a player tries to buy a region.
*/
public class BuyingRegionEvent extends CancellableAreaShopEvent {
public class BuyingRegionEvent extends CancellableRegionEvent<BuyRegion> {
private BuyRegion region;
private Player player;
private final OfflinePlayer player;
/**
* Constructor
* Constructor.
* @param region The region that is about to get bought
* @param player The player that tries to buy the region
*/
public BuyingRegionEvent(BuyRegion region, Player player) {
this.region = region;
public BuyingRegionEvent(BuyRegion region, OfflinePlayer player) {
super(region);
this.player = player;
}
/**
* Get the region that is about to be bought
* @return the region that is about to be bought
*/
public BuyRegion getRegion() {
return region;
}
/**
* Get the player that is trying to buy the region
* Get the player that is trying to buy the region.
* @return The player that is trying to buy the region
*/
public Player getPlayer() {
public OfflinePlayer getPlayer() {
return player;
}
}
@@ -0,0 +1,19 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when a region has been added to AreaShop.
*/
public class DeletingRegionEvent extends CancellableRegionEvent<GeneralRegion> {
/**
* Constructor.
* @param region The region that has been added
*/
public DeletingRegionEvent(GeneralRegion region) {
super(region);
}
}
@@ -1,48 +1,39 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableAreaShopEvent;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.RentRegion;
import org.bukkit.entity.Player;
import org.bukkit.OfflinePlayer;
/**
* Broadcasted when a player tries to rent a region
* Broadcasted when a player tries to rent a region.
*/
public class RentingRegionEvent extends CancellableAreaShopEvent {
public class RentingRegionEvent extends CancellableRegionEvent<RentRegion> {
private RentRegion region;
private Player player;
private boolean extending;
private final OfflinePlayer player;
private final boolean extending;
/**
* Constructor
* Constructor.
* @param region The region that is about to be rented
* @param player The player that tries to rent the region
* @param extending true if the player is extending the rental of the region, otherwise false
*/
public RentingRegionEvent(RentRegion region, Player player, boolean extending) {
this.region = region;
public RentingRegionEvent(RentRegion region, OfflinePlayer player, boolean extending) {
super(region);
this.player = player;
this.extending = extending;
}
/**
* Get the region that is about to be rented
* @return the region that is about to be rented
*/
public RentRegion getRegion() {
return region;
}
/**
* Get the player that is trying to rent the region
* Get the player that is trying to rent the region.
* @return The player that is trying to rent the region
*/
public Player getPlayer() {
public OfflinePlayer getPlayer() {
return player;
}
/**
* Check if the player is extending the region or renting it for the first time
* Check if the player is extending the region or renting it for the first time.
* @return true if the player tries to extend the region, false if he tries to rent it the first time
*/
public boolean isExtending() {
@@ -1,40 +1,31 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableAreaShopEvent;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
import org.bukkit.entity.Player;
import org.bukkit.OfflinePlayer;
/**
* Broadcasted when a player tries to resell a region
* Broadcasted when a player tries to resell a region.
*/
public class ResellingRegionEvent extends CancellableAreaShopEvent {
public class ResellingRegionEvent extends CancellableRegionEvent<BuyRegion> {
private BuyRegion region;
private Player player;
private final OfflinePlayer player;
/**
* Contructor
* Contructor.
* @param region The region that the player is trying to resell
* @param player The player that is trying to buy this region from the current owner
*/
public ResellingRegionEvent(BuyRegion region, Player player) {
this.region = region;
public ResellingRegionEvent(BuyRegion region, OfflinePlayer player) {
super(region);
this.player = player;
}
/**
* Get the region that is about to be bought
* @return the region that is about to be bought
*/
public BuyRegion getRegion() {
return region;
}
/**
* Get the player that is trying to buy the region
* Get the player that is trying to buy the region.
* @return The player that is trying to buy the region
*/
public Player getBuyer() {
public OfflinePlayer getBuyer() {
return player;
}
}
@@ -1,28 +1,18 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableAreaShopEvent;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
/**
* Broadcasted when a region is about to get sold
* Broadcasted when a region is about to get sold.
*/
public class SellingRegionEvent extends CancellableAreaShopEvent {
private BuyRegion region;
public class SellingRegionEvent extends CancellableRegionEvent<BuyRegion> {
/**
* Constructor
* Constructor.
* @param region The region that is about to get sold
*/
public SellingRegionEvent(BuyRegion region) {
this.region = region;
}
/**
* Get the region that is about to be sold
* @return the region that is about to be sold
*/
public BuyRegion getRegion() {
return region;
super(region);
}
}
@@ -1,28 +1,18 @@
package me.wiefferink.areashop.events.ask;
import me.wiefferink.areashop.events.CancellableAreaShopEvent;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.RentRegion;
/**
* Broadcasted when a region is about to be unrented
* Broadcasted when a region is about to be unrented.
*/
public class UnrentingRegionEvent extends CancellableAreaShopEvent {
private RentRegion region;
public class UnrentingRegionEvent extends CancellableRegionEvent<RentRegion> {
/**
* Constructor
* Constructor.
* @param region The region that is about to be unrented
*/
public UnrentingRegionEvent(RentRegion region) {
this.region = region;
}
/**
* Get the region that is about to be rented
* @return the region that is about to be rented
*/
public RentRegion getRegion() {
return region;
super(region);
}
}
@@ -1,41 +1,32 @@
package me.wiefferink.areashop.events.askandnotify;
import me.wiefferink.areashop.events.CancellableAreaShopEvent;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
/**
* Broadcasted when a friend is being added to a region
* Broadcasted when a friend is being added to a region.
*/
public class AddFriendEvent extends CancellableAreaShopEvent {
public class AddedFriendEvent extends CancellableRegionEvent<GeneralRegion> {
private GeneralRegion region;
private OfflinePlayer friend;
private CommandSender by;
private final OfflinePlayer friend;
private final CommandSender by;
/**
* Constructor
* Constructor.
* @param region The region the friend is getting added to
* @param friend The friend that is about to be added
* @param by The CommandSender that is adding the friend, or null if none
*/
public AddFriendEvent(GeneralRegion region, OfflinePlayer friend, CommandSender by) {
this.region = region;
public AddedFriendEvent(GeneralRegion region, OfflinePlayer friend, CommandSender by) {
super(region);
this.friend = friend;
this.by = by;
}
/**
* Get the region where the friend is getting added to
* @return the region
*/
public GeneralRegion getRegion() {
return region;
}
/**
* Get the OfflinePlayer that is getting added as friend
* Get the OfflinePlayer that is getting added as friend.
* @return The friend that is getting added
*/
public OfflinePlayer getFriend() {
@@ -43,7 +34,7 @@ public class AddFriendEvent extends CancellableAreaShopEvent {
}
/**
* Get the CommandSender that is adding the friend
* Get the CommandSender that is adding the friend.
* @return null if none, a CommandSender if done by someone (likely Player or ConsoleCommandSender)
*/
public CommandSender getBy() {
@@ -1,41 +1,32 @@
package me.wiefferink.areashop.events.askandnotify;
import me.wiefferink.areashop.events.CancellableAreaShopEvent;
import me.wiefferink.areashop.events.CancellableRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
/**
* Broadcasted when a friend is being added to a region
* Broadcasted when a friend is being added to a region.
*/
public class DeleteFriendEvent extends CancellableAreaShopEvent {
public class DeletedFriendEvent extends CancellableRegionEvent<GeneralRegion> {
private GeneralRegion region;
private OfflinePlayer friend;
private CommandSender by;
private final OfflinePlayer friend;
private final CommandSender by;
/**
* Constructor
* Constructor.
* @param region The region the friend is getting added to
* @param friend The friend that is about to be added
* @param by The CommandSender that is adding the friend, or null if none
*/
public DeleteFriendEvent(GeneralRegion region, OfflinePlayer friend, CommandSender by) {
this.region = region;
public DeletedFriendEvent(GeneralRegion region, OfflinePlayer friend, CommandSender by) {
super(region);
this.friend = friend;
this.by = by;
}
/**
* Get the region where the friend is getting added to
* @return the region
*/
public GeneralRegion getRegion() {
return region;
}
/**
* Get the OfflinePlayer that is getting added as friend
* Get the OfflinePlayer that is getting added as friend.
* @return The friend that is getting added
*/
public OfflinePlayer getFriend() {
@@ -43,7 +34,7 @@ public class DeleteFriendEvent extends CancellableAreaShopEvent {
}
/**
* Get the CommandSender that is adding the friend
* Get the CommandSender that is adding the friend.
* @return null if none, a CommandSender if done by someone (likely Player or ConsoleCommandSender)
*/
public CommandSender getBy() {
@@ -1,28 +1,19 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyAreaShopEvent;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when a region has been added to AreaShop
* Broadcasted when a region has been added to AreaShop.
*/
public class AddedRegionEvent extends NotifyAreaShopEvent {
private GeneralRegion region;
public class AddedRegionEvent extends NotifyRegionEvent<GeneralRegion> {
/**
* Constructor
* Constructor.
* @param region The region that has been added
*/
public AddedRegionEvent(GeneralRegion region) {
this.region = region;
super(region);
}
/**
* Get the region that has been added
* @return the region that has been added
*/
public GeneralRegion getRegion() {
return region;
}
}
@@ -1,28 +1,19 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyAreaShopEvent;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
/**
* Broadcasted when a region has been bought
* Broadcasted when a region has been bought.
*/
public class BoughtRegionEvent extends NotifyAreaShopEvent {
private BuyRegion region;
public class BoughtRegionEvent extends NotifyRegionEvent<BuyRegion> {
/**
* Constructor
* Constructor.
* @param region The region that has been bought
*/
public BoughtRegionEvent(BuyRegion region) {
this.region = region;
super(region);
}
/**
* Get the region that has been bought
* @return the region that has been bought
*/
public BuyRegion getRegion() {
return region;
}
}
@@ -0,0 +1,18 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when a region has been removed from AreaShop.
*/
public class DeletedRegionEvent extends NotifyRegionEvent<GeneralRegion> {
/**
* Constructor.
* @param region The region that has been removed
*/
public DeletedRegionEvent(GeneralRegion region) {
super(region);
}
}
@@ -1,29 +0,0 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyAreaShopEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when the data of a region changes.
* Should be used for updating displays that use region data.
*/
public class RegionUpdateEvent extends NotifyAreaShopEvent {
private GeneralRegion region;
/**
* Contructor
* @param region The region that has been updated
*/
public RegionUpdateEvent(GeneralRegion region) {
this.region = region;
}
/**
* The region that has been updated
* @return The GeneralRegion that has been updated
*/
public GeneralRegion getRegion() {
return region;
}
}
@@ -1,28 +0,0 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyAreaShopEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when a region has been removed from AreaShop
*/
public class RemovedRegionEvent extends NotifyAreaShopEvent {
private GeneralRegion region;
/**
* Constructor
* @param region The region that has been removed
*/
public RemovedRegionEvent(GeneralRegion region) {
this.region = region;
}
/**
* Get the region that has been removed
* @return the region that has been removed
*/
public GeneralRegion getRegion() {
return region;
}
}
@@ -1,36 +1,28 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyAreaShopEvent;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.RentRegion;
/**
* Broadcasted when a region has been rented
* Broadcasted when a region has been rented.
*/
public class RentedRegionEvent extends NotifyAreaShopEvent {
public class RentedRegionEvent extends NotifyRegionEvent<RentRegion> {
private RentRegion region;
private boolean extended;
private final boolean extended;
/**
* Constructor
* Constructor.
* @param region The region that has been rented
* @param extended true if the region has been extended, false if this is the first time buying the region
*/
public RentedRegionEvent(RentRegion region, boolean extended) {
super(region);
this.region = region;
this.extended = extended;
}
/**
* Get the region that has been rented
* @return the region that has been rented
*/
public RentRegion getRegion() {
return region;
}
/**
* Check if the region was extended or rented for the first time
* Check if the region was extended or rented for the first time.
* @return true if the region was extended, false when rented for the first time
*/
public boolean hasExtended() {
@@ -1,38 +1,29 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyAreaShopEvent;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
import java.util.UUID;
/**
* Broadcasted when a region has been resold
* Broadcasted when a region has been resold.
*/
public class ResoldRegionEvent extends NotifyAreaShopEvent {
public class ResoldRegionEvent extends NotifyRegionEvent<BuyRegion> {
private BuyRegion region;
private UUID from;
private final UUID from;
/**
* Constructor
* Constructor.
* @param region The region that has been resold
* @param from The player from who the region has been resold to the current owner
*/
public ResoldRegionEvent(BuyRegion region, UUID from) {
this.region = region;
super(region);
this.from = from;
}
/**
* Get the region that has been bought
* @return the region that has been bought
*/
public BuyRegion getRegion() {
return region;
}
/**
* Get the player that the region has been bought from
* Get the player that the region has been bought from.
* @return The UUID of the player that the region has been bought from
*/
public UUID getFromPlayer() {
@@ -1,41 +1,32 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyAreaShopEvent;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.BuyRegion;
import java.util.UUID;
/**
* Broadcasted when a region is sold
* Broadcasted when a region is sold.
*/
public class SoldRegionEvent extends NotifyAreaShopEvent {
public class SoldRegionEvent extends NotifyRegionEvent<BuyRegion> {
private BuyRegion region;
private UUID oldBuyer;
private double refundedMoney;
private final UUID oldBuyer;
private final double refundedMoney;
/**
* Constructor
* @param region The region that has been sold
* @param oldBuyer The player for which the region has been sold
* Constructor.
* @param region The region that has been sold
* @param oldBuyer The player for which the region has been sold
* @param refundedMoney The amount of money that has been refunded
*/
public SoldRegionEvent(BuyRegion region, UUID oldBuyer, double refundedMoney) {
this.region = region;
super(region);
this.oldBuyer = oldBuyer;
this.refundedMoney = refundedMoney;
}
/**
* Get the region that has been sold
* @return the region that has been sold
*/
public BuyRegion getRegion() {
return region;
}
/**
* Get the player that the region is sold for
* Get the player that the region is sold for.
* @return The UUID of the player that the region is sold for
*/
public UUID getOldBuyer() {
@@ -43,7 +34,7 @@ public class SoldRegionEvent extends NotifyAreaShopEvent {
}
/**
* Get the amount that is paid back to the player
* Get the amount that is paid back to the player.
* @return The amount of money paid back to the player
*/
public double getRefundedMoney() {
@@ -1,41 +1,32 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyAreaShopEvent;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.RentRegion;
import java.util.UUID;
/**
* Broadcasted when a region has been unrented
* Broadcasted when a region has been unrented.
*/
public class UnrentedRegionEvent extends NotifyAreaShopEvent {
public class UnrentedRegionEvent extends NotifyRegionEvent<RentRegion> {
private RentRegion region;
private UUID oldRenter;
private double refundedMoney;
private final UUID oldRenter;
private final double refundedMoney;
/**
* Constructor
* @param region The region that has been unrented
* @param oldRenter The player that rented the region before it was unrented
* Constructor.
* @param region The region that has been unrented
* @param oldRenter The player that rented the region before it was unrented
* @param refundedMoney The amount of money that has been refunded
*/
public UnrentedRegionEvent(RentRegion region, UUID oldRenter, double refundedMoney) {
this.region = region;
super(region);
this.oldRenter = oldRenter;
this.refundedMoney = refundedMoney;
}
/**
* Get the region that has been unrented
* @return the region that has been unrented
*/
public RentRegion getRegion() {
return region;
}
/**
* Get the player that the region was unrented for
* Get the player that the region was unrented for.
* @return The UUID of the player that the region was unrented for
*/
public UUID getOldRenter() {
@@ -43,7 +34,7 @@ public class UnrentedRegionEvent extends NotifyAreaShopEvent {
}
/**
* Get the amount that is paid back to the player
* Get the amount that is paid back to the player.
* @return The amount of money paid back to the player
*/
public double getRefundedMoney() {
@@ -0,0 +1,19 @@
package me.wiefferink.areashop.events.notify;
import me.wiefferink.areashop.events.NotifyRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
/**
* Broadcasted when the data of a region changes.
* Should be used for updating displays that use region data.
*/
public class UpdateRegionEvent extends NotifyRegionEvent<GeneralRegion> {
/**
* Contructor.
* @param region The region that has been updated
*/
public UpdateRegionEvent(GeneralRegion region) {
super(region);
}
}
@@ -0,0 +1,118 @@
package me.wiefferink.areashop.features;
import me.wiefferink.areashop.events.ask.AddingRegionEvent;
import me.wiefferink.areashop.events.ask.BuyingRegionEvent;
import me.wiefferink.areashop.events.ask.DeletingRegionEvent;
import me.wiefferink.areashop.events.ask.RentingRegionEvent;
import me.wiefferink.areashop.events.ask.ResellingRegionEvent;
import me.wiefferink.areashop.events.ask.SellingRegionEvent;
import me.wiefferink.areashop.events.ask.UnrentingRegionEvent;
import me.wiefferink.areashop.events.notify.AddedRegionEvent;
import me.wiefferink.areashop.events.notify.BoughtRegionEvent;
import me.wiefferink.areashop.events.notify.DeletedRegionEvent;
import me.wiefferink.areashop.events.notify.RentedRegionEvent;
import me.wiefferink.areashop.events.notify.ResoldRegionEvent;
import me.wiefferink.areashop.events.notify.SoldRegionEvent;
import me.wiefferink.areashop.events.notify.UnrentedRegionEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import java.util.List;
public class CommandsFeature extends RegionFeature {
/**
* Run command for a certain event.
* @param region Region to execute the events for
* @param event The event
* @param before The 'before' or 'after' commands
*/
public void runEventCommands(GeneralRegion region, GeneralRegion.RegionEvent event, boolean before) {
ConfigurationSection eventCommandProfileSection = region.getConfigurationSectionSetting("general.eventCommandProfile", "eventCommandProfiles");
if(eventCommandProfileSection == null) {
return;
}
List<String> commands = eventCommandProfileSection.getStringList(event.getValue() + "." + (before ? "before" : "after"));
if(commands == null || commands.isEmpty()) {
return;
}
region.runCommands(Bukkit.getConsoleSender(), commands);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void addingRegion(AddingRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.CREATED, true);
}
@EventHandler
public void addedRegion(AddedRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.CREATED, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void deletingRegion(DeletingRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.DELETED, true);
}
@EventHandler
public void deletedRegion(DeletedRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.DELETED, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void rentingRegion(RentingRegionEvent event) {
// Technically the rent can still be cancelled if the payment fails...
runEventCommands(event.getRegion(), event.isExtending() ? GeneralRegion.RegionEvent.EXTENDED : GeneralRegion.RegionEvent.RENTED, true);
}
@EventHandler
public void rentedRegion(RentedRegionEvent event) {
runEventCommands(event.getRegion(), event.hasExtended() ? GeneralRegion.RegionEvent.EXTENDED : GeneralRegion.RegionEvent.RENTED, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void buyingRegion(BuyingRegionEvent event) {
// Technically the buy can still be cancelled if the payment fails...
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.BOUGHT, true);
}
@EventHandler
public void boughtRegion(BoughtRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.BOUGHT, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void resellingRegion(ResellingRegionEvent event) {
// Technically the resell can still be cancelled if the payment fails...
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.RESELL, true);
}
@EventHandler
public void resoldRegion(ResoldRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.RESELL, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void sellingRegion(SellingRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.SOLD, true);
}
@EventHandler
public void soldRegion(SoldRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.SOLD, false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void unrentingRegion(UnrentingRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.UNRENTED, true);
}
@EventHandler
public void unrentedRegion(UnrentedRegionEvent event) {
runEventCommands(event.getRegion(), GeneralRegion.RegionEvent.UNRENTED, false);
}
}
@@ -1,15 +1,25 @@
package me.wiefferink.areashop.features;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.events.ask.*;
import me.wiefferink.areashop.events.askandnotify.AddFriendEvent;
import me.wiefferink.areashop.events.askandnotify.DeleteFriendEvent;
import me.wiefferink.areashop.events.notify.*;
import me.wiefferink.areashop.events.ask.BuyingRegionEvent;
import me.wiefferink.areashop.events.ask.RentingRegionEvent;
import me.wiefferink.areashop.events.ask.ResellingRegionEvent;
import me.wiefferink.areashop.events.ask.SellingRegionEvent;
import me.wiefferink.areashop.events.ask.UnrentingRegionEvent;
import me.wiefferink.areashop.events.askandnotify.AddedFriendEvent;
import me.wiefferink.areashop.events.askandnotify.DeletedFriendEvent;
import me.wiefferink.areashop.events.notify.AddedRegionEvent;
import me.wiefferink.areashop.events.notify.BoughtRegionEvent;
import me.wiefferink.areashop.events.notify.DeletedRegionEvent;
import me.wiefferink.areashop.events.notify.RentedRegionEvent;
import me.wiefferink.areashop.events.notify.ResoldRegionEvent;
import me.wiefferink.areashop.events.notify.SoldRegionEvent;
import me.wiefferink.areashop.events.notify.UnrentedRegionEvent;
import me.wiefferink.areashop.events.notify.UpdateRegionEvent;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class DebugFeature extends Feature implements Listener {
public class DebugFeature extends RegionFeature {
@EventHandler
public void buyingRegion(BuyingRegionEvent event) {
@@ -28,7 +38,7 @@ public class DebugFeature extends Feature implements Listener {
@EventHandler
public void soldRegion(SoldRegionEvent event) {
AreaShop.debug("SoldRegionEvent: "+Utils.toName(event.getOldBuyer())+" sold "+event.getRegion().getName());
AreaShop.debug("SoldRegionEvent: " + Utils.toName(event.getOldBuyer()) + " sold " + event.getRegion().getName());
}
@EventHandler
@@ -38,7 +48,7 @@ public class DebugFeature extends Feature implements Listener {
@EventHandler
public void resoldRegion(ResoldRegionEvent event) {
AreaShop.debug("ResoldRegionEvent: "+Utils.toName(event.getFromPlayer())+" resold "+event.getRegion().getName()+" to "+event.getRegion().getPlayerName());
AreaShop.debug("ResoldRegionEvent: " + Utils.toName(event.getFromPlayer()) + " resold " + event.getRegion().getName() + " to " + event.getRegion().getPlayerName());
}
@EventHandler
@@ -58,31 +68,31 @@ public class DebugFeature extends Feature implements Listener {
@EventHandler
public void unrentedRegion(UnrentedRegionEvent event) {
AreaShop.debug("UnrentedRegionEvent: "+Utils.toName(event.getOldRenter())+" unrented "+event.getRegion().getName());
AreaShop.debug("UnrentedRegionEvent: " + Utils.toName(event.getOldRenter()) + " unrented " + event.getRegion().getName());
}
@EventHandler
public void regionUpdate(RegionUpdateEvent event) {
//AreaShop.debug("RegionUpdateEvent: " + event.getRegion().getName() + " updated");
public void regionUpdate(UpdateRegionEvent event) {
//AreaShop.debug("UpdateRegionEvent: " + event.getRegion().getName() + " updated");
}
@EventHandler
public void addedRegion(AddedRegionEvent event) {
AreaShop.debug("AddedRegionEvent: "+event.getRegion().getName());
AreaShop.debug("AddedRegionEvent: " + event.getRegion().getName());
}
@EventHandler
public void removedRegion(RemovedRegionEvent event) {
AreaShop.debug("RemovedRegionEvent: "+event.getRegion().getName());
public void removedRegion(DeletedRegionEvent event) {
AreaShop.debug("DeletedRegionEvent: " + event.getRegion().getName());
}
@EventHandler
public void addedFriend(AddFriendEvent event) {
AreaShop.debug("AddFriendEvent: region "+event.getRegion().getName()+", "+event.getFriend().getName()+" by "+(event.getBy() == null ? "<nobody>" : event.getBy().getName()));
public void addedFriend(AddedFriendEvent event) {
AreaShop.debug("AddedFriendEvent: region " + event.getRegion().getName() + ", " + event.getFriend().getName() + " by " + (event.getBy() == null ? "<nobody>" : event.getBy().getName()));
}
@EventHandler
public void deleteFriend(DeleteFriendEvent event) {
AreaShop.debug("DeleteFriendEvent: region "+event.getRegion().getName()+", "+event.getFriend().getName()+" by "+(event.getBy() == null ? "<nobody>" : event.getBy().getName()));
public void deleteFriend(DeletedFriendEvent event) {
AreaShop.debug("DeletedFriendEvent: region " + event.getRegion().getName() + ", " + event.getFriend().getName() + " by " + (event.getBy() == null ? "<nobody>" : event.getBy().getName()));
}
}
@@ -1,7 +0,0 @@
package me.wiefferink.areashop.features;
import me.wiefferink.areashop.AreaShop;
public class Feature {
AreaShop plugin = AreaShop.getInstance();
}
@@ -1,77 +1,78 @@
package me.wiefferink.areashop.features;
import me.wiefferink.areashop.events.askandnotify.AddFriendEvent;
import me.wiefferink.areashop.events.askandnotify.DeleteFriendEvent;
import me.wiefferink.areashop.events.askandnotify.AddedFriendEvent;
import me.wiefferink.areashop.events.askandnotify.DeletedFriendEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class FriendsFeature extends Feature {
private GeneralRegion region;
public class FriendsFeature extends RegionFeature {
public FriendsFeature(GeneralRegion region) {
this.region = region;
setRegion(region);
}
/**
* Add a friend to the region
* Add a friend to the region.
* @param player The UUID of the player to add
* @param by The CommandSender that is adding the friend, or null
* @return true if the friend has been added, false if adding a friend was cancelled by another plugin
*/
public boolean addFriend(UUID player, CommandSender by) {
// Fire and check event
AddFriendEvent event = new AddFriendEvent(region, Bukkit.getOfflinePlayer(player), by);
AddedFriendEvent event = new AddedFriendEvent(getRegion(), Bukkit.getOfflinePlayer(player), by);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
plugin.message(by, "general-cancelled", event.getReason(), this);
return false;
}
Set<String> friends = new HashSet<>(region.getConfig().getStringList("general.friends"));
Set<String> friends = new HashSet<>(getRegion().getConfig().getStringList("general.friends"));
friends.add(player.toString());
List<String> list = new ArrayList<>(friends);
region.setSetting("general.friends", list);
getRegion().setSetting("general.friends", list);
return true;
}
/**
* Delete a friend from the region
* Delete a friend from the region.
* @param player The UUID of the player to delete
* @param by The CommandSender that is adding the friend, or null
* @return true if the friend has been added, false if adding a friend was cancelled by another plugin
*/
public boolean deleteFriend(UUID player, CommandSender by) {
// Fire and check event
DeleteFriendEvent event = new DeleteFriendEvent(region, Bukkit.getOfflinePlayer(player), by);
DeletedFriendEvent event = new DeletedFriendEvent(getRegion(), Bukkit.getOfflinePlayer(player), by);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
plugin.message(by, "general-cancelled", event.getReason(), this);
return false;
}
Set<String> friends = new HashSet<>(region.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()) {
region.setSetting("general.friends", null);
if(friends.isEmpty()) {
getRegion().setSetting("general.friends", null);
} else {
region.setSetting("general.friends", list);
getRegion().setSetting("general.friends", friends);
}
return true;
}
/**
* Get the list of friends added to this region
* Get the list of friends added to this region.
* @return Friends added to this region
*/
public Set<UUID> getFriends() {
HashSet<UUID> result = new HashSet<>();
for(String friend : region.getConfig().getStringList("general.friends")) {
for(String friend : getRegion().getConfig().getStringList("general.friends")) {
try {
UUID id = UUID.fromString(friend);
result.add(id);
@@ -83,14 +84,14 @@ public class FriendsFeature extends Feature {
}
/**
* Get the list of friends added to this region
* Get the list of friends added to this region.
* @return Friends added to this region
*/
public Set<String> getFriendNames() {
HashSet<String> result = new HashSet<>();
for(UUID friend : getFriends()) {
OfflinePlayer player = Bukkit.getOfflinePlayer(friend);
if(player != null) {
if(player != null && player.getName() != null) {
result.add(player.getName());
}
}
@@ -98,10 +99,10 @@ public class FriendsFeature extends Feature {
}
/**
* Remove all friends that are added to this region
* Remove all friends that are added to this region.
*/
public void clearFriends() {
region.setSetting("general.friends", null);
getRegion().setSetting("general.friends", null);
}
}
@@ -0,0 +1,6 @@
# Features
This package contains classes that represent a certain feature and implement it for all regions.
* `RegionFeature` represents a single feature, for each implementing class one instance is created without an assigned region, to listen for events etc, and an instance per region is created on-demand to handle region specific things.
* More features will be moved from `GeneralRegion` to these kind of classes, to keep all code for a feature together and cleanup `GeneralRegion`.
@@ -0,0 +1,73 @@
package me.wiefferink.areashop.features;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
/*
Possible future feature classes:
- RestrictedToRegion feature
- rent/buy restricted to the region itself
- RestrictedToWorld feature
- rent/buy restricted to the world of the region
- CountLimitsFeature:
- Check region limits
- TimeLimitsFeatures:
- maxExtends
- maxRentTime
- SchematicFeature
- save/load schematics
- CommandsFeature
- execute commands after extend/rent/etc
- LandlordFeature
- manage landlords
- ExpirationWarningsFeature
- sending rental expiration warnings
*/
public abstract class RegionFeature implements Listener {
public static final AreaShop plugin = AreaShop.getInstance();
protected YamlConfiguration config = plugin.getConfig();
private GeneralRegion region;
/**
* Set the region for this feature.
* @param region Feature region
*/
public void setRegion(GeneralRegion region) {
this.region = region;
}
/**
* Get the region of this feature.
* @return region of this feature, or null if generic
*/
public GeneralRegion getRegion() {
return region;
}
/**
* Start listening to events.
*/
public void listen() {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
/**
* Destroy the feature and deregister everything.
*/
public void shutdownFeature() {
HandlerList.unregisterAll(this);
shutdown();
}
/**
* Dummy method a RegionFeature implementation can override.
*/
public void shutdown() {
}
}
@@ -1,346 +0,0 @@
package me.wiefferink.areashop.features;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.events.notify.RegionUpdateEvent;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Sign;
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.Listener;
import java.util.*;
public class SignsFeature extends Feature implements Listener {
private static Map<String, RegionSign> allSigns = new HashMap<>();
private GeneralRegion region;
private Map<String, RegionSign> signs;
public SignsFeature() {
}
public SignsFeature(GeneralRegion region) {
this.region = region;
signs = new HashMap<>();
// Setup current signs
ConfigurationSection signSection = region.getConfig().getConfigurationSection("general.signs");
if(signSection != null) {
for(String signKey : signSection.getKeys(false)) {
RegionSign sign = new RegionSign(region, this, signKey);
Location location = sign.getLocation();
if(location == null) {
plugin.getLogger().warning("Sign with key "+signKey+" of region "+region.getName()+" does not have a proper location");
continue;
}
signs.put(sign.getStringLocation(), sign);
}
allSigns.putAll(signs);
}
}
/**
* 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();
}
/**
* 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 static RegionSign getSignByLocation(Location location) {
return allSigns.get(locationToString(location));
}
@EventHandler
public void regionUpdate(RegionUpdateEvent event) {
event.getRegion().getSignsFeature().update();
}
/**
* Update all signs connected to this region
* @return true if all signs are updated correctly, false if one or more updates failed
*/
public boolean update() {
boolean result = true;
for(RegionSign sign : signs.values()) {
result = result&sign.update();
}
return result;
}
/**
* Check if any of the signs need periodic updating
* @return true if one or more of the signs need periodic updating, otherwise false
*/
public boolean needsPeriodicUpdate() {
boolean result = false;
for(RegionSign sign : signs.values()) {
result = result|sign.needsPeriodicUpdate();
}
return result;
}
/**
* Get a list with all sign locations
* @return A List with all sign locations
*/
public List<Location> getSignLocations() {
List<Location> result = new ArrayList<>();
for(RegionSign sign : signs.values()) {
result.add(sign.getLocation());
}
return result;
}
/**
* Add a sign to this region
* @param location The location of the sign
* @param signType The type of the sign (WALL_SIGN or SIGN_POST)
* @param facing The orientation of the sign
* @param profile The profile to use with this sign (null for default)
*/
public void addSign(Location location, Material signType, BlockFace facing, String profile) {
int i = 0;
while(region.getConfig().isSet("general.signs."+i)) {
i++;
}
String signPath = "general.signs."+i+".";
region.setSetting(signPath+"location", Utils.locationToConfig(location));
region.setSetting(signPath+"facing", facing.name());
region.setSetting(signPath+"signType", signType.name());
if(profile != null && profile.length() != 0) {
region.setSetting(signPath+"profile", profile);
}
// Add to the map
RegionSign sign = new RegionSign(region, this, i+"");
signs.put(sign.getStringLocation(), sign);
allSigns.put(sign.getStringLocation(), sign);
}
/**
* Checks if there is a sign from this region at the specified location
* @param location Location to check
* @return true if this region has a sign at the location, otherwise false
*/
public boolean isSignOfRegion(Location location) {
Set<String> signs;
if(region.getConfig().getConfigurationSection("general.signs") == null) {
return false;
}
signs = region.getConfig().getConfigurationSection("general.signs").getKeys(false);
for(String sign : signs) {
Location signLocation = Utils.configToLocation(region.getConfig().getConfigurationSection("general.signs."+sign+".location"));
if(signLocation != null
&& signLocation.getWorld().equals(location.getWorld())
&& signLocation.getBlockX() == location.getBlockX()
&& signLocation.getBlockY() == location.getBlockY()
&& signLocation.getBlockZ() == location.getBlockZ()) {
return true;
}
}
return false;
}
/**
* Sign that is connected to a region to display information and interact with the region
*/
public class RegionSign {
private GeneralRegion region;
private SignsFeature signsFeature;
private String key;
public RegionSign(GeneralRegion region, SignsFeature signsFeature, String key) {
this.region = region;
this.signsFeature = signsFeature;
this.key = key;
}
/**
* Get the location of this sign
* @return The location of this sign
*/
public Location getLocation() {
return Utils.configToLocation(region.getConfig().getConfigurationSection("general.signs."+key+".location"));
}
/**
* Location string to be used as key in maps
* @return Location string
*/
public String getStringLocation() {
return locationToString(getLocation());
}
/**
* Get the region this sign is linked to
* @return The region this sign is linked to
*/
public GeneralRegion getRegion() {
return region;
}
/**
* Remove this sign from the region
*/
public void remove() {
getLocation().getBlock().setType(Material.AIR);
signs.remove(getStringLocation());
allSigns.remove(getStringLocation());
region.setSetting("general.signs."+key, null);
}
public String getProfile() {
String profile = region.getConfig().getString("general.signs."+key+".profile");
if(profile == null || profile.length() == 0) {
profile = region.getStringSetting("general.signProfile");
}
return profile;
}
/**
* Update this sign
* @return true if the update was successful, otherwise false
*/
public boolean update() {
if(region.isDeleted()) {
return false;
}
YamlConfiguration config = region.getConfig();
// Get the prefix
String prefix = "signProfiles."+getProfile()+"."+region.getState().getValue().toLowerCase()+".";
// Get the lines
String[] signLines = new String[4];
signLines[0] = plugin.getConfig().getString(prefix+"line1");
signLines[1] = plugin.getConfig().getString(prefix+"line2");
signLines[2] = plugin.getConfig().getString(prefix+"line3");
signLines[3] = plugin.getConfig().getString(prefix+"line4");
// Check if the sign should be present
Block block = getLocation().getBlock();
if(!plugin.getConfig().isSet(prefix)
|| ((signLines[0] == null || signLines[0].length() == 0)
&& (signLines[1] == null || signLines[1].length() == 0)
&& (signLines[2] == null || signLines[2].length() == 0)
&& (signLines[3] == null || signLines[3].length() == 0))) {
block.setType(Material.AIR);
} else {
Sign signState = null;
if(block.getType() != Material.WALL_SIGN && block.getType() != Material.SIGN_POST) {
Material signType;
try {
signType = Material.valueOf(config.getString("general.signs."+key+".signType"));
} catch(NullPointerException|IllegalArgumentException e) {
signType = null;
}
if(signType != Material.WALL_SIGN && signType != Material.SIGN_POST) {
AreaShop.debug(" setting sign failed");
return false;
}
block.setType(signType);
signState = (Sign)block.getState();
org.bukkit.material.Sign signData = (org.bukkit.material.Sign)signState.getData();
BlockFace signFace;
try {
signFace = BlockFace.valueOf(config.getString("general.signs."+key+".facing"));
} catch(NullPointerException|IllegalArgumentException e) {
signFace = null;
}
if(signFace != null) {
signData.setFacingDirection(signFace);
signState.setData(signData);
}
}
if(signState == null) {
signState = (Sign)block.getState();
}
org.bukkit.material.Sign signData = (org.bukkit.material.Sign)signState.getData();
if(!config.isString("general.signs."+key+".signType")) {
region.setSetting("general.signs."+key+".signType", signState.getType().toString());
}
if(!config.isString("general.signs."+key+".facing")) {
region.setSetting("general.signs."+key+".facing", signData.getFacing().toString());
}
// Apply replacements and color and then set it on the sign
for(int i = 0; i < signLines.length; i++) {
if(signLines[i] == null) {
signState.setLine(i, "");
continue;
}
signLines[i] = region.applyAllReplacements(signLines[i]);
signLines[i] = Utils.applyColors(signLines[i]);
signState.setLine(i, signLines[i]);
}
signState.update();
}
return true;
}
/**
* Check if the sign needs to update periodically
* @return true if it needs periodic updates, otherwise false
*/
public boolean needsPeriodicUpdate() {
// Get the prefix
String prefix = "signProfiles."+getProfile()+"."+region.getState().getValue().toLowerCase()+".line";
String line;
// Get the lines
for(int i = 1; i < 5; i++) {
line = plugin.getConfig().getString(prefix+i);
if(line != null && line.length() != 0 && line.contains(AreaShop.tagTimeLeft)) {
return true;
}
}
return false;
}
/**
* Run commands when a player clicks a sign
* @param clicker The player that clicked the sign
* @param clickType The type of clicking
* @return true if the commands ran successfully, false if any of them failed
*/
public boolean runSignCommands(Player clicker, GeneralRegion.ClickType clickType) {
// Get the profile set in the config
String profile = region.getConfig().getString("general.signs."+key+".profile");
if(profile == null || profile.length() == 0) {
profile = region.getStringSetting("general.signProfile");
}
// Get paths (state may change after running them)
String playerPath = "signProfiles."+profile+"."+region.getState().getValue().toLowerCase()+"."+clickType.getValue()+"Player";
String consolePath = "signProfiles."+profile+"."+region.getState().getValue().toLowerCase()+"."+clickType.getValue()+"Console";
// Run player commands if specified
List<String> playerCommands = new ArrayList<>();
for(String command : plugin.getConfig().getStringList(playerPath)) {
playerCommands.add(command.replace(AreaShop.tagClicker, clicker.getName()));
}
region.runCommands(clicker, playerCommands);
// Run console commands if specified
List<String> consoleCommands = new ArrayList<>();
for(String command : plugin.getConfig().getStringList(consolePath)) {
consoleCommands.add(command.replace(AreaShop.tagClicker, clicker.getName()));
}
region.runCommands(Bukkit.getConsoleSender(), consoleCommands);
return !playerCommands.isEmpty() || !consoleCommands.isEmpty();
}
}
}
@@ -0,0 +1,602 @@
package me.wiefferink.areashop.features;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import io.papermc.lib.PaperLib;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.handlers.worldguard.v7.WorldGuardHandler;
import me.wiefferink.areashop.interfaces.WorldGuardInterface;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.areashop.tools.Value;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
public class TeleportFeature extends RegionFeature {
public TeleportFeature() {
}
public TeleportFeature(GeneralRegion region) {
setRegion(region);
}
/**
* Checks if a certain location is safe to teleport to.
*
* @param location The location to check
* @return true if it is safe, otherwise false
*/
private static boolean isSafe(Location location) {
Block feet = location.getBlock();
Block head = feet.getRelative(BlockFace.UP);
Block below = feet.getRelative(BlockFace.DOWN);
Block above = head.getRelative(BlockFace.UP);
// Check the block at the feet and head of the player
if ((feet.getType().isSolid() && !canSpawnIn(feet.getType())) || feet.isLiquid()) {
return false;
} else if ((head.getType().isSolid() && !canSpawnIn(head.getType())) || head.isLiquid()) {
return false;
} else if (!below.getType().isSolid() || cannotSpawnOn(below.getType()) || below.isLiquid()) {
return false;
} else if (above.isLiquid() || cannotSpawnBeside(above.getType())) {
return false;
}
// Get all blocks around the player (below foot level, foot level, head level and above head level)
Set<Material> around = new HashSet<>();
for (int y = 0; y <= 3; y++) {
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
// Skip blocks in the column of the player
if (x == 0 && z == 0) {
continue;
}
around.add(below.getRelative(x, y, z).getType());
}
}
}
// Check the blocks around the player
for (Material material : around) {
if (cannotSpawnBeside(material)) {
return false;
}
}
return true;
}
/**
* Check if a player can spawn in here.
*
* @param material Material to check (assumed that this is at the feet or head level)
* @return true when it is safe to spawn inside, otherwise false
*/
private static boolean canSpawnIn(Material material) {
String name = material.name();
return name.contains("DOOR")
|| name.contains("SIGN")
|| name.contains("PLATE") // Redstone plates
|| name.equals("DRAGON_EGG");
}
/**
* Check if a player can spawn on here.
*
* @param material Material to check (assumed that this is below the feet)
* @return true when it is safe to spawn on top of, otherwise false
*/
private static boolean cannotSpawnOn(Material material) {
String name = material.name();
return name.equals("CACTUS")
|| name.contains("PISTON")
|| name.contains("SIGN")
|| name.contains("DOOR")
|| name.contains("PLATE")
|| name.contains("REDSTONE_LAMP")
|| name.contains("FENCE")
|| name.contains("GLASS_PANE") || name.contains("THIN_GLASS")
|| name.equals("DRAGON_EGG")
|| name.contains("MAGMA");
}
/**
* Check if a player can spawn next to it.
*
* @param material Material to check (assumed that this is somewhere around the player)
* @return true when it is safe to spawn next to, otherwise false
*/
private static boolean cannotSpawnBeside(Material material) {
String name = material.name();
return name.contains("LAVA")
|| name.contains("CACTUS")
|| name.equals("FIRE")
|| name.contains("MAGMA");
}
/**
* Get the teleportlocation set for this region.
*
* @return The teleport location, or null if not set
*/
public Location getTeleportLocation() {
return Utils.configToLocation(getRegion().getConfigurationSectionSetting("general.teleportLocation"));
}
/**
* Check if the region has a teleportLocation specified.
*
* @return true if the region has a teleportlocation, false otherwise
*/
public boolean hasTeleportLocation() {
return getRegion().getConfigurationSectionSetting("general.teleportLocation") != null;
}
/**
* Set the teleport location of this region.
*
* @param location The location to set as teleport location
*/
public void setTeleport(Location location) {
if (location == null) {
getRegion().setSetting("general.teleportLocation", null);
} else {
getRegion().setSetting("general.teleportLocation", Utils.locationToConfig(location, true));
}
}
/**
* Teleport a player to the region or sign.
*
* @param player Player that should be teleported
* @param toSign true to teleport to the first sign of the region, false for teleporting to the region itself
* @param checkRestrictions Set to true if teleport permissions should be checked, false otherwise, also toggles cross-world check
* @return true if the teleport succeeded, otherwise false
*/
public CompletableFuture<Boolean> teleportPlayer(Player player, boolean toSign, boolean checkRestrictions) {
// Check basics
if (getRegion().getWorld() == null) {
getRegion().message(player, "general-noWorld");
return CompletableFuture.completedFuture(false);
}
if (getRegion().getRegion() == null) {
getRegion().message(player, "general-noRegion");
return CompletableFuture.completedFuture(false);
}
if (checkRestrictions) {
// Check correct world
if (!getRegion().getBooleanSetting("general.teleportCrossWorld") && !player.getWorld().equals(getRegion().getWorld())) {
getRegion().message(player, "teleport-wrongWorld", player.getWorld().getName());
return CompletableFuture.completedFuture(false);
}
boolean owner = player.getUniqueId().equals(getRegion().getOwner());
boolean friend = getRegion().getFriendsFeature().getFriends().contains(player.getUniqueId());
boolean available = getRegion().isAvailable();
// Teleport to sign instead if they dont have permission for teleporting to region
if ((!toSign && owner && !player.hasPermission("areashop.teleport") && player.hasPermission("areashop.teleportsign")
|| !toSign && !owner && !friend && !player.hasPermission("areashop.teleportall") && player.hasPermission("areashop.teleportsignall")
|| !toSign && !owner && friend && !player.hasPermission("areashop.teleportfriend") && player.hasPermission("areashop.teleportfriendsign")
|| !toSign && !owner && !friend && available && !player.hasPermission("areashop.teleportavailable") && player.hasPermission("areashop.teleportavailablesign"))) {
getRegion().message(player, "teleport-changedToSign");
toSign = true;
}
// Check permissions
if (owner && !available && !player.hasPermission("areashop.teleport") && !toSign) {
getRegion().message(player, "teleport-noPermission");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportall") && !toSign && !friend) {
getRegion().message(player, "teleport-noPermissionOther");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportfriend") && !toSign && friend) {
getRegion().message(player, "teleport-noPermissionFriend");
return CompletableFuture.completedFuture(false);
} else if (available && !player.hasPermission("areashop.teleportavailable") && !toSign) {
getRegion().message(player, "teleport-noPermissionAvailable");
return CompletableFuture.completedFuture(false);
} else if (owner && !available && !player.hasPermission("areashop.teleportsign") && toSign) {
getRegion().message(player, "teleport-noPermissionSign");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportsignall") && toSign && !friend) {
getRegion().message(player, "teleport-noPermissionOtherSign");
return CompletableFuture.completedFuture(false);
} else if (!owner && !available && !player.hasPermission("areashop.teleportfriendsign") && toSign && friend) {
getRegion().message(player, "teleport-noPermissionFriendSign");
return CompletableFuture.completedFuture(false);
} else if (available && !player.hasPermission("areashop.teleportavailablesign") && toSign) {
getRegion().message(player, "teleport-noPermissionAvailableSign");
return CompletableFuture.completedFuture(false);
}
}
// Get the starting location
Value<Boolean> toSignRef = new Value<>(toSign);
Location startLocation = getStartLocation(player, toSignRef);
toSign = toSignRef.get();
boolean insideRegion;
if (toSign) {
insideRegion = getRegion().getBooleanSetting("general.teleportToSignIntoRegion");
} else {
insideRegion = getRegion().getBooleanSetting("general.teleportIntoRegion");
}
// Check locations starting from startLocation and then a cube that increases
// radius around that (until no block in the region is found at all cube sides)
ProtectedRegion worldguardRegion = getRegion().getRegion();
boolean blocksInRegion = worldguardRegion.contains(startLocation.getBlockX(), startLocation.getBlockY(), startLocation.getBlockZ());
if (!blocksInRegion && insideRegion) {
getRegion().message(player, "teleport-blocked");
return CompletableFuture.completedFuture(false);
}
// Tracking of which sides to continue the search
final World world = getRegion().getWorld();
BlockVector3 min = worldguardRegion.getMinimumPoint();
BlockVector3 max = worldguardRegion.getMaximumPoint();
for (int x = min.getX(); x < max.getX(); x += 16) {
for (int z = min.getZ(); z < max.getZ(); z += 16) {
PaperLib.getChunkAtAsync(world, x >> 4, z >> 4);
}
}
CompletableFuture<Boolean> future = new CompletableFuture<>();
final boolean finalToSign = toSign;
Bukkit.getScheduler().runTaskLater(plugin, () -> {
boolean blocksInRegionCopy = blocksInRegion;
Location safeLocation = startLocation;
// Tries limit tracking
int radius = 1;
int checked = 1;
int maxTries = plugin.getConfig().getInt("maximumTries");
// Tracking of which sides to continue the search
boolean done = isSafe(safeLocation);
boolean northDone = false, eastDone = false, southDone = false, westDone = false, topDone = false, bottomDone = false;
boolean continueThisDirection;
while ((blocksInRegionCopy || !insideRegion) && !done) {
blocksInRegionCopy = false;
// North side
continueThisDirection = false;
for (int x = -radius + 1; x <= radius && !done && !northDone; x++) {
for (int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(x, y, -radius);
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
northDone = northDone || !continueThisDirection;
// East side
continueThisDirection = false;
for (int z = -radius + 1; z <= radius && !done && !eastDone; z++) {
for (int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(radius, y, z);
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
eastDone = eastDone || !continueThisDirection;
// South side
continueThisDirection = false;
for (int x = radius - 1; x >= -radius && !done && !southDone; x--) {
for (int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(x, y, radius);
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
southDone = southDone || !continueThisDirection;
// West side
continueThisDirection = false;
for (int z = radius - 1; z >= -radius && !done && !westDone; z--) {
for (int y = -radius + 1; y < radius && !done; y++) {
safeLocation = startLocation.clone().add(-radius, y, z);
if (safeLocation.getBlockY() > 256 || safeLocation.getBlockY() < 0) {
continue;
}
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
westDone = westDone || !continueThisDirection;
// Top side
continueThisDirection = false;
// Middle block of the top
if ((startLocation.getBlockY() + radius) > 256) {
topDone = true;
}
if (!done && !topDone) {
safeLocation = startLocation.clone().add(0, radius, 0);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
for (int r = 1; r <= radius && !done && !topDone; r++) {
// North
for (int x = -r + 1; x <= r && !done; x++) {
safeLocation = startLocation.clone().add(x, radius, -r);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// East
for (int z = -r + 1; z <= r && !done; z++) {
safeLocation = startLocation.clone().add(r, radius, z);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// South side
for (int x = r - 1; x >= -r && !done; x--) {
safeLocation = startLocation.clone().add(x, radius, r);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// West side
for (int z = r - 1; z >= -r && !done; z--) {
safeLocation = startLocation.clone().add(-r, radius, z);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
topDone = topDone || !continueThisDirection;
// Bottom side
continueThisDirection = false;
// Middle block of the bottom
if (startLocation.getBlockY() - radius < 0) {
bottomDone = true;
}
if (!done && !bottomDone) {
safeLocation = startLocation.clone().add(0, -radius, 0);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
for (int r = 1; r <= radius && !done && !bottomDone; r++) {
// North
for (int x = -r + 1; x <= r && !done; x++) {
safeLocation = startLocation.clone().add(x, -radius, -r);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// East
for (int z = -r + 1; z <= r && !done; z++) {
safeLocation = startLocation.clone().add(r, -radius, z);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// South side
for (int x = r - 1; x >= -r && !done; x--) {
safeLocation = startLocation.clone().add(x, -radius, r);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
// West side
for (int z = r - 1; z >= -r && !done; z--) {
safeLocation = startLocation.clone().add(-r, -radius, z);
if (worldguardRegion.contains(safeLocation.getBlockX(), safeLocation.getBlockY(), safeLocation.getBlockZ()) || !insideRegion) {
checked++;
done = isSafe(safeLocation) || checked > maxTries;
blocksInRegionCopy = true;
continueThisDirection = true;
}
}
}
bottomDone = bottomDone || !continueThisDirection;
// Increase cube radius
radius++;
}
if (done && isSafe(safeLocation)) {
final SignsFeature feature = getRegion().getSignsFeature();
Collection<RegionSign> signs = feature.getSigns();
if (finalToSign) {
getRegion().message(player, "teleport-successSign");
// Let the player look at the sign
Vector playerVector = safeLocation.toVector();
playerVector.setY(playerVector.getY() + player.getEyeHeight(true));
final RegionSign first = signs.iterator().next();
Vector signVector = first.getLocation().add(0.5, 0.5, 0.5).toVector();
Vector direction = playerVector.clone().subtract(signVector).normalize();
safeLocation.setYaw(180 - (float) Math.toDegrees(Math.atan2(direction.getX(), direction.getZ())));
safeLocation.setPitch(90 - (float) Math.toDegrees(Math.acos(direction.getY())));
} else {
getRegion().message(player, "teleport-success");
}
AreaShop.debug("Found location: " + safeLocation.toString() + " Tries: " + checked);
PaperLib.teleportAsync(player, safeLocation).thenAccept(future::complete);
} else {
getRegion().message(player, "teleport-noSafe", checked, maxTries);
AreaShop.debug("No location found, checked " + checked + " spots of max " + maxTries);
future.complete(false);
}
}, 1);
return future;
}
/**
* Teleport a player to the region or sign when he has permissions for it.
*
* @param player Player that should be teleported
* @param toSign true to teleport to the first sign of the region, false for teleporting to the region itself
* @return true if the teleport succeeded, otherwise false
*/
public CompletableFuture<Boolean> teleportPlayer(Player player, boolean toSign) {
return teleportPlayer(player, toSign, true);
}
/**
* Teleport a player to the region when he has permissions for it.
*
* @param player Player that should be teleported
* @return true if the teleport succeeded, otherwise false
*/
public CompletableFuture<Boolean> teleportPlayer(Player player) {
return teleportPlayer(player, false, true);
}
/**
* Get the start location of a safe teleport search.
*
* @param player The player to get it for
* @param toSign true to try teleporting to the first sign, false for teleporting to the region
* @return The start location
*/
private Location getStartLocation(Player player, Value<Boolean> toSign) {
Location startLocation = null;
ProtectedRegion worldguardRegion = getRegion().getRegion();
// Try to get sign location
Collection<RegionSign> signs = getRegion().getSignsFeature().getSigns();
boolean signAvailable = !signs.isEmpty();
if (toSign.get()) {
RegionSign sign = signs.iterator().next();
if (signAvailable) {
// Use the location 1 below the sign to prevent weird spawing above the sign
startLocation = sign.getLocation(); //.subtract(0.0, 1.0, 0.0);
startLocation.setPitch(player.getLocation().getPitch());
startLocation.setYaw(player.getLocation().getYaw());
// Move player x blocks away from the sign
double distance = getRegion().getDoubleSetting("general.teleportSignDistance");
if (distance > 0) {
BlockFace facing = sign.getFacing();
Vector facingVector = new Vector(facing.getModX(), facing.getModY(), facing.getModZ())
.normalize()
.multiply(distance);
startLocation.setX(startLocation.getBlockX() + 0.5);
startLocation.setZ(startLocation.getBlockZ() + 0.5);
startLocation.add(facingVector);
}
} else {
// No sign available
getRegion().message(player, "teleport-changedToNoSign");
toSign.set(false);
}
}
// Use teleportation location that is set for the region
if (startLocation == null && hasTeleportLocation()) {
startLocation = getTeleportLocation();
}
// Calculate a default location
if (startLocation == null) {
final WorldGuardInterface handler = AreaShop.getInstance().getWorldGuardHandler();
// Set to block in the middle, y configured in the config
Vector regionMin = handler.getMinimumPoint(worldguardRegion);
Vector regionMax = handler.getMaximumPoint(worldguardRegion);
Vector middle = regionMin.clone().midpoint(regionMax);
String configSetting = getRegion().getStringSetting("general.teleportLocationY");
if ("bottom".equalsIgnoreCase(configSetting)) {
middle = middle.setY(regionMin.getBlockY());
} else if ("top".equalsIgnoreCase(configSetting)) {
middle = middle.setY(regionMax.getBlockY());
} else if ("middle".equalsIgnoreCase(configSetting)) {
middle = middle.setY(middle.getBlockY());
} else {
try {
int vertical = Integer.parseInt(configSetting);
middle = middle.setY(vertical);
} catch (NumberFormatException e) {
AreaShop.warn("Could not parse general.teleportLocationY: '" + configSetting + "'");
}
}
final Location location = player.getLocation();
startLocation = new Location(getRegion().getWorld(), middle.getX(), middle.getY(), middle.getZ(), location.getYaw(), location.getPitch());
}
// Set location in the center of the block
startLocation.setX(startLocation.getBlockX() + 0.5);
startLocation.setZ(startLocation.getBlockZ() + 0.5);
return startLocation;
}
}
@@ -6,61 +6,74 @@ import com.sk89q.worldguard.protection.flags.InvalidFlagFormat;
import com.sk89q.worldguard.protection.flags.RegionGroupFlag;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.events.notify.RegionUpdateEvent;
import me.wiefferink.areashop.messages.Message;
import me.wiefferink.areashop.events.notify.UpdateRegionEvent;
import me.wiefferink.areashop.interfaces.RegionAccessSet;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public class WorldGuardRegionFlagsFeature extends Feature implements Listener {
public class WorldGuardRegionFlagsFeature extends RegionFeature {
@EventHandler
public void regionUpdate(RegionUpdateEvent event) {
public void regionUpdate(UpdateRegionEvent event) {
updateRegionFlags(event.getRegion());
}
/**
* Set the region flags/options to the values of a ConfigurationSection
* Set the region flags/options to the values of a ConfigurationSection.
* @param region The region to update the flags for
* @return true if the flags have been set correctly, otherwise false
*/
protected boolean updateRegionFlags(GeneralRegion region) {
private boolean updateRegionFlags(GeneralRegion region) {
boolean result = true;
// General region flags
String allPath = "flagProfiles."+region.getStringSetting("general.flagProfile")+".ALL";
ConfigurationSection generalFlags = plugin.getConfig().getConfigurationSection(allPath);
if(plugin.getConfig().isSet(allPath) && generalFlags != null) { // Explicitely check if it is set, so don't apply if only in the default config
result = updateRegionFlags(region, generalFlags);
// Get section defining the region flag profile
ConfigurationSection flagProfileSection = region.getConfigurationSectionSetting("general.flagProfile", "flagProfiles");
if(flagProfileSection == null) {
return false;
}
// Specific region flags
String specificPath = "flagProfiles."+region.getStringSetting("general.flagProfile")+"."+region.getState().getValue();
if(plugin.getConfig().isSet(specificPath)) { // Do no apply default flags if they are removed from the active config
ConfigurationSection specificFlags = plugin.getConfig().getConfigurationSection(specificPath);
result = result && specificFlags != null && updateRegionFlags(region, specificFlags);
// Region flags for all states
ConfigurationSection allFlags = flagProfileSection.getConfigurationSection("ALL");
if(allFlags != null) {
result = updateRegionFlags(region, allFlags);
}
// Region flags for the current state
ConfigurationSection stateFlags = flagProfileSection.getConfigurationSection(region.getState().getValue());
// If in reselling mode, fallback to 'resale' section if 'resell' is not found (legacy configuration problem: https://github.com/NLthijs48/AreaShop/issues/303)
if(stateFlags == null && region.getState() == GeneralRegion.RegionState.RESELL) {
stateFlags = flagProfileSection.getConfigurationSection("resale");
}
if(stateFlags != null) {
result &= updateRegionFlags(region, stateFlags);
}
return result;
}
/**
* Set the region flags/options to the values of a ConfigurationSection
* Set the region flags/options to the values of a ConfigurationSection.
* @param region The region to update the flags for
* @param flags The flags to apply
* @param flags The flags to apply
* @return true if the flags have been set correctly, otherwise false
*/
protected boolean updateRegionFlags(GeneralRegion region, ConfigurationSection flags) {
private boolean updateRegionFlags(GeneralRegion region, ConfigurationSection flags) {
boolean result = true;
Set<String> flagNames = flags.getKeys(false);
WorldGuardPlugin worldGuard = plugin.getWorldGuard();
// Get the region
ProtectedRegion wRegion = region.getRegion();
if(wRegion == null) {
ProtectedRegion worldguardRegion = region.getRegion();
if(worldguardRegion == null) {
AreaShop.debug("Region '" + region.getName() + "' does not exist, setting flags failed");
return false;
}
@@ -72,34 +85,38 @@ public class WorldGuardRegionFlagsFeature extends Feature implements Listener {
value = translateBukkitToWorldGuardColors(value);
}
if(flagName.equalsIgnoreCase("members")) {
plugin.getWorldGuardHandler().setMembers(wRegion, value);
plugin.getWorldGuardHandler().setMembers(worldguardRegion, parseAccessSet(value));
//AreaShop.debug(" Flag " + flagName + " set: " + members.toUserFriendlyString());
} else if(flagName.equalsIgnoreCase("owners")) {
plugin.getWorldGuardHandler().setOwners(wRegion, value);
plugin.getWorldGuardHandler().setOwners(worldguardRegion, parseAccessSet(value));
//AreaShop.debug(" Flag " + flagName + " set: " + owners.toUserFriendlyString());
} else if(flagName.equalsIgnoreCase("priority")) {
try {
int priority = Integer.parseInt(value);
wRegion.setPriority(priority);
if(worldguardRegion.getPriority() != priority) {
worldguardRegion.setPriority(priority);
}
//AreaShop.debug(" Flag " + flagName + " set: " + value);
} catch(NumberFormatException e) {
plugin.getLogger().warning("The value of flag " + flagName + " is not a number");
AreaShop.warn("The value of flag " + flagName + " is not a number");
result = false;
}
} else if(flagName.equalsIgnoreCase("parent")) {
if(region.getWorld() == null || worldGuard.getRegionManager(region.getWorld()) == null) {
if(region.getWorld() == null || plugin.getRegionManager(region.getWorld()) == null) {
continue;
}
ProtectedRegion parentRegion = worldGuard.getRegionManager(region.getWorld()).getRegion(value);
ProtectedRegion parentRegion = plugin.getRegionManager(region.getWorld()).getRegion(value);
if(parentRegion != null) {
try {
wRegion.setParent(parentRegion);
//AreaShop.debug(" Flag " + flagName + " set: " + value);
} catch(ProtectedRegion.CircularInheritanceException e) {
plugin.getLogger().warning("The parent set in the config is not correct (circular inheritance)");
if(!parentRegion.equals(worldguardRegion.getParent())) {
try {
worldguardRegion.setParent(parentRegion);
//AreaShop.debug(" Flag " + flagName + " set: " + value);
} catch(ProtectedRegion.CircularInheritanceException e) {
AreaShop.warn("The parent set in the config is not correct (circular inheritance)");
}
}
} else {
plugin.getLogger().warning("The parent set in the config is not correct (region does not exist)");
AreaShop.warn("The parent set in the config is not correct (region does not exist)");
}
} else {
// Parse all other normal flags (groups are also handled)
@@ -108,14 +125,16 @@ public class WorldGuardRegionFlagsFeature extends Feature implements Listener {
Flag<?> foundFlag = plugin.getWorldGuardHandler().fuzzyMatchFlag(flagName);
if(foundFlag == null) {
plugin.getLogger().warning("Found wrong flag in flagProfiles section: " + flagName + ", check if that is the correct WorldGuard flag");
AreaShop.warn("Found wrong flag in flagProfiles section: " + flagName + ", check if that is the correct WorldGuard flag");
continue;
}
RegionGroupFlag groupFlag = foundFlag.getRegionGroupFlag();
if(value == null || value.isEmpty()) {
wRegion.setFlag(foundFlag, null);
if(groupFlag != null) {
wRegion.setFlag(groupFlag, null);
if(worldguardRegion.getFlag(foundFlag) != null) {
worldguardRegion.setFlag(foundFlag, null);
}
if(groupFlag != null && worldguardRegion.getFlag(groupFlag) != null) {
worldguardRegion.setFlag(groupFlag, null);
}
//AreaShop.debug(" Flag " + flagName + " reset (+ possible group of flag)");
} else {
@@ -128,7 +147,7 @@ public class WorldGuardRegionFlagsFeature extends Feature implements Listener {
try {
groupValue = plugin.getWorldGuardHandler().parseFlagGroupInput(groupFlag, part.substring(2));
} catch(InvalidFlagFormat e) {
plugin.getLogger().warning("Found wrong group value for flag " + flagName);
AreaShop.warn("Found wrong group value for flag " + flagName);
}
}
} else {
@@ -142,18 +161,18 @@ public class WorldGuardRegionFlagsFeature extends Feature implements Listener {
}
if(flagSetting != null) {
try {
setFlag(wRegion, foundFlag, flagSetting);
setFlag(worldguardRegion, foundFlag, flagSetting);
//AreaShop.debug(" Flag " + flagName + " set: " + flagSetting);
} catch(InvalidFlagFormat e) {
plugin.getLogger().warning("Found wrong value for flag " + flagName);
AreaShop.warn("Found wrong value for flag " + flagName);
}
}
if(groupValue != null) {
if(groupValue == groupFlag.getDefault()) {
wRegion.setFlag(groupFlag, null);
worldguardRegion.setFlag(groupFlag, null);
//AreaShop.debug(" Group of flag " + flagName + " set to default: " + groupValue);
} else {
wRegion.setFlag(groupFlag, groupValue);
worldguardRegion.setFlag(groupFlag, groupValue);
//AreaShop.debug(" Group of flag " + flagName + " set: " + groupValue);
}
}
@@ -161,28 +180,68 @@ public class WorldGuardRegionFlagsFeature extends Feature implements Listener {
}
}
// Indicate that the regions needs to be saved
plugin.getFileManager().saveIsRequiredForRegionWorld(region.getWorldName());
if(worldGuard.getDescription().getVersion().startsWith("5.")) {
plugin.getFileManager().saveIsRequiredForRegionWorld(region.getWorldName());
}
return result;
}
/**
* Set a WorldGuard region flag
* @param region The WorldGuard region to set
* @param flag The flag to set
* @param value The value to set the flag to
* @param <V> They type of flag to set
* @throws InvalidFlagFormat When the value of the flag is wrong
* Build an RegionAccessSet from an input that specifies player names, player uuids and groups.
* @param input Input string defining the access set
* @return RegionAccessSet containing the entities parsed from the input
*/
private <V> void setFlag(ProtectedRegion region, Flag<V> flag, String value) throws InvalidFlagFormat {
region.setFlag(flag, plugin.getWorldGuardHandler().parseFlagInput(flag, value));
public RegionAccessSet parseAccessSet(String input) {
RegionAccessSet result = new RegionAccessSet();
String[] inputParts = input.split(", ");
for(String access : inputParts) {
if(access != null && !access.isEmpty()) {
// Check for groups
if(access.startsWith("g:")) {
if(access.length() > 2) {
result.getGroupNames().add(access.substring(2));
}
} else if(access.startsWith("n:")) {
if(access.length() > 2) {
result.getPlayerNames().add(access.substring(2));
}
} else {
try {
result.getPlayerUniqueIds().add(UUID.fromString(access));
} catch(IllegalArgumentException e) {
AreaShop.warn("Tried using '" + access + "' as uuid for a region member/owner, is your flagProfiles section correct?");
}
}
}
}
return result;
}
/**
* Translate the color codes you put in greeting/farewell messages to the weird color codes of WorldGuard
* Set a WorldGuard region flag.
* @param region The WorldGuard region to set
* @param flag The flag to set
* @param value The value to set the flag to
* @param <V> They type of flag to set
* @throws InvalidFlagFormat When the value of the flag is wrong
*/
private <V> void setFlag(ProtectedRegion region, Flag<V> flag, String value) throws InvalidFlagFormat {
V current = region.getFlag(flag);
V next = plugin.getWorldGuardHandler().parseFlagInput(flag, value);
if(!Objects.equals(current, next)) {
region.setFlag(flag, next);
}
}
/**
* Translate the color codes you put in greeting/farewell messages to the weird color codes of WorldGuard.
* @param message The message where the color codes should be translated (this message has bukkit color codes)
* @return The string with the WorldGuard color codes
*/
public String translateBukkitToWorldGuardColors(String message) {
private String translateBukkitToWorldGuardColors(String message) {
String result = message;
result = result.replace("&c", "&r");
result = result.replace("&4", "&R");
@@ -0,0 +1,284 @@
package me.wiefferink.areashop.features.signs;
import com.google.common.base.Objects;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.Utils;
import me.wiefferink.interactivemessenger.processing.Message;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Sign;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
/**
* Sign that is connected to a region to display information and interact with the region.
*/
public class RegionSign {
private final SignsFeature signsFeature;
private final String key;
public RegionSign(SignsFeature signsFeature, String key) {
this.signsFeature = signsFeature;
this.key = key;
}
/**
* Get the location of this sign.
*
* @return The location of this sign
*/
public Location getLocation() {
return Utils.configToLocation(getRegion().getConfig().getConfigurationSection("general.signs." + key + ".location"));
}
/**
* Location string to be used as key in maps.
*
* @return Location string
*/
public String getStringLocation() {
return SignsFeature.locationToString(getLocation());
}
/**
* Chunk string to be used as key in maps.
*
* @return Chunk string
*/
public String getStringChunk() {
return SignsFeature.chunkToString(getLocation());
}
/**
* Get the region this sign is linked to.
*
* @return The region this sign is linked to
*/
public GeneralRegion getRegion() {
return signsFeature.getRegion();
}
/**
* Remove this sign from the region.
*/
public void remove() {
getLocation().getBlock().setType(Material.AIR);
signsFeature.getSignsRef().remove(getStringLocation());
SignsFeature.getAllSigns().remove(getStringLocation());
SignsFeature.getSignsByChunk().get(getStringChunk()).remove(this);
getRegion().setSetting("general.signs." + key, null);
}
/**
* Get the ConfigurationSection defining the sign layout.
*
* @return The sign layout config
*/
public ConfigurationSection getProfile() {
return getRegion().getConfigurationSectionSetting("general.signProfile", "signProfiles", getRegion().getConfig().get("general.signs." + key + ".profile"));
}
/**
* Get the facing of the sign as saved in the config.
*
* @return BlockFace the sign faces, or null if unknown
*/
public BlockFace getFacing() {
try {
return BlockFace.valueOf(getRegion().getConfig().getString("general.signs." + key + ".facing"));
} catch (NullPointerException | IllegalArgumentException e) {
return null;
}
}
/**
* Get the material of the sign as saved in the config.
*
* @return Material of the sign, usually {@link Material#WALL_SIGN}, {@link Material#SIGN}, or one of the other wood types (different result for 1.13-), Material.AIR if none.
*/
public Material getMaterial() {
String name = getRegion().getConfig().getString("general.signs." + key + ".signType");
Material result = Materials.signNameToMaterial(name);
return result == null ? Material.AIR : result;
}
/**
* Update this sign.
*
* @return true if the update was successful, otherwise false
*/
public boolean update() {
// Ignore updates of signs in chunks that are not loaded
Location signLocation = getLocation();
if (signLocation == null
|| signLocation.getWorld() == null
|| !signLocation.getWorld().isChunkLoaded(signLocation.getBlockX() >> 4, signLocation.getBlockZ() >> 4)) {
return false;
}
if (getRegion().isDeleted()) {
return false;
}
YamlConfiguration regionConfig = getRegion().getConfig();
ConfigurationSection signConfig = getProfile();
Block block = signLocation.getBlock();
if (signConfig == null || !signConfig.isSet(getRegion().getState().getValue())) {
block.setType(Material.AIR);
return true;
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue());
// Get the lines
String[] signLines = new String[4];
boolean signEmpty = true;
for (int i = 0; i < 4; i++) {
signLines[i] = stateConfig.getString("line" + (i + 1));
signEmpty &= (signLines[i] == null || signLines[i].isEmpty());
}
if (signEmpty) {
block.setType(Material.AIR);
return true;
}
// Place the sign back (with proper rotation and type) after it has been hidden or (indirectly) destroyed
if (!Materials.isSign(block.getType())) {
Material signType = getMaterial();
if (signType.name().contains("SIGN")) {
// 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());
}
}
// Save current rotation and type
if (!regionConfig.isString("general.signs." + key + ".signType")) {
getRegion().setSetting("general.signs." + key + ".signType", block.getType().name());
}
if (!regionConfig.isString("general.signs." + key + ".facing")) {
final BlockFace rotation;
if (Materials.isSign(block.getType())) {
BlockData data = block.getBlockData();
if (data instanceof org.bukkit.block.data.type.Sign) {
rotation = ((org.bukkit.block.data.type.Sign) block.getBlockData()).getRotation();
} else if (data instanceof WallSign) {
rotation = ((WallSign) data).getFacing();
} else {
rotation = null;
}
} else {
rotation = null;
}
getRegion().setSetting("general.signs." + key + ".facing", rotation == null ? null : rotation.toString());
}
// Apply replacements and color and then set it on the sign
Sign signState = (Sign) block.getState();
for (int i = 0; i < signLines.length; i++) {
if (signLines[i] == null) {
signState.setLine(i, "");
continue;
}
signLines[i] = Message.fromString(signLines[i]).replacements(getRegion()).getSingle();
signLines[i] = Utils.applyColors(signLines[i]);
signState.setLine(i, signLines[i]);
}
signState.update();
return true;
}
/**
* Check if the sign needs to update periodically.
*
* @return true if it needs periodic updates, otherwise false
*/
public boolean needsPeriodicUpdate() {
ConfigurationSection signConfig = getProfile();
if (signConfig == null || !signConfig.isSet(getRegion().getState().getValue().toLowerCase())) {
return false;
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue().toLowerCase());
if (stateConfig == null) {
return false;
}
// Check the lines for the timeleft tag
for (int i = 1; i <= 4; i++) {
String line = stateConfig.getString("line" + i);
if (line != null && !line.isEmpty() && line.contains(Message.VARIABLE_START + AreaShop.tagTimeLeft + Message.VARIABLE_END)) {
return true;
}
}
return false;
}
/**
* Run commands when a player clicks a sign.
*
* @param clicker The player that clicked the sign
* @param clickType The type of clicking
* @return true if the commands ran successfully, false if any of them failed
*/
public boolean runSignCommands(Player clicker, GeneralRegion.ClickType clickType) {
ConfigurationSection signConfig = getProfile();
if (signConfig == null) {
return false;
}
ConfigurationSection stateConfig = signConfig.getConfigurationSection(getRegion().getState().getValue().toLowerCase());
// Run player commands if specified
List<String> playerCommands = new ArrayList<>();
for (String command : stateConfig.getStringList(clickType.getValue() + "Player")) {
// TODO move variable checking code to InteractiveMessenger?
playerCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName()));
}
getRegion().runCommands(clicker, playerCommands);
// Run console commands if specified
List<String> consoleCommands = new ArrayList<>();
for (String command : stateConfig.getStringList(clickType.getValue() + "Console")) {
consoleCommands.add(command.replace(Message.VARIABLE_START + AreaShop.tagClicker + Message.VARIABLE_END, clicker.getName()));
}
getRegion().runCommands(Bukkit.getConsoleSender(), consoleCommands);
return !playerCommands.isEmpty() || !consoleCommands.isEmpty();
}
@Override
public boolean equals(Object object) {
return object instanceof RegionSign && ((RegionSign) object).getRegion().equals(this.getRegion()) && ((RegionSign) object).key.equals(this.key);
}
@Override
public int hashCode() {
return Objects.hashCode(key, getRegion().getName());
}
}
@@ -0,0 +1,636 @@
package me.wiefferink.areashop.features.signs;
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.managers.FileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
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.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
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 {
private static final Map<String, RegionSign> allSigns = new ConcurrentHashMap<>();
private static final Map<String, List<RegionSign>> signsByChunk = new ConcurrentHashMap<>();
private Map<String, RegionSign> signs;
public SignsFeature() {
}
/**
* Constructor.
*
* @param region The region to bind to
*/
public SignsFeature(GeneralRegion region) {
setRegion(region);
signs = new HashMap<>();
// Setup current signs
ConfigurationSection signSection = region.getConfig().getConfigurationSection("general.signs");
if (signSection != null) {
for (String signKey : signSection.getKeys(false)) {
RegionSign sign = new RegionSign(this, signKey);
Location location = sign.getLocation();
if (location == null) {
AreaShop.warn("Sign with key " + signKey + " of region " + region.getName() + " does not have a proper location");
continue;
}
signs.put(sign.getStringLocation(), sign);
signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>())
.add(sign);
}
allSigns.putAll(signs);
}
}
/**
* 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 static RegionSign getSignByLocation(Location location) {
return allSigns.get(locationToString(location));
}
/**
* Get the map with all signs.
*
* @return Map with all signs: locationString -&gt; RegionSign
*/
public static 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 static Map<String, List<RegionSign>> getSignsByChunk() {
return signsByChunk;
}
@Override
public void shutdown() {
// Deregister signs from the registry
if (signs != null) {
for (Map.Entry<String, RegionSign> entry : signs.entrySet()) {
allSigns.remove(entry.getKey());
signsByChunk.get(entry.getValue().getStringChunk()).remove(entry.getValue());
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onSignBreak(BlockBreakEvent event) {
if (event.isCancelled()) {
return;
}
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
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")) {
regionSign.remove();
plugin.message(event.getPlayer(), "delsign-success", regionSign.getRegion());
} else { // Cancel the breaking of the sign
event.setCancelled(true);
plugin.message(event.getPlayer(), "delsign-noPermission", regionSign.getRegion());
}
}
}
@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());
if (regionSign == null) {
return;
}
// 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);
event.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onSignClick(PlayerInteractEvent event) {
Block block = event.getClickedBlock();
if (block == null) {
return;
}
// Only listen to left and right clicks on blocks
if (!(event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK)) {
return;
}
// Only care about clicking blocks
if (!Materials.isSign(block.getType())) {
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();
if (plugin.getSignlinkerManager().isInSignLinkMode(player)) {
return;
}
// Get the clicktype
GeneralRegion.ClickType clickType = null;
if (player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) {
clickType = GeneralRegion.ClickType.SHIFTLEFTCLICK;
} else if (!player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) {
clickType = GeneralRegion.ClickType.LEFTCLICK;
} else if (player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) {
clickType = GeneralRegion.ClickType.SHIFTRIGHTCLICK;
} else if (!player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) {
clickType = GeneralRegion.ClickType.RIGHTCLICK;
}
boolean ran = regionSign.runSignCommands(player, clickType);
// Only cancel event if at least one command has been executed
event.setCancelled(ran);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onSignChange(SignChangeEvent event) {
if (event.isCancelled()) {
return;
}
Player player = event.getPlayer();
if (!plugin.isReady()) {
plugin.message(player, "general-notReady");
return;
}
final BlockFace rotation;
final BlockData data = event.getBlock().getBlockData();
if (data instanceof Sign) {
rotation = ((Sign) data).getRotation();
} else if (data instanceof WallSign) {
rotation = ((WallSign) data).getFacing();
} else {
rotation = null;
}
final String line0 = event.getLine(0) == null ? "" : event.getLine(0);
// Check if the sign is meant for this plugin
if (line0.contains(plugin.getConfig().getString("signTags.rent"))) {
if (!player.hasPermission("areashop.createrent") && !player.hasPermission("areashop.createrent.member") && !player.hasPermission("areashop.createrent.owner")) {
plugin.message(player, "setup-noPermissionRent");
return;
}
// Get the other lines
String secondLine = event.getLine(1);
String thirdLine = event.getLine(2);
String fourthLine = event.getLine(3);
// Get the regionManager for accessing regions
RegionManager regionManager = plugin.getRegionManager(event.getPlayer().getWorld());
// If the secondLine does not contain a name try to find the region by location
if (secondLine == null || secondLine.isEmpty()) {
Set<ProtectedRegion> regions = plugin.getWorldGuardHandler().getApplicableRegionsSet(event.getBlock().getLocation());
if (regions != null) {
boolean first = true;
ProtectedRegion candidate = null;
for (ProtectedRegion pr : regions) {
if (first) {
candidate = pr;
first = false;
} else {
if (pr.getPriority() > candidate.getPriority()) {
candidate = pr;
} else if (pr.getParent() != null && pr.getParent().equals(candidate)) {
candidate = pr;
} else {
plugin.message(player, "setup-couldNotDetect", candidate.getId(), pr.getId());
return;
}
}
}
if (candidate != null) {
secondLine = candidate.getId();
}
}
}
boolean priceSet = fourthLine != null && !fourthLine.isEmpty();
boolean durationSet = thirdLine != null && !thirdLine.isEmpty();
// check if all the lines are correct
if (secondLine == null || secondLine.isEmpty()) {
plugin.message(player, "setup-noRegion");
return;
}
ProtectedRegion region = regionManager.getRegion(secondLine);
if (region == null) {
plugin.message(player, "cmd-noRegion", secondLine);
return;
}
FileManager.AddResult addResult = plugin.getFileManager().checkRegionAdd(player, regionManager.getRegion(secondLine), event.getPlayer().getWorld(), GeneralRegion.RegionType.RENT);
switch (addResult) {
case BLACKLISTED:
plugin.message(player, "setup-blacklisted", secondLine);
break;
case ALREADYADDED:
plugin.message(player, "setup-alreadyRentSign");
break;
case ALREADYADDEDOTHERWORLD:
plugin.message(player, "setup-alreadyOtherWorld");
break;
case NOPERMISSION:
plugin.message(player, "setup-noPermission", secondLine);
break;
default:
if (thirdLine != null && !thirdLine.isEmpty() && !Utils.checkTimeFormat(thirdLine)) {
plugin.message(player, "setup-wrongDuration");
break;
}
double price = 0.0;
if (priceSet) {
// Check the fourth line
try {
price = Double.parseDouble(fourthLine);
} catch (NumberFormatException e) {
plugin.message(player, "setup-wrongPrice");
return;
}
}
// Add rent to the FileManager
final RentRegion rent = new RentRegion(secondLine, event.getPlayer().getWorld());
boolean isMember = plugin.getWorldGuardHandler().containsMember(rent.getRegion(), player.getUniqueId());
boolean isOwner = plugin.getWorldGuardHandler().containsOwner(rent.getRegion(), player.getUniqueId());
boolean landlord = (!player.hasPermission("areashop.createrent")
&& ((player.hasPermission("areashop.createrent.owner") && isOwner)
|| (player.hasPermission("areashop.createrent.member") && isMember)));
if (landlord) {
rent.setLandlord(player.getUniqueId(), player.getName());
}
if (priceSet) {
rent.setPrice(price);
}
if (durationSet) {
rent.setDuration(thirdLine);
}
rent.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), rotation, null);
AddingRegionEvent addingRegionEvent = plugin.getFileManager().addRegion(rent);
if (addingRegionEvent.isCancelled()) {
plugin.message(player, "general-cancelled", addingRegionEvent.getReason());
return;
}
rent.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
plugin.message(player, "setup-rentSuccess", rent);
// Update the region after the event has written its lines
Bukkit.getScheduler().runTask(plugin, rent::update);
}
} else if (line0.contains(plugin.getConfig().getString("signTags.buy"))) {
// Check for permission
if (!player.hasPermission("areashop.createbuy") && !player.hasPermission("areashop.createbuy.member") && !player.hasPermission("areashop.createbuy.owner")) {
plugin.message(player, "setup-noPermissionBuy");
return;
}
// Get the other lines
String secondLine = event.getLine(1);
String thirdLine = event.getLine(2);
// Get the regionManager for accessing regions
RegionManager regionManager = plugin.getRegionManager(event.getPlayer().getWorld());
// If the secondLine does not contain a name try to find the region by location
if (secondLine == null || secondLine.isEmpty()) {
Set<ProtectedRegion> regions = plugin.getWorldGuardHandler().getApplicableRegionsSet(event.getBlock().getLocation());
if (regions != null) {
boolean first = true;
ProtectedRegion candidate = null;
for (ProtectedRegion pr : regions) {
if (first) {
candidate = pr;
first = false;
} else {
if (pr.getPriority() > candidate.getPriority()) {
candidate = pr;
} else if (pr.getParent() != null && pr.getParent().equals(candidate)) {
candidate = pr;
} else {
plugin.message(player, "setup-couldNotDetect", candidate.getId(), pr.getId());
return;
}
}
}
if (candidate != null) {
secondLine = candidate.getId();
}
}
}
boolean priceSet = thirdLine != null && !thirdLine.isEmpty();
// Check if all the lines are correct
if (secondLine == null || secondLine.isEmpty()) {
plugin.message(player, "setup-noRegion");
return;
}
ProtectedRegion region = regionManager.getRegion(secondLine);
if (region == null) {
plugin.message(player, "cmd-noRegion", secondLine);
return;
}
FileManager.AddResult addResult = plugin.getFileManager().checkRegionAdd(player, region, event.getPlayer().getWorld(), GeneralRegion.RegionType.BUY);
if (addResult == FileManager.AddResult.BLACKLISTED) {
plugin.message(player, "setup-blacklisted", secondLine);
} else if (addResult == FileManager.AddResult.ALREADYADDED) {
plugin.message(player, "setup-alreadyRentSign");
} else if (addResult == FileManager.AddResult.ALREADYADDEDOTHERWORLD) {
plugin.message(player, "setup-alreadyOtherWorld");
} else if (addResult == FileManager.AddResult.NOPERMISSION) {
plugin.message(player, "setup-noPermission", secondLine);
} else {
double price = 0.0;
if (priceSet) {
// Check the fourth line
try {
price = Double.parseDouble(thirdLine);
} catch (NumberFormatException e) {
plugin.message(player, "setup-wrongPrice");
return;
}
}
// Add buy to the FileManager
final BuyRegion buy = new BuyRegion(secondLine, event.getPlayer().getWorld());
boolean isMember = plugin.getWorldGuardHandler().containsMember(buy.getRegion(), player.getUniqueId());
boolean isOwner = plugin.getWorldGuardHandler().containsOwner(buy.getRegion(), player.getUniqueId());
boolean landlord = (!player.hasPermission("areashop.createbuy")
&& ((player.hasPermission("areashop.createbuy.owner") && isOwner)
|| (player.hasPermission("areashop.createbuy.member") && isMember)));
if (landlord) {
buy.setLandlord(player.getUniqueId(), player.getName());
}
if (priceSet) {
buy.setPrice(price);
}
buy.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), rotation, null);
AddingRegionEvent addingRegionEvent = plugin.getFileManager().addRegion(buy);
if (addingRegionEvent.isCancelled()) {
plugin.message(player, "general-cancelled", addingRegionEvent.getReason());
return;
}
buy.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
plugin.message(player, "setup-buySuccess", buy);
// Update the region after the event has written its lines
Bukkit.getScheduler().runTask(plugin, buy::update);
}
} else if (event.getLine(0).contains(plugin.getConfig().getString("signTags.add"))) {
// Check for permission
if (!player.hasPermission("areashop.addsign")) {
plugin.message(player, "addsign-noPermission");
return;
}
// Get the other lines
String secondLine = event.getLine(1);
String thirdLine = event.getLine(2);
GeneralRegion region;
if (secondLine != null && !secondLine.isEmpty()) {
// Get region by secondLine of the sign
region = plugin.getFileManager().getRegion(secondLine);
if (region == null) {
plugin.message(player, "addSign-notRegistered", secondLine);
return;
}
} else {
// Get region by sign position
List<GeneralRegion> regions = Utils.getImportantRegions(event.getBlock().getLocation());
if (regions.isEmpty()) {
plugin.message(player, "addsign-noRegions");
return;
} else if (regions.size() > 1) {
plugin.message(player, "addsign-couldNotDetectSign", regions.get(0).getName(), regions.get(1).getName());
return;
}
region = regions.get(0);
}
String profile = thirdLine == null || thirdLine.isEmpty() ? null : thirdLine;
region.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), rotation, profile);
if (profile == null) {
plugin.message(player, "addsign-success", region);
} else {
plugin.message(player, "addsign-successProfile", thirdLine, region);
}
// Update the region later because this event will do it first
Bukkit.getScheduler().runTask(plugin, region::update);
}
}
@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.
*
* @return true if all signs are updated correctly, false if one or more updates failed
*/
public boolean update() {
boolean result = true;
for (RegionSign sign : signs.values()) {
result &= sign.update();
}
return result;
}
/**
* Check if any of the signs need periodic updating.
*
* @return true if one or more of the signs need periodic updating, otherwise false
*/
public boolean needsPeriodicUpdate() {
for (RegionSign sign : signs.values()) {
if (sign.needsPeriodicUpdate()) {
return true;
}
}
return false;
}
/**
* Get the signs of this region.
*
* @return List of signs
*/
public Collection<RegionSign> getSigns() {
return new HashSet<>(signs.values());
}
/**
* Get the signs of this region.
*
* @return Map with signs: locationString -&gt; RegionSign
*/
Map<String, RegionSign> getSignsRef() {
return signs;
}
/**
* Get a list with all sign locations.
*
* @return A List with all sign locations
*/
public List<Location> getSignLocations() {
List<Location> result = new ArrayList<>();
for (RegionSign sign : signs.values()) {
result.add(sign.getLocation());
}
return result;
}
/**
* Add a sign to this region.
*
* @param location The location of the sign
* @param signType The type of the sign (WALL_SIGN or SIGN_POST)
* @param facing The orientation of the sign
* @param profile The profile to use with this sign (null for default)
*/
public void addSign(Location location, Material signType, BlockFace facing, String profile) {
int i = 0;
while (getRegion().getConfig().isSet("general.signs." + i)) {
i++;
}
String signPath = "general.signs." + i + ".";
getRegion().setSetting(signPath + "location", Utils.locationToConfig(location));
getRegion().setSetting(signPath + "facing", facing != null ? facing.name() : null);
getRegion().setSetting(signPath + "signType", signType != null ? signType.name() : null);
if (profile != null && !profile.isEmpty()) {
getRegion().setSetting(signPath + "profile", profile);
}
// Add to the map
RegionSign sign = new RegionSign(this, i + "");
signs.put(sign.getStringLocation(), sign);
allSigns.put(sign.getStringLocation(), sign);
signsByChunk.computeIfAbsent(sign.getStringChunk(), key -> new ArrayList<>())
.add(sign);
}
}
@@ -1,743 +0,0 @@
/*
* Copyright 2011-2013 Tyler Blair. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and contributors and should not be interpreted as representing official policies,
* either expressed or implied, of anybody else.
*/
package me.wiefferink.areashop.lib;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.scheduler.BukkitTask;
import java.io.*;
import java.lang.reflect.Method;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.*;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
public class Metrics {
/**
* The current revision number
*/
private final static int REVISION = 7;
/**
* The base url of the metrics domain
*/
private static final String BASE_URL = "http://report.mcstats.org";
/**
* The url used to report a server's status
*/
private static final String REPORT_URL = "/plugin/%s";
/**
* Interval of time to ping (in minutes)
*/
private static final int PING_INTERVAL = 15;
/**
* The plugin this metrics submits for
*/
private final Plugin plugin;
/**
* All of the custom graphs to submit to metrics
*/
private final Set<Graph> graphs = Collections.synchronizedSet(new HashSet<Graph>());
/**
* The plugin configuration file
*/
private final YamlConfiguration configuration;
/**
* The plugin configuration file
*/
private final File configurationFile;
/**
* Unique server id
*/
private final String guid;
/**
* Debug mode
*/
private final boolean debug;
/**
* Lock for synchronization
*/
private final Object optOutLock = new Object();
/**
* The scheduled task
*/
private volatile BukkitTask task = null;
public Metrics(final Plugin plugin) throws IOException {
if(plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null");
}
this.plugin = plugin;
// load the config
configurationFile = getConfigFile();
configuration = YamlConfiguration.loadConfiguration(configurationFile);
// add some defaults
configuration.addDefault("opt-out", false);
configuration.addDefault("guid", UUID.randomUUID().toString());
configuration.addDefault("debug", false);
// Do we need to create the file?
if(configuration.get("guid", null) == null) {
configuration.options().header("http://mcstats.org").copyDefaults(true);
configuration.save(configurationFile);
}
// Load the guid then
guid = configuration.getString("guid");
debug = configuration.getBoolean("debug", false);
}
/**
* Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics
* website. Plotters can be added to the graph object returned.
* @param name The name of the graph
* @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given
*/
public Graph createGraph(final String name) {
if(name == null) {
throw new IllegalArgumentException("Graph name cannot be null");
}
// Construct the graph object
final Graph graph = new Graph(name);
// Now we can add our graph
graphs.add(graph);
// and return back
return graph;
}
/**
* Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend
* @param graph The name of the graph
*/
public void addGraph(final Graph graph) {
if(graph == null) {
throw new IllegalArgumentException("Graph cannot be null");
}
graphs.add(graph);
}
/**
* Start measuring statistics. This will immediately create an async repeating task as the plugin and send the
* initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200
* ticks.
* @return True if statistics measuring is running, otherwise false.
*/
public boolean start() {
synchronized(optOutLock) {
// Did we opt out?
if(isOptOut()) {
return false;
}
// Is metrics already running?
if(task != null) {
return true;
}
// Begin hitting the server with glorious data
task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() {
private boolean firstPost = true;
public void run() {
try {
// This has to be synchronized or it can collide with the disable method.
synchronized(optOutLock) {
// Disable Task, if it is running and the server owner decided to opt-out
if(isOptOut() && task != null) {
task.cancel();
task = null;
// Tell all plotters to stop gathering information.
for(Graph graph : graphs) {
graph.onOptOut();
}
}
}
// We use the inverse of firstPost because if it is the first time we are posting,
// it is not a interval ping, so it evaluates to FALSE
// Each time thereafter it will evaluate to TRUE, i.e PING!
postPlugin(!firstPost);
// After the first post we set firstPost to false
// Each post thereafter will be a ping
firstPost = false;
} catch(IOException e) {
if(debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage());
}
}
}
}, 0, PING_INTERVAL * 1200);
return true;
}
}
/**
* Has the server owner denied plugin metrics?
* @return true if metrics should be opted out of it
*/
public boolean isOptOut() {
synchronized(optOutLock) {
try {
// Reload the metrics file
configuration.load(getConfigFile());
} catch(IOException ex) {
if(debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
}
return true;
} catch(InvalidConfigurationException ex) {
if(debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
}
return true;
}
return configuration.getBoolean("opt-out", false);
}
}
/**
* Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task.
*/
public void enable() throws IOException {
// This has to be synchronized or it can collide with the check in the task.
synchronized(optOutLock) {
// Check if the server owner has already set opt-out, if not, set it.
if(isOptOut()) {
configuration.set("opt-out", false);
configuration.save(configurationFile);
}
// Enable Task, if it is not running
if(task == null) {
start();
}
}
}
/**
* Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task.
*/
public void disable() throws IOException {
// This has to be synchronized or it can collide with the check in the task.
synchronized(optOutLock) {
// Check if the server owner has already set opt-out, if not, set it.
if(!isOptOut()) {
configuration.set("opt-out", true);
configuration.save(configurationFile);
}
// Disable Task, if it is running
if(task != null) {
task.cancel();
task = null;
}
}
}
/**
* Gets the File object of the config file that should be used to store data such as the GUID and opt-out status
* @return the File object for the config file
*/
public File getConfigFile() {
// I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use
// is to abuse the plugin object we already have
// plugin.getDataFolder() => base/plugins/PluginA/
// pluginsFolder => base/plugins/
// The base is not necessarily relative to the startup directory.
File pluginsFolder = plugin.getDataFolder().getParentFile();
// return => base/plugins/PluginMetrics/config.yml
return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml");
}
/**
* Gets the online player (backwards compatibility)
* @return online player amount
*/
private int getOnlinePlayers() {
try {
Method onlinePlayerMethod = Server.class.getMethod("getOnlinePlayers");
if(onlinePlayerMethod.getReturnType().equals(Collection.class)) {
return ((Collection<?>)onlinePlayerMethod.invoke(Bukkit.getServer())).size();
} else {
return ((Player[])onlinePlayerMethod.invoke(Bukkit.getServer())).length;
}
} catch(Exception ex) {
if(debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
}
}
return 0;
}
/**
* Generic method that posts a plugin to the metrics website
*/
private void postPlugin(final boolean isPing) throws IOException {
// Server software specific section
PluginDescriptionFile description = plugin.getDescription();
String pluginName = description.getName();
boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled
String pluginVersion = description.getVersion();
String serverVersion = Bukkit.getVersion();
int playersOnline = this.getOnlinePlayers();
// END server software specific section -- all code below does not use any code outside of this class / Java
// Construct the post data
StringBuilder json = new StringBuilder(1024);
json.append('{');
// The plugin's description file containg all of the plugin data such as name, version, author, etc
appendJSONPair(json, "guid", guid);
appendJSONPair(json, "plugin_version", pluginVersion);
appendJSONPair(json, "server_version", serverVersion);
appendJSONPair(json, "players_online", Integer.toString(playersOnline));
// New data as of R6
String osname = System.getProperty("os.name");
String osarch = System.getProperty("os.arch");
String osversion = System.getProperty("os.version");
String java_version = System.getProperty("java.version");
int coreCount = Runtime.getRuntime().availableProcessors();
// normalize os arch .. amd64 -> x86_64
if(osarch.equals("amd64")) {
osarch = "x86_64";
}
appendJSONPair(json, "osname", osname);
appendJSONPair(json, "osarch", osarch);
appendJSONPair(json, "osversion", osversion);
appendJSONPair(json, "cores", Integer.toString(coreCount));
appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0");
appendJSONPair(json, "java_version", java_version);
// If we're pinging, append it
if(isPing) {
appendJSONPair(json, "ping", "1");
}
if(graphs.size() > 0) {
synchronized(graphs) {
json.append(',');
json.append('"');
json.append("graphs");
json.append('"');
json.append(':');
json.append('{');
boolean firstGraph = true;
final Iterator<Graph> iter = graphs.iterator();
while(iter.hasNext()) {
Graph graph = iter.next();
StringBuilder graphJson = new StringBuilder();
graphJson.append('{');
for(Plotter plotter : graph.getPlotters()) {
appendJSONPair(graphJson, plotter.getColumnName(), Integer.toString(plotter.getValue()));
}
graphJson.append('}');
if(!firstGraph) {
json.append(',');
}
json.append(escapeJSON(graph.getName()));
json.append(':');
json.append(graphJson);
firstGraph = false;
}
json.append('}');
}
}
// close json
json.append('}');
// Create the url
URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName)));
// Connect to the website
URLConnection connection;
// Mineshafter creates a socks proxy, so we can safely bypass it
// It does not reroute POST requests so we need to go around it
if(isMineshafterPresent()) {
connection = url.openConnection(Proxy.NO_PROXY);
} else {
connection = url.openConnection();
}
byte[] uncompressed = json.toString().getBytes();
byte[] compressed = gzip(json.toString());
// Headers
connection.addRequestProperty("User-Agent", "MCStats/" + REVISION);
connection.addRequestProperty("Content-Type", "application/json");
connection.addRequestProperty("Content-Encoding", "gzip");
connection.addRequestProperty("Content-Length", Integer.toString(compressed.length));
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.setDoOutput(true);
if(debug) {
System.out.println("[Metrics] Prepared request for " + pluginName + " uncompressed=" + uncompressed.length + " compressed=" + compressed.length);
}
// Write the data
OutputStream os = connection.getOutputStream();
os.write(compressed);
os.flush();
// Now read the response
final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String response = reader.readLine();
// close resources
os.close();
reader.close();
if(response == null || response.startsWith("ERR") || response.startsWith("7")) {
if(response == null) {
response = "null";
} else if(response.startsWith("7")) {
response = response.substring(response.startsWith("7,") ? 2 : 1);
}
throw new IOException(response);
} else {
// Is this the first update this hour?
if(response.equals("1") || response.contains("This is your first update this hour")) {
synchronized(graphs) {
final Iterator<Graph> iter = graphs.iterator();
while(iter.hasNext()) {
final Graph graph = iter.next();
for(Plotter plotter : graph.getPlotters()) {
plotter.reset();
}
}
}
}
}
}
/**
* GZip compress a string of bytes
*/
public static byte[] gzip(String input) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzos = null;
try {
gzos = new GZIPOutputStream(baos);
gzos.write(input.getBytes("UTF-8"));
} catch(IOException e) {
e.printStackTrace();
} finally {
if(gzos != null) try {
gzos.close();
} catch(IOException ignore) {
}
}
return baos.toByteArray();
}
/**
* Check if mineshafter is present. If it is, we need to bypass it to send POST requests
* @return true if mineshafter is installed on the server
*/
private boolean isMineshafterPresent() {
try {
Class.forName("mineshafter.MineServer");
return true;
} catch(Exception e) {
return false;
}
}
/**
* Appends a json encoded key/value pair to the given string builder.
*/
private static void appendJSONPair(StringBuilder json, String key, String value) throws UnsupportedEncodingException {
boolean isValueNumeric = false;
try {
if(value.equals("0") || !value.endsWith("0")) {
Double.parseDouble(value);
isValueNumeric = true;
}
} catch(NumberFormatException e) {
isValueNumeric = false;
}
if(json.charAt(json.length() - 1) != '{') {
json.append(',');
}
json.append(escapeJSON(key));
json.append(':');
if(isValueNumeric) {
json.append(value);
} else {
json.append(escapeJSON(value));
}
}
/**
* Escape a string to create a valid JSON string
*/
private static String escapeJSON(String text) {
StringBuilder builder = new StringBuilder();
builder.append('"');
for(int index = 0; index < text.length(); index++) {
char chr = text.charAt(index);
switch(chr) {
case '"':
case '\\':
builder.append('\\');
builder.append(chr);
break;
case '\b':
builder.append("\\b");
break;
case '\t':
builder.append("\\t");
break;
case '\n':
builder.append("\\n");
break;
case '\r':
builder.append("\\r");
break;
default:
if(chr < ' ') {
String t = "000" + Integer.toHexString(chr);
builder.append("\\u" + t.substring(t.length() - 4));
} else {
builder.append(chr);
}
break;
}
}
builder.append('"');
return builder.toString();
}
/**
* Encode text as UTF-8
* @param text the text to encode
* @return the encoded text, as UTF-8
*/
private static String urlEncode(final String text) throws UnsupportedEncodingException {
return URLEncoder.encode(text, "UTF-8");
}
/**
* Represents a custom graph on the website
*/
public static class Graph {
/**
* The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is
* rejected
*/
private final String name;
/**
* The set of plotters that are contained within this graph
*/
private final Set<Plotter> plotters = new LinkedHashSet<Plotter>();
private Graph(final String name) {
this.name = name;
}
/**
* Gets the graph's name
* @return the Graph's name
*/
public String getName() {
return name;
}
/**
* Add a plotter to the graph, which will be used to plot entries
* @param plotter the plotter to add to the graph
*/
public void addPlotter(final Plotter plotter) {
plotters.add(plotter);
}
/**
* Remove a plotter from the graph
* @param plotter the plotter to remove from the graph
*/
public void removePlotter(final Plotter plotter) {
plotters.remove(plotter);
}
/**
* Gets an <b>unmodifiable</b> set of the plotter objects in the graph
* @return an unmodifiable {@link java.util.Set} of the plotter objects
*/
public Set<Plotter> getPlotters() {
return Collections.unmodifiableSet(plotters);
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(final Object object) {
if(!(object instanceof Graph)) {
return false;
}
final Graph graph = (Graph)object;
return graph.name.equals(name);
}
/**
* Called when the server owner decides to opt-out of BukkitMetrics while the server is running.
*/
protected void onOptOut() {
}
}
/**
* Interface used to collect custom data for a plugin
*/
public static abstract class Plotter {
/**
* The plot's name
*/
private final String name;
/**
* Construct a plotter with the default plot name
*/
public Plotter() {
this("Default");
}
/**
* Construct a plotter with a specific plot name
* @param name the name of the plotter to use, which will show up on the website
*/
public Plotter(final String name) {
this.name = name;
}
/**
* Get the current value for the plotted point. Since this function defers to an external function it may or may
* not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called
* from any thread so care should be taken when accessing resources that need to be synchronized.
* @return the current value for the point to be plotted.
*/
public abstract int getValue();
/**
* Get the column name for the plotted point
* @return the plotted point's column name
*/
public String getColumnName() {
return name;
}
/**
* Called after the website graphs have been updated
*/
public void reset() {
}
@Override
public int hashCode() {
return getColumnName().hashCode();
}
@Override
public boolean equals(final Object object) {
if(!(object instanceof Plotter)) {
return false;
}
final Plotter plotter = (Plotter)object;
return plotter.name.equals(name) && plotter.getValue() == getValue();
}
}
}
@@ -1,750 +0,0 @@
package me.wiefferink.areashop.lib;
import me.wiefferink.areashop.AreaShop;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Check for updates on BukkitDev for a given plugin, and download the updates if needed.
* <p/>
* <b>VERY, VERY IMPORTANT</b>: Because there are no standards for adding auto-update toggles in your plugin's config, this system provides NO CHECK WITH YOUR CONFIG to make sure the user has allowed auto-updating.
* <br>
* It is a <b>BUKKIT POLICY</b> that you include a boolean value in your config that prevents the auto-updater from running <b>AT ALL</b>.
* <br>
* If you fail to include this option in your config, your plugin will be <b>REJECTED</b> when you attempt to submit it to dev.bukkit.org.
* <p/>
* An example of a good configuration option would be something similar to 'auto-update: true' - if this value is set to false you may NOT run the auto-updater.
* <br>
* If you are unsure about these rules, please read the plugin submission guidelines: http://goo.gl/8iU5l
*
* @author Gravity
* @version 2.3
*/
public class Updater {
/* Constants */
// Remote file's title
private static final String TITLE_VALUE = "name";
// Remote file's download link
private static final String LINK_VALUE = "downloadUrl";
// Remote file's release type
private static final String TYPE_VALUE = "releaseType";
// Remote file's build version
private static final String VERSION_VALUE = "gameVersion";
// Path to GET
private static final String QUERY = "/servermods/files?projectIds=";
// Slugs will be appended to this to get to the project's RSS feed
private static final String HOST = "https://api.curseforge.com";
// User-agent when querying Curse
private static final String USER_AGENT = "Updater (by Gravity)";
// Used for locating version numbers in file names
private static final String DELIMETER = "^V|[\\s_-]V";
// If the version number contains one of these, don't update.
private static final String[] NO_UPDATE_TAG = { "-DEV", "-PRE", "-SNAPSHOT" };
// Used for downloading files
private static final int BYTE_SIZE = 1024;
// Config key for api key
private static final String API_KEY_CONFIG_KEY = "api-key";
// Config key for disabling Updater
private static final String DISABLE_CONFIG_KEY = "disable";
// Default api key value in config
private static final String API_KEY_DEFAULT = "PUT_API_KEY_HERE";
// Default disable value in config
private static final boolean DISABLE_DEFAULT = false;
/* User-provided variables */
// Plugin running Updater
private final Plugin plugin;
// Type of update check to run
private final UpdateType type;
// Whether to announce file downloads
private final boolean announce;
// The plugin file (jar)
private final File file;
// The folder that downloads will be placed in
private final File updateFolder;
// The provided callback (if any)
private final UpdateCallback callback;
// Project's Curse ID
private int id = -1;
// BukkitDev ServerMods API key
private String apiKey = null;
/* Collected from Curse API */
private String versionName;
private String versionLink;
private String versionType;
private String versionGameVersion;
/* Update process variables */
// Connection to RSS
private URL url;
// Updater thread
private Thread thread;
// Used for determining the outcome of the update process
private Updater.UpdateResult result = Updater.UpdateResult.SUCCESS;
/**
* Gives the developer the result of the update process. Can be obtained by called {@link #getResult()}
*/
public enum UpdateResult {
/**
* The updater found an update, and has readied it to be loaded the next time the server restarts/reloads.
*/
SUCCESS,
/**
* The updater did not find an update, and nothing was downloaded.
*/
NO_UPDATE,
/**
* The server administrator has disabled the updating system.
*/
DISABLED,
/**
* The updater found an update, but was unable to download it.
*/
FAIL_DOWNLOAD,
/**
* For some reason, the updater was unable to contact dev.bukkit.org to download the file.
*/
FAIL_DBO,
/**
* When running the version check, the file on DBO did not contain a recognizable version.
*/
FAIL_NOVERSION,
/**
* The id provided by the plugin running the updater was invalid and doesn't exist on DBO.
*/
FAIL_BADID,
/**
* The server administrator has improperly configured their API key in the configuration.
*/
FAIL_APIKEY,
/**
* The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded.
*/
UPDATE_AVAILABLE
}
/**
* Allows the developer to specify the type of update that will be run.
*/
public enum UpdateType {
/**
* Run a version check, and then if the file is out of date, download the newest version.
*/
DEFAULT,
/**
* Don't run a version check, just find the latest update and download it.
*/
NO_VERSION_CHECK,
/**
* Get information about the version and the download size, but don't actually download anything.
*/
NO_DOWNLOAD
}
/**
* Represents the various release types of a file on BukkitDev.
*/
public enum ReleaseType {
/**
* An "alpha" file.
*/
ALPHA,
/**
* A "beta" file.
*/
BETA,
/**
* A "release" file.
*/
RELEASE
}
/**
* Initialize the updater.
*
* @param plugin The plugin that is checking for an update.
* @param id The dev.bukkit.org id of the project.
* @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class.
* @param type Specify the type of update this will be. See {@link UpdateType}
* @param announce True if the program should announce the progress of new updates in console.
*/
public Updater(Plugin plugin, int id, File file, UpdateType type, boolean announce) {
this(plugin, id, file, type, null, announce);
}
/**
* Initialize the updater with the provided callback.
*
* @param plugin The plugin that is checking for an update.
* @param id The dev.bukkit.org id of the project.
* @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class.
* @param type Specify the type of update this will be. See {@link UpdateType}
* @param callback The callback instance to notify when the Updater has finished
*/
public Updater(Plugin plugin, int id, File file, UpdateType type, UpdateCallback callback) {
this(plugin, id, file, type, callback, false);
}
/**
* Initialize the updater with the provided callback.
*
* @param plugin The plugin that is checking for an update.
* @param id The dev.bukkit.org id of the project.
* @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class.
* @param type Specify the type of update this will be. See {@link UpdateType}
* @param callback The callback instance to notify when the Updater has finished
* @param announce True if the program should announce the progress of new updates in console.
*/
public Updater(Plugin plugin, int id, File file, UpdateType type, UpdateCallback callback, boolean announce) {
this.plugin = plugin;
this.type = type;
this.announce = announce;
this.file = file;
this.id = id;
this.updateFolder = this.plugin.getServer().getUpdateFolderFile();
this.callback = callback;
final File pluginFile = this.plugin.getDataFolder().getParentFile();
final File updaterFile = new File(pluginFile, "Updater");
final File updaterConfigFile = new File(updaterFile, "config.yml");
YamlConfiguration config = new YamlConfiguration();
config.options().header("This configuration file affects all plugins using the Updater system (version 2+ - http://forums.bukkit.org/threads/96681/ )" + '\n'
+ "If you wish to use your API key, read http://wiki.bukkit.org/ServerMods_API and place it below." + '\n'
+ "Some updating systems will not adhere to the disabled value, but these may be turned off in their plugin's configuration.");
config.addDefault(API_KEY_CONFIG_KEY, API_KEY_DEFAULT);
config.addDefault(DISABLE_CONFIG_KEY, DISABLE_DEFAULT);
if (!updaterFile.exists()) {
this.fileIOOrError(updaterFile, updaterFile.mkdir(), true);
}
boolean createFile = !updaterConfigFile.exists();
try {
if (createFile) {
this.fileIOOrError(updaterConfigFile, updaterConfigFile.createNewFile(), true);
config.options().copyDefaults(true);
config.save(updaterConfigFile);
} else {
config.load(updaterConfigFile);
}
} catch (final Exception e) {
final String message;
if (createFile) {
message = "The updater could not create configuration at " + updaterFile.getAbsolutePath();
} else {
message = "The updater could not load configuration at " + updaterFile.getAbsolutePath();
}
this.plugin.getLogger().log(Level.SEVERE, message, e);
}
if (config.getBoolean(DISABLE_CONFIG_KEY)) {
this.result = UpdateResult.DISABLED;
return;
}
String key = config.getString(API_KEY_CONFIG_KEY);
if (API_KEY_DEFAULT.equalsIgnoreCase(key) || "".equals(key)) {
key = null;
}
this.apiKey = key;
try {
this.url = new URL(Updater.HOST + Updater.QUERY + this.id);
} catch (final MalformedURLException e) {
this.plugin.getLogger().log(Level.SEVERE, "The project ID provided for updating, " + this.id + " is invalid.", e);
this.result = UpdateResult.FAIL_BADID;
}
if (this.result != UpdateResult.FAIL_BADID) {
this.thread = new Thread(new UpdateRunnable());
this.thread.start();
} else {
runUpdater();
}
}
/**
* Get the result of the update process.
*
* @return result of the update process.
* @see UpdateResult
*/
public Updater.UpdateResult getResult() {
this.waitForThread();
return this.result;
}
/**
* Get the latest version's release type.
*
* @return latest version's release type.
* @see ReleaseType
*/
public ReleaseType getLatestType() {
this.waitForThread();
if (this.versionType != null) {
for (ReleaseType type : ReleaseType.values()) {
if (this.versionType.equalsIgnoreCase(type.name())) {
return type;
}
}
}
return null;
}
/**
* Get the latest version's game version (such as "CB 1.2.5-R1.0").
*
* @return latest version's game version.
*/
public String getLatestGameVersion() {
this.waitForThread();
return this.versionGameVersion;
}
/**
* Get the latest version's name (such as "Project v1.0").
*
* @return latest version's name.
*/
public String getLatestName() {
this.waitForThread();
return this.versionName;
}
/**
* Get the latest version's direct file link.
*
* @return latest version's file link.
*/
public String getLatestFileLink() {
this.waitForThread();
return this.versionLink;
}
/**
* As the result of Updater output depends on the thread's completion, it is necessary to wait for the thread to finish
* before allowing anyone to check the result.
*/
private void waitForThread() {
if ((this.thread != null) && this.thread.isAlive()) {
try {
this.thread.join();
} catch (final InterruptedException e) {
this.plugin.getLogger().log(Level.SEVERE, null, e);
}
}
}
/**
* Save an update from dev.bukkit.org into the server's update folder.
*
* @param file the name of the file to save it as.
*/
private void saveFile(String file) {
final File folder = this.updateFolder;
deleteOldFiles();
if (!folder.exists()) {
this.fileIOOrError(folder, folder.mkdir(), true);
}
downloadFile();
// Check to see if it's a zip file, if it is, unzip it.
final File dFile = new File(folder.getAbsolutePath(), file);
if (dFile.getName().endsWith(".zip")) {
// Unzip
this.unzip(dFile.getAbsolutePath());
}
if (this.announce) {
this.plugin.getLogger().info("Finished updating.");
}
}
/**
* Download a file and save it to the specified folder.
*/
private void downloadFile() {
BufferedInputStream in = null;
FileOutputStream fout = null;
try {
URL fileUrl = new URL(this.versionLink);
final int fileLength = fileUrl.openConnection().getContentLength();
in = new BufferedInputStream(fileUrl.openStream());
fout = new FileOutputStream(new File(this.updateFolder, file.getName()));
final byte[] data = new byte[Updater.BYTE_SIZE];
int count;
if (this.announce) {
this.plugin.getLogger().info("About to download a new update: " + this.versionName);
}
long downloaded = 0;
while ((count = in.read(data, 0, Updater.BYTE_SIZE)) != -1) {
downloaded += count;
fout.write(data, 0, count);
final int percent = (int) ((downloaded * 100) / fileLength);
if (this.announce && ((percent % 10) == 0)) {
this.plugin.getLogger().info("Downloading update: " + percent + "% of " + fileLength + " bytes.");
}
}
} catch (Exception ex) {
this.plugin.getLogger().log(Level.WARNING, "The auto-updater tried to download a new update, but was unsuccessful.", ex);
this.result = Updater.UpdateResult.FAIL_DOWNLOAD;
} finally {
try {
if (in != null) {
in.close();
}
} catch (final IOException ex) {
this.plugin.getLogger().log(Level.SEVERE, null, ex);
}
try {
if (fout != null) {
fout.close();
}
} catch (final IOException ex) {
this.plugin.getLogger().log(Level.SEVERE, null, ex);
}
}
}
/**
* Remove possibly leftover files from the update folder.
*/
private void deleteOldFiles() {
//Just a quick check to make sure we didn't leave any files from last time...
File[] list = listFilesOrError(this.updateFolder);
for (final File xFile : list) {
if (xFile.getName().endsWith(".zip")) {
this.fileIOOrError(xFile, xFile.mkdir(), true);
}
}
}
/**
* Part of Zip-File-Extractor, modified by Gravity for use with Updater.
*
* @param file the location of the file to extract.
*/
private void unzip(String file) {
final File fSourceZip = new File(file);
try {
final String zipPath = file.substring(0, file.length() - 4);
ZipFile zipFile = new ZipFile(fSourceZip);
Enumeration<? extends ZipEntry> e = zipFile.entries();
while (e.hasMoreElements()) {
ZipEntry entry = e.nextElement();
File destinationFilePath = new File(zipPath, entry.getName());
this.fileIOOrError(destinationFilePath.getParentFile(), destinationFilePath.getParentFile().mkdirs(), true);
if (!entry.isDirectory()) {
final BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
int b;
final byte[] buffer = new byte[Updater.BYTE_SIZE];
final FileOutputStream fos = new FileOutputStream(destinationFilePath);
final BufferedOutputStream bos = new BufferedOutputStream(fos, Updater.BYTE_SIZE);
while ((b = bis.read(buffer, 0, Updater.BYTE_SIZE)) != -1) {
bos.write(buffer, 0, b);
}
bos.flush();
bos.close();
bis.close();
final String name = destinationFilePath.getName();
if (name.endsWith(".jar") && this.pluginExists(name)) {
File output = new File(this.updateFolder, name);
this.fileIOOrError(output, destinationFilePath.renameTo(output), true);
}
}
}
zipFile.close();
// Move any plugin data folders that were included to the right place, Bukkit won't do this for us.
moveNewZipFiles(zipPath);
} catch (final IOException e) {
this.plugin.getLogger().log(Level.SEVERE, "The auto-updater tried to unzip a new update file, but was unsuccessful.", e);
this.result = Updater.UpdateResult.FAIL_DOWNLOAD;
} finally {
this.fileIOOrError(fSourceZip, fSourceZip.delete(), false);
}
}
/**
* Find any new files extracted from an update into the plugin's data directory.
* @param zipPath path of extracted files.
*/
private void moveNewZipFiles(String zipPath) {
File[] list = listFilesOrError(new File(zipPath));
for (final File dFile : list) {
if (dFile.isDirectory() && this.pluginExists(dFile.getName())) {
// Current dir
final File oFile = new File(this.plugin.getDataFolder().getParent(), dFile.getName());
// List of existing files in the new dir
final File[] dList = listFilesOrError(dFile);
// List of existing files in the current dir
final File[] oList = listFilesOrError(oFile);
for (File cFile : dList) {
// Loop through all the files in the new dir
boolean found = false;
for (final File xFile : oList) {
// Loop through all the contents in the current dir to see if it exists
if (xFile.getName().equals(cFile.getName())) {
found = true;
break;
}
}
if (!found) {
// Move the new file into the current dir
File output = new File(oFile, cFile.getName());
this.fileIOOrError(output, cFile.renameTo(output), true);
} else {
// This file already exists, so we don't need it anymore.
this.fileIOOrError(cFile, cFile.delete(), false);
}
}
}
this.fileIOOrError(dFile, dFile.delete(), false);
}
File zip = new File(zipPath);
this.fileIOOrError(zip, zip.delete(), false);
}
/**
* Check if the name of a jar is one of the plugins currently installed, used for extracting the correct files out of a zip.
*
* @param name a name to check for inside the plugins folder.
* @return true if a file inside the plugins folder is named this.
*/
private boolean pluginExists(String name) {
File[] plugins = listFilesOrError(new File("plugins"));
for (final File file : plugins) {
if (file.getName().equals(name)) {
return true;
}
}
return false;
}
/**
* Check to see if the program should continue by evaluating whether the plugin is already updated, or shouldn't be updated.
*
* @return true if the version was located and is not the same as the remote's newest.
*/
private boolean versionCheck() {
final String title = this.versionName;
if (this.type != UpdateType.NO_VERSION_CHECK) {
String localVersion = this.plugin.getDescription().getVersion();
if(localVersion.contains("#")) { // Strip build number
localVersion = localVersion.substring(0, localVersion.indexOf("#"));
}
if (title.split(DELIMETER).length == 2) {
// Get the newest file's version number
final String remoteVersion = title.split(DELIMETER)[1].split(" ")[0];
if (this.hasTag(localVersion) || !this.shouldUpdate(localVersion, remoteVersion)) {
// We already have the latest version, or this build is tagged for no-update
this.result = Updater.UpdateResult.NO_UPDATE;
return false;
}
} else {
// The file's name did not contain the string 'vVersion'
final String authorInfo = this.plugin.getDescription().getAuthors().isEmpty() ? "" : " (" + this.plugin.getDescription().getAuthors().get(0) + ")";
this.plugin.getLogger().warning("The author of this plugin" + authorInfo + " has misconfigured their Auto Update system");
this.plugin.getLogger().warning("File versions should follow the format 'PluginName vVERSION'");
this.plugin.getLogger().warning("Please notify the author of this error.");
this.result = Updater.UpdateResult.FAIL_NOVERSION;
return false;
}
}
return true;
}
/**
* <b>If you wish to run mathematical versioning checks, edit this method.</b>
* <p>
* With default behavior, Updater will NOT verify that a remote version available on BukkitDev
* which is not this version is indeed an "update".
* If a version is present on BukkitDev that is not the version that is currently running,
* Updater will assume that it is a newer version.
* This is because there is no standard versioning scheme, and creating a calculation that can
* determine whether a new update is actually an update is sometimes extremely complicated.
* </p>
* <p>
* Updater will call this method from {@link #versionCheck()} before deciding whether
* the remote version is actually an update.
* If you have a specific versioning scheme with which a mathematical determination can
* be reliably made to decide whether one version is higher than another, you may
* revise this method, using the local and remote version parameters, to execute the
* appropriate check.
* </p>
* <p>
* Returning a value of <b>false</b> will tell the update process that this is NOT a new version.
* Without revision, this method will always consider a remote version at all different from
* that of the local version a new update.
* </p>
* @param localVersion the current version
* @param remoteVersion the remote version
* @return true if Updater should consider the remote version an update, false if not.
*/
public boolean shouldUpdate(String localVersion, String remoteVersion) {
return !localVersion.equalsIgnoreCase(remoteVersion);
}
/**
* Evaluate whether the version number is marked showing that it should not be updated by this program.
*
* @param version a version number to check for tags in.
* @return true if updating should be disabled.
*/
private boolean hasTag(String version) {
for (final String string : Updater.NO_UPDATE_TAG) {
if (version.contains(string)) {
return true;
}
}
return false;
}
/**
* Make a connection to the BukkitDev API and request the newest file's details.
*
* @return true if successful.
*/
private boolean read() {
try {
final URLConnection conn = this.url.openConnection();
conn.setConnectTimeout(5000);
if (this.apiKey != null) {
conn.addRequestProperty("X-API-Key", this.apiKey);
}
conn.addRequestProperty("User-Agent", Updater.USER_AGENT);
conn.setDoOutput(true);
final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final String response = reader.readLine();
final JSONArray array = (JSONArray) JSONValue.parse(response);
if (array.isEmpty()) {
this.plugin.getLogger().warning("The updater could not find any files for the project id " + this.id);
this.result = UpdateResult.FAIL_BADID;
return false;
}
JSONObject latestUpdate = (JSONObject) array.get(array.size() - 1);
this.versionName = (String) latestUpdate.get(Updater.TITLE_VALUE);
this.versionLink = (String) latestUpdate.get(Updater.LINK_VALUE);
this.versionType = (String) latestUpdate.get(Updater.TYPE_VALUE);
this.versionGameVersion = (String) latestUpdate.get(Updater.VERSION_VALUE);
return true;
} catch (final IOException e) {
if (e.getMessage().contains("HTTP response code: 403")) {
this.plugin.getLogger().severe("dev.bukkit.org rejected the API key provided in plugins/Updater/config.yml");
this.plugin.getLogger().severe("Please double-check your configuration to ensure it is correct.");
this.result = UpdateResult.FAIL_APIKEY;
} else {
this.plugin.getLogger().severe("The updater could not contact dev.bukkit.org for updating.");
this.plugin.getLogger().severe("If you have not recently modified your configuration and this is the first time you are seeing this message, the site may be experiencing temporary downtime.");
this.result = UpdateResult.FAIL_DBO;
}
AreaShop.debug(ExceptionUtils.getStackTrace(e));
return false;
}
}
/**
* Perform a file operation and log any errors if it fails.
* @param file file operation is performed on.
* @param result result of file operation.
* @param create true if a file is being created, false if deleted.
*/
private void fileIOOrError(File file, boolean result, boolean create) {
if (!result) {
this.plugin.getLogger().severe("The updater could not " + (create ? "create" : "delete") + " file at: " + file.getAbsolutePath());
}
}
private File[] listFilesOrError(File folder) {
File[] contents = folder.listFiles();
if (contents == null) {
this.plugin.getLogger().severe("The updater could not access files at: " + this.updateFolder.getAbsolutePath());
return new File[0];
} else {
return contents;
}
}
/**
* Called on main thread when the Updater has finished working, regardless
* of result.
*/
public interface UpdateCallback {
/**
* Called when the updater has finished working.
* @param updater The updater instance
*/
void onFinish(Updater updater);
}
private class UpdateRunnable implements Runnable {
@Override
public void run() {
runUpdater();
}
}
private void runUpdater() {
if (this.url != null && (this.read() && this.versionCheck())) {
// Obtain the results of the project's file feed
if ((this.versionLink != null) && (this.type != UpdateType.NO_DOWNLOAD)) {
String name = this.file.getName();
// If it's a zip file, it shouldn't be downloaded as the plugin's name
if (this.versionLink.endsWith(".zip")) {
name = this.versionLink.substring(this.versionLink.lastIndexOf("/") + 1);
}
this.saveFile(name);
} else {
this.result = UpdateResult.UPDATE_AVAILABLE;
}
}
if (this.callback != null) {
new BukkitRunnable() {
@Override
public void run() {
runCallback();
}
}.runTask(this.plugin);
}
}
private void runCallback() {
this.callback.onFinish(this);
}
}
@@ -1,9 +1,11 @@
package me.wiefferink.areashop.listeners;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
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;
import org.bukkit.event.EventPriority;
@@ -12,129 +14,122 @@ import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* Checks for placement of signs for this plugin
* @author NLThijs48
* Notify region expiry and track activity time.
*/
public final class PlayerLoginLogoutListener implements Listener {
private AreaShop plugin;
/**
* Constructor
* @param plugin The AreaShop plugin
*/
public PlayerLoginLogoutListener(AreaShop plugin) {
this.plugin = plugin;
}
/**
* Called when a sign is changed
* @param event The event
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLogin(PlayerLoginEvent event) {
if(event.getResult() != Result.ALLOWED) {
return;
}
final Player player = event.getPlayer();
// Notify admins for plugin updates
if(plugin.updateAvailable() && player.hasPermission("areashop.notifyupdate")) {
AreaShop.getInstance().message(player, "update-playerNotify", AreaShop.getInstance().getDescription().getVersion(), AreaShop.getInstance().getUpdater().getLatestName());
}
// Schedule task to check for notifications, prevents a lag spike at login
new BukkitRunnable() {
@Override
public void run() {
// Delay until all regions are loaded
if(!plugin.isReady()) {
return;
}
if(!player.isOnline()) {
this.cancel();
return;
}
// Notify for rents that almost run out
for(RentRegion region : plugin.getFileManager().getRents()) {
if(region.isRenter(player)) {
String warningSetting = region.getStringSetting("rent.warningOnLoginTime");
if(warningSetting == null || warningSetting.isEmpty()) {
continue;
}
long warningTime = Utils.durationStringToLong(warningSetting);
if(region.getTimeLeft() < warningTime) {
// Send the warning message later to let it appear after general MOTD messages
AreaShop.getInstance().message(player, "rent-expireWarning", region);
}
}
}
this.cancel();
}
}.runTaskTimer(plugin, 25, 25);
// Check if the player has regions that use an old name of him and update them
final List<GeneralRegion> regions = new ArrayList<>(plugin.getFileManager().getRegions());
new BukkitRunnable() {
private int current = 0;
@Override
public void run() {
// Delay until all regions are loaded
if(!plugin.isReady()) {
return;
}
// Check all regions
for(int i=0; i<plugin.getConfig().getInt("nameupdate.regionsPerTick"); i++) {
if(current < regions.size()) {
GeneralRegion region = regions.get(current);
if(region.isOwner(player)) {
if(region.isBuyRegion()) {
if(!player.getName().equals(region.getStringSetting("buy.buyerName"))) {
region.setSetting("buy.buyerName", player.getName());
region.update();
}
} else if(region.isRentRegion()) {
if(!player.getName().equals(region.getStringSetting("rent.renterName"))) {
region.setSetting("rent.renterName", player.getName());
region.update();
}
}
}
current++;
}
}
if(current >= regions.size()) {
this.cancel();
}
}
}.runTaskTimer(plugin, 22, 1); // Wait a bit before starting to prevent a lot of stress on the server when a player joins (a lot of plugins already do stuff then)
}
// Active time updates
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLogout(PlayerQuitEvent event) {
updateLastActive(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerKick(PlayerKickEvent event) {
updateLastActive(event.getPlayer());
}
/**
* Update the last active time for all regions the player is owner off
* @param player The player to update the active times for
*/
private void updateLastActive(Player player) {
for(GeneralRegion region : plugin.getFileManager().getRegions()) {
if(region.isOwner(player)) {
region.updateLastActiveTime();
}
}
}
private final AreaShop plugin;
/**
* Constructor.
*
* @param plugin The AreaShop plugin
*/
public PlayerLoginLogoutListener(AreaShop plugin) {
this.plugin = plugin;
}
/**
* Called when a sign is changed.
*
* @param event The event
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLogin(PlayerLoginEvent event) {
if (event.getResult() != Result.ALLOWED) {
return;
}
final Player player = event.getPlayer();
// Schedule task to check for notifications, prevents a lag spike at login
Utils.runCancellingTaskTimer(() -> {
// Delay until all regions are loaded
if (!plugin.isReady()) {
return true;
}
if (!player.isOnline()) {
return false;
}
// Notify for rents that almost run out
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;
}
long warningTime = Utils.durationStringToLong(warningSetting);
if (region.getTimeLeft() < warningTime) {
// Send the warning message later to let it appear after general MOTD messages
AreaShop.getInstance().message(player, "rent-expireWarning", region);
}
}
}
// Notify admins for plugin updates
AreaShop.getInstance().notifyUpdate(player);
return false;
}, 25, 25, false);
// Check if the player has regions that use an old name of him and update them
Utils.runCancellingTaskTimer(() -> {
if (!plugin.isReady()) {
return true;
}
List<GeneralRegion> regions = new LinkedList<>();
for (GeneralRegion region : plugin.getFileManager().getRegions()) {
if (region instanceof TransactionalRegion && ((TransactionalRegion) region).isTransactionHolder(player)) {
regions.add(region);
}
}
final String name = player.getName();
Utils.runAsBatches(regions,
plugin.getConfig().getInt("nameupdate.regionsPerTick"),
(region) -> {
if (region instanceof BuyRegion || region instanceof RentRegion) {
String key = region instanceof BuyRegion ? "buy.buyerName" : "rent.renterName";
if (!name.equals(region.getStringSetting("buy.buyerName"))) {
region.setSetting(key, player.getName());
region.update();
}
}
}, false);
return false;
}, 22, 10, false);
}
// Active time updates
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLogout(PlayerQuitEvent event) {
updateLastActive(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerKick(PlayerKickEvent event) {
updateLastActive(event.getPlayer());
}
/**
* Update the last active time for all regions the player is owner off.
*
* @param player The player to update the active times for
*/
private void updateLastActive(Player player) {
for (GeneralRegion region : plugin.getFileManager().getRegions()) {
if (region instanceof TransactionalRegion && ((TransactionalRegion) region).isTransactionHolder(player)) {
region.updateLastActiveTime();
}
}
}
}
@@ -1,131 +0,0 @@
package me.wiefferink.areashop.listeners;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.SignsFeature;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPhysicsEvent;
/**
* Checks for placement of signs for this plugin
* @author NLThijs48
*/
public final class SignBreakListener implements Listener {
private AreaShop plugin;
/**
* Constructor
* @param plugin The AreaShop plugin
*/
public SignBreakListener(AreaShop plugin) {
this.plugin = plugin;
}
/**
* Called when a block is broken
* @param event The event
*/
@EventHandler(priority = EventPriority.HIGH)
public void onSignBreak(BlockBreakEvent event) {
if(event.isCancelled()) {
return;
}
Block block = event.getBlock();
// Check if it is a sign
if(block.getType() == Material.WALL_SIGN || block.getType() == Material.SIGN_POST) {
// Check if the rent sign is really the same as a saved rent
SignsFeature.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")) {
regionSign.remove();
plugin.message(event.getPlayer(), "delsign-success", regionSign.getRegion());
} else { // Cancel the breaking of the sign
event.setCancelled(true);
plugin.message(event.getPlayer(), "delsign-noPermission", regionSign.getRegion());
}
}
}
/**
* Called when the physics of a block change
* @param event The event
*/
@EventHandler(priority = EventPriority.HIGHEST)
public void onIndirectSignBreak(BlockPhysicsEvent event){
Block block = event.getBlock();
if(event.isCancelled()) {
return;
}
if(block.getType() == Material.SIGN_POST || block.getType() == Material.WALL_SIGN){
Sign sign = (Sign)block.getState();
Block attachedTo = block.getRelative(((org.bukkit.material.Sign)sign.getData()).getAttachedFace());
if(attachedTo.getType() == Material.AIR){
// Check if the rent sign is really the same as a saved rent
SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if(regionSign != null) {
event.setCancelled(true); // Cancel the sign breaking, will create a floating sign but at least it is not disconnected/gone
}
}
}
}
}
@@ -1,359 +0,0 @@
package me.wiefferink.areashop.listeners;
import com.sk89q.worldedit.bukkit.selections.CuboidSelection;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.managers.FileManager;
import me.wiefferink.areashop.regions.BuyRegion;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.material.Sign;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.List;
import java.util.Set;
/**
* Checks for placement of signs for this plugin
* @author NLThijs48
*/
public final class SignChangeListener implements Listener {
private AreaShop plugin;
/**
* Constructor
* @param plugin The AreaShop plugin
*/
public SignChangeListener(AreaShop plugin) {
this.plugin = plugin;
}
/**
* Called when a sign is changed
* @param event The event
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onSignChange(SignChangeEvent event) {
if(event.isCancelled()) {
return;
}
Player player = event.getPlayer();
if(!plugin.isReady()) {
plugin.message(player, "general-notReady");
return;
}
// Check if the sign is meant for this plugin
if(event.getLine(0).contains(plugin.getConfig().getString("signTags.rent"))) {
if(!player.hasPermission("areashop.createrent") && !player.hasPermission("areashop.createrent.member") && !player.hasPermission("areashop.createrent.owner")) {
plugin.message(player, "setup-noPermissionRent");
return;
}
// Get the other lines
String secondLine = event.getLine(1);
String thirdLine = event.getLine(2);
String fourthLine = event.getLine(3);
// Get the regionManager for accessing regions
RegionManager regionManager = plugin.getWorldGuard().getRegionManager(event.getPlayer().getWorld());
// If the secondLine does not contain a name try to find the region by location
if(secondLine == null || secondLine.length() == 0) {
Set<ProtectedRegion> regions = plugin.getWorldGuardHandler().getApplicableRegionsSet(event.getBlock().getLocation());
if(regions != null) {
boolean first = true;
ProtectedRegion candidate = null;
for(ProtectedRegion pr : regions) {
if(first) {
candidate = pr;
first = false;
} else {
if(pr.getPriority() > candidate.getPriority()) {
candidate = pr;
} else if(pr.getPriority() < candidate.getPriority()) {
// Already got the correct one
} else if(pr.getParent() != null && pr.getParent().equals(candidate)) {
candidate = pr;
} else if(candidate.getParent() != null && candidate.getParent().equals(pr)) {
// Already got the correct one
} else {
plugin.message(player, "setup-couldNotDetect", candidate.getId(), pr.getId());
return;
}
}
}
if(candidate != null) {
secondLine = candidate.getId();
}
}
}
boolean priceSet = fourthLine != null && fourthLine.length() != 0;
boolean durationSet = thirdLine != null && thirdLine.length() != 0;
// check if all the lines are correct
if(secondLine == null || secondLine.length() == 0) {
plugin.message(player, "setup-noRegion");
return;
}
ProtectedRegion region = regionManager.getRegion(secondLine);
if(region == null) {
plugin.message(player, "cmd-notRegistered", secondLine);
return;
}
FileManager.AddResult addResult = plugin.getFileManager().checkRegionAdd(player, regionManager.getRegion(secondLine), GeneralRegion.RegionType.RENT);
if(addResult == FileManager.AddResult.BLACKLISTED) {
plugin.message(player, "setup-blacklisted", secondLine);
} else if(addResult == FileManager.AddResult.ALREADYADDED) {
plugin.message(player, "setup-alreadyRentSign");
} else if(addResult == FileManager.AddResult.NOPERMISSION) {
plugin.message(player, "setup-noPermission", secondLine);
} else if(thirdLine != null && thirdLine.length() != 0 && !Utils.checkTimeFormat(thirdLine)) {
plugin.message(player, "setup-wrongDuration");
} else {
double price = 0.0;
if(priceSet) {
// Check the fourth line
try {
price = Double.parseDouble(fourthLine);
} catch (NumberFormatException e) {
plugin.message(player, "setup-wrongPrice");
return;
}
}
// Add rent to the FileManager
final RentRegion rent = new RentRegion(plugin, secondLine, event.getPlayer().getWorld());
boolean isMember = plugin.getWorldGuardHandler().containsMember(rent.getRegion(), player.getUniqueId());
boolean isOwner = plugin.getWorldGuardHandler().containsOwner(rent.getRegion(), player.getUniqueId());
boolean landlord = (!player.hasPermission("areashop.createrent")
&& ((player.hasPermission("areashop.createrent.owner") && isOwner)
|| (player.hasPermission("areashop.createrent.member") && isMember)));
if(landlord) {
rent.setLandlord(player.getUniqueId(), player.getName());
}
if(priceSet) {
rent.setPrice(price);
}
if(durationSet) {
rent.setDuration(thirdLine);
}
Sign sign = (Sign)event.getBlock().getState().getData();
rent.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), sign.getFacing(), null);
// Run commands
rent.runEventCommands(GeneralRegion.RegionEvent.CREATED, true);
plugin.getFileManager().addRent(rent);
rent.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
plugin.message(player, "setup-rentSuccess", rent);
// Update the region after the event has written its lines
new BukkitRunnable() {
@Override
public void run() {
rent.update();
}
}.runTaskLater(plugin, 1);
// Run commands
rent.runEventCommands(GeneralRegion.RegionEvent.CREATED, false);
}
} else if (event.getLine(0).contains(plugin.getConfig().getString("signTags.buy"))) {
// Check for permission
if(!player.hasPermission("areashop.createbuy") && !player.hasPermission("areashop.createbuy.member") && !player.hasPermission("areashop.createbuy.owner")) {
plugin.message(player, "setup-noPermissionBuy");
return;
}
// Get the other lines
String secondLine = event.getLine(1);
String thirdLine = event.getLine(2);
// Get the regionManager for accessing regions
RegionManager regionManager = plugin.getWorldGuard().getRegionManager(event.getPlayer().getWorld());
// If the secondLine does not contain a name try to find the region by location
if(secondLine == null || secondLine.length() == 0) {
Set<ProtectedRegion> regions = plugin.getWorldGuardHandler().getApplicableRegionsSet(event.getBlock().getLocation());
if(regions != null) {
boolean first = true;
ProtectedRegion candidate = null;
for(ProtectedRegion pr : regions) {
if(first) {
candidate = pr;
first = false;
} else {
if(pr.getPriority() > candidate.getPriority()) {
candidate = pr;
} else if(pr.getPriority() < candidate.getPriority()) {
// Already got the correct one
} else if(pr.getParent() != null && pr.getParent().equals(candidate)) {
candidate = pr;
} else if(candidate.getParent() != null && candidate.getParent().equals(pr)) {
// Already got the correct one
} else {
plugin.message(player, "setup-couldNotDetect", candidate.getId(), pr.getId());
return;
}
}
}
if(candidate != null) {
secondLine = candidate.getId();
}
}
}
boolean priceSet = thirdLine != null && thirdLine.length() != 0;
// Check if all the lines are correct
if(secondLine == null || secondLine.length() == 0) {
plugin.message(player, "setup-noRegion");
return;
}
ProtectedRegion region = regionManager.getRegion(secondLine);
if(region == null) {
plugin.message(player, "add-noRegion", secondLine);
return;
}
FileManager.AddResult addResult = plugin.getFileManager().checkRegionAdd(player, region, GeneralRegion.RegionType.BUY);
if(addResult == FileManager.AddResult.BLACKLISTED) {
plugin.message(player, "setup-blacklisted", secondLine);
} else if(addResult == FileManager.AddResult.ALREADYADDED) {
plugin.message(player, "setup-alreadyRentSign");
} else if(addResult == FileManager.AddResult.NOPERMISSION) {
plugin.message(player, "setup-noPermission", secondLine);
} else {
double price = 0.0;
if(priceSet) {
// Check the fourth line
try {
price = Double.parseDouble(thirdLine);
} catch (NumberFormatException e) {
plugin.message(player, "setup-wrongPrice");
return;
}
}
// Add buy to the FileManager
final BuyRegion buy = new BuyRegion(plugin, secondLine, event.getPlayer().getWorld());
boolean isMember = plugin.getWorldGuardHandler().containsMember(buy.getRegion(), player.getUniqueId());
boolean isOwner = plugin.getWorldGuardHandler().containsOwner(buy.getRegion(), player.getUniqueId());
boolean landlord = (!player.hasPermission("areashop.createbuy")
&& ((player.hasPermission("areashop.createbuy.owner") && isOwner)
|| (player.hasPermission("areashop.createbuy.member") && isMember)));
if(landlord) {
buy.setLandlord(player.getUniqueId(), player.getName());
}
if(priceSet) {
buy.setPrice(price);
}
Sign sign = (Sign)event.getBlock().getState().getData();
buy.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), sign.getFacing(), null);
// Run commands
buy.runEventCommands(GeneralRegion.RegionEvent.CREATED, true);
plugin.getFileManager().addBuy(buy);
buy.handleSchematicEvent(GeneralRegion.RegionEvent.CREATED);
plugin.message(player, "setup-buySuccess", buy);
// Update the region after the event has written its lines
new BukkitRunnable() {
@Override
public void run() {
buy.update();
}
}.runTaskLater(plugin, 1);
// Run commands
buy.runEventCommands(GeneralRegion.RegionEvent.CREATED, false);
}
} else if(event.getLine(0).contains(plugin.getConfig().getString("signTags.add"))) {
// Check for permission
if(!player.hasPermission("areashop.addsign")) {
plugin.message(player, "addsign-noPermission");
return;
}
// Get the other lines
String secondLine = event.getLine(1);
String thirdLine = event.getLine(2);
GeneralRegion region;
if(secondLine != null && secondLine.length() != 0) {
// Get region by secondLine of the sign
region = plugin.getFileManager().getRegion(secondLine);
if(region == null) {
plugin.message(player, "add-noRegion", secondLine);
return;
}
} else {
// Get region by sign position
List<GeneralRegion> regions = Utils.getASRegionsInSelection(new CuboidSelection(event.getBlock().getWorld(), event.getBlock().getLocation(), event.getBlock().getLocation()));
if(regions.isEmpty()) {
plugin.message(player, "addsign-noRegions");
return;
} else if(regions.size() > 1) {
plugin.message(player, "addsign-couldNotDetectSign", regions.get(0).getName(), regions.get(1).getName());
return;
}
region = regions.get(0);
}
Sign sign = (Sign)event.getBlock().getState().getData();
if(thirdLine == null || thirdLine.length() == 0) {
region.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), sign.getFacing(), null);
plugin.message(player, "addsign-success", region);
} else {
region.getSignsFeature().addSign(event.getBlock().getLocation(), event.getBlock().getType(), sign.getFacing(), thirdLine);
plugin.message(player, "addsign-successProfile", thirdLine, region);
}
// Update the region later because this event will do it first
final GeneralRegion regionUpdate = region;
new BukkitRunnable() {
@Override
public void run() {
regionUpdate.update();
}
}.runTaskLater(plugin, 1);
}
}
}
@@ -1,94 +0,0 @@
package me.wiefferink.areashop.listeners;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.SignsFeature;
import me.wiefferink.areashop.regions.GeneralRegion.ClickType;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
public class SignClickListener implements Listener {
private AreaShop plugin;
/**
* Constructor
* @param plugin The AreaShop plugin
*/
public SignClickListener(AreaShop plugin) {
this.plugin = plugin;
}
/**
* Called when a player interacts
* @param event The event
*/
@EventHandler(priority = EventPriority.HIGH)
public void onSignClick(PlayerInteractEvent event) {
if(event.isCancelled()) {
return;
}
Block block = event.getClickedBlock();
// Check for clicking a sign and rightclicking
if((event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK)
&& (block.getType() == Material.SIGN_POST || block.getType() == Material.WALL_SIGN)) {
// Check if the rent sign is really the same as a saved rent
SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if(regionSign == null) {
return;
}
Player player = event.getPlayer();
if(plugin.getSignlinkerManager().isInSignLinkMode(player)) {
return;
}
// Get the clicktype
ClickType clickType = null;
if(player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) {
clickType = ClickType.SHIFTLEFTCLICK;
} else if(!player.isSneaking() && event.getAction() == Action.LEFT_CLICK_BLOCK) {
clickType = ClickType.LEFTCLICK;
} else if(player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) {
clickType = ClickType.SHIFTRIGHTCLICK;
} else if(!player.isSneaking() && event.getAction() == Action.RIGHT_CLICK_BLOCK) {
clickType = ClickType.RIGHTCLICK;
}
// Run the commands
boolean ran = regionSign.runSignCommands(player, clickType);
// Only cancel event if at least one command has been executed
event.setCancelled(ran);
}
}
}
@@ -1,7 +1,39 @@
package me.wiefferink.areashop.managers;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.commands.*;
import me.wiefferink.areashop.commands.AddCommand;
import me.wiefferink.areashop.commands.AddfriendCommand;
import me.wiefferink.areashop.commands.AddsignCommand;
import me.wiefferink.areashop.commands.BuyCommand;
import me.wiefferink.areashop.commands.CommandAreaShop;
import me.wiefferink.areashop.commands.DelCommand;
import me.wiefferink.areashop.commands.DelfriendCommand;
import me.wiefferink.areashop.commands.DelsignCommand;
import me.wiefferink.areashop.commands.FindCommand;
import me.wiefferink.areashop.commands.GroupaddCommand;
import me.wiefferink.areashop.commands.GroupdelCommand;
import me.wiefferink.areashop.commands.GroupinfoCommand;
import me.wiefferink.areashop.commands.GrouplistCommand;
import me.wiefferink.areashop.commands.HelpCommand;
import me.wiefferink.areashop.commands.ImportCommand;
import me.wiefferink.areashop.commands.InfoCommand;
import me.wiefferink.areashop.commands.LinksignsCommand;
import me.wiefferink.areashop.commands.MeCommand;
import me.wiefferink.areashop.commands.MessageCommand;
import me.wiefferink.areashop.commands.ReloadCommand;
import me.wiefferink.areashop.commands.RentCommand;
import me.wiefferink.areashop.commands.ResellCommand;
import me.wiefferink.areashop.commands.SchematiceventCommand;
import me.wiefferink.areashop.commands.SellCommand;
import me.wiefferink.areashop.commands.SetdurationCommand;
import me.wiefferink.areashop.commands.SetlandlordCommand;
import me.wiefferink.areashop.commands.SetownerCommand;
import me.wiefferink.areashop.commands.SetpriceCommand;
import me.wiefferink.areashop.commands.SetrestoreCommand;
import me.wiefferink.areashop.commands.SetteleportCommand;
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.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -12,65 +44,63 @@ import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
public class CommandManager implements CommandExecutor, TabCompleter {
private AreaShop plugin;
private ArrayList<CommandAreaShop> commands;
public class CommandManager extends Manager implements CommandExecutor, TabCompleter {
private final ArrayList<CommandAreaShop> commands;
/**
* Constructor
* @param plugin The AreaShop plugin
* Constructor.
*/
public CommandManager(AreaShop plugin) {
this.plugin = plugin;
public CommandManager() {
commands = new ArrayList<>();
commands.add(new HelpCommand(plugin));
commands.add(new RentCommand(plugin));
commands.add(new UnrentCommand(plugin));
commands.add(new BuyCommand(plugin));
commands.add(new SellCommand(plugin));
commands.add(new MeCommand(plugin));
commands.add(new InfoCommand(plugin));
commands.add(new TeleportCommand(plugin));
commands.add(new SetteleportCommand(plugin));
commands.add(new AddfriendCommand(plugin));
commands.add(new DelfriendCommand(plugin));
commands.add(new FindCommand(plugin));
commands.add(new ResellCommand(plugin));
commands.add(new StopresellCommand(plugin));
commands.add(new SetrestoreCommand(plugin));
commands.add(new SetpriceCommand(plugin));
commands.add(new SetownerCommand(plugin));
commands.add(new SetdurationCommand(plugin));
commands.add(new ReloadCommand(plugin));
commands.add(new GroupaddCommand(plugin));
commands.add(new GroupdelCommand(plugin));
commands.add(new GrouplistCommand(plugin));
commands.add(new GroupinfoCommand(plugin));
commands.add(new SchematiceventCommand(plugin));
commands.add(new AddCommand(plugin));
commands.add(new DelCommand(plugin));
commands.add(new AddsignCommand(plugin));
commands.add(new DelsignCommand(plugin));
commands.add(new LinksignsCommand(plugin));
commands.add(new StackCommand(plugin));
commands.add(new SetlandlordCommand(plugin));
commands.add(new MessageCommand(plugin));
commands.add(new HelpCommand());
commands.add(new RentCommand());
commands.add(new UnrentCommand());
commands.add(new BuyCommand());
commands.add(new SellCommand());
commands.add(new MeCommand());
commands.add(new InfoCommand());
commands.add(new TeleportCommand());
commands.add(new SetteleportCommand());
commands.add(new AddfriendCommand());
commands.add(new DelfriendCommand());
commands.add(new FindCommand());
commands.add(new ResellCommand());
commands.add(new StopresellCommand());
commands.add(new SetrestoreCommand());
commands.add(new SetpriceCommand());
commands.add(new SetownerCommand());
commands.add(new SetdurationCommand());
commands.add(new ReloadCommand());
commands.add(new GroupaddCommand());
commands.add(new GroupdelCommand());
commands.add(new GrouplistCommand());
commands.add(new GroupinfoCommand());
commands.add(new SchematiceventCommand());
commands.add(new AddCommand());
commands.add(new DelCommand());
commands.add(new AddsignCommand());
commands.add(new DelsignCommand());
commands.add(new LinksignsCommand());
commands.add(new StackCommand());
commands.add(new SetlandlordCommand());
commands.add(new MessageCommand());
commands.add(new ImportCommand());
// Register commands in bukkit
plugin.getCommand("AreaShop").setExecutor(this);
plugin.getCommand("AreaShop").setExecutor(this);
plugin.getCommand("AreaShop").setTabCompleter(this);
}
}
/**
* Get the list with AreaShop commands
* Get the list with AreaShop commands.
* @return The list with AreaShop commands
*/
public ArrayList<CommandAreaShop> getCommands() {
public List<CommandAreaShop> getCommands() {
return commands;
}
/**
* Shows the help page for the CommandSender
* Shows the help page for the CommandSender.
* @param target The CommandSender to show the help to
*/
public void showHelp(CommandSender target) {
@@ -84,7 +114,7 @@ public class CommandManager implements CommandExecutor, TabCompleter {
plugin.message(target, "help-alias");
for(CommandAreaShop command : commands) {
String help = command.getHelp(target);
if(help != null && help.length() != 0) {
if(help != null && !help.isEmpty()) {
messages.add(help);
}
}
@@ -93,36 +123,43 @@ public class CommandManager implements CommandExecutor, TabCompleter {
plugin.messageNoPrefix(target, message);
}
}
@Override
public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) {
if(!plugin.isReady()) {
plugin.message(sender, "general-notReady");
return true;
}
// Redirect `/as info player <player>` to `/as me <player>`
if(args.length == 3 && "info".equals(args[0]) && "player".equals(args[1])) {
args[0] = "me";
args[1] = args[2];
}
// Execute command
boolean executed = false;
for(int i=0; i<commands.size() && !executed; i++) {
boolean executed = false;
for(int i = 0; i < commands.size() && !executed; i++) {
if(commands.get(i).canExecute(command, args)) {
commands.get(i).execute(sender, args);
executed = true;
}
}
if(!executed && args.length == 0) {
this.showHelp(sender);
} else if(!executed && args.length > 0) {
// Indicate that the '/as updaterents' and '/as updatebuys' commands are removed
if("updaterents".equalsIgnoreCase(args[0]) || "updatebuys".equalsIgnoreCase(args[0])) {
plugin.message(sender, "reload-updateCommandChanged");
// Show help
if (!executed) {
if (args.length == 0) {
this.showHelp(sender);
} else {
plugin.message(sender, "cmd-notValid");
// Indicate that the '/as updaterents' and '/as updatebuys' commands are removed
if("updaterents".equalsIgnoreCase(args[0]) || "updatebuys".equalsIgnoreCase(args[0])) {
plugin.message(sender, "reload-updateCommandChanged");
} else {
plugin.message(sender, "cmd-notValid");
}
}
}
return true;
}
@@ -133,19 +170,17 @@ public class CommandManager implements CommandExecutor, TabCompleter {
return result;
}
int toCompleteNumber = args.length;
String toCompletePrefix = args[args.length-1].toLowerCase();
String toCompletePrefix = args[args.length - 1].toLowerCase();
//AreaShop.debug("toCompleteNumber=" + toCompleteNumber + ", toCompletePrefix=" + toCompletePrefix + ", length=" + toCompletePrefix.length());
if(toCompleteNumber == 1) {
for(CommandAreaShop c : commands) {
String begin = c.getCommandStart();
result.add(begin.substring(begin.indexOf(' ') +1));
result.add(begin.substring(begin.indexOf(' ') + 1));
}
} else {
String[] start = new String[args.length];
start[0] = command.getName();
for(int i=1; i<args.length; i++) {
start[i] = args[i-1];
}
System.arraycopy(args, 0, start, 1, args.length - 1);
for(CommandAreaShop c : commands) {
if(c.canExecute(command, args)) {
result = c.getTabCompleteList(toCompleteNumber, start, sender);
@@ -153,13 +188,13 @@ public class CommandManager implements CommandExecutor, TabCompleter {
}
}
// Filter and sort the results
if(result.size() > 0) {
if(!result.isEmpty()) {
SortedSet<String> set = new TreeSet<>();
for(String suggestion : result) {
if(suggestion.toLowerCase().startsWith(toCompletePrefix)) {
set.add(suggestion);
}
}
}
result.clear();
result.addAll(set);
}
@@ -0,0 +1,88 @@
package me.wiefferink.areashop.managers;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.features.CommandsFeature;
import me.wiefferink.areashop.features.DebugFeature;
import me.wiefferink.areashop.features.FriendsFeature;
import me.wiefferink.areashop.features.RegionFeature;
import me.wiefferink.areashop.features.TeleportFeature;
import me.wiefferink.areashop.features.WorldGuardRegionFlagsFeature;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.regions.GeneralRegion;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class FeatureManager extends Manager {
// List of defined features
private static final Set<Class<? extends RegionFeature>> featureClasses = new HashSet<>(Arrays.asList(
DebugFeature.class,
SignsFeature.class,
FriendsFeature.class,
WorldGuardRegionFlagsFeature.class,
TeleportFeature.class,
CommandsFeature.class
));
// One instance of each feature, registered for event handling
private final Set<RegionFeature> globalFeatures;
private final Map<Class<? extends RegionFeature>, Constructor<? extends RegionFeature>> regionFeatureConstructors;
/**
* Constructor.
*/
public FeatureManager() {
// Instantiate and register global features (one per type, for event handling)
globalFeatures = new HashSet<>();
for(Class<? extends RegionFeature> clazz : featureClasses) {
try {
Constructor<? extends RegionFeature> constructor = clazz.getConstructor();
RegionFeature feature = constructor.newInstance();
feature.listen();
globalFeatures.add(feature);
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | IllegalArgumentException e) {
AreaShop.error("Failed to instantiate global feature:", clazz, e);
} catch(NoSuchMethodException e) {
// Feature does not have a global part
}
}
// Setup constructors for region specific features
regionFeatureConstructors = new HashMap<>();
for(Class<? extends RegionFeature> clazz : featureClasses) {
try {
regionFeatureConstructors.put(clazz, clazz.getConstructor(GeneralRegion.class));
} catch(NoSuchMethodException | IllegalArgumentException e) {
// The feature does not have a region specific part
}
}
}
@Override
public void shutdown() {
for(RegionFeature feature : globalFeatures) {
feature.shutdown();
}
}
/**
* Instanciate a feature for a certain region.
* @param region The region to create a feature for
* @param featureClazz The class of the feature to create
* @return The feature class
*/
public RegionFeature getRegionFeature(GeneralRegion region, Class<? extends RegionFeature> featureClazz) {
try {
return regionFeatureConstructors.get(featureClazz).newInstance(region);
} catch(InstantiationException | InvocationTargetException | IllegalAccessException | IllegalArgumentException e) {
AreaShop.error("Failed to instantiate feature", featureClazz, "for region", region, e, e.getCause());
}
return null;
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,16 @@
package me.wiefferink.areashop.managers;
import me.wiefferink.areashop.AreaShop;
public abstract class Manager {
final AreaShop plugin = AreaShop.getInstance();
/**
* Called at shutdown of the plugin.
*/
public void shutdown() {
// To override by extending classes
}
}
@@ -1,13 +1,18 @@
package me.wiefferink.areashop.managers;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.features.SignsFeature;
import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Materials;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Sign;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -16,25 +21,34 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.material.Sign;
import org.bukkit.util.BlockIterator;
import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class SignLinkerManager implements Listener {
private AreaShop plugin = null;
private Map<UUID, SignLinker> signLinkers;
public class SignLinkerManager extends Manager implements Listener {
private final Map<UUID, SignLinker> signLinkers;
private boolean eventsRegistered;
public SignLinkerManager(AreaShop plugin) {
this.plugin = plugin;
public SignLinkerManager() {
signLinkers = new HashMap<>();
eventsRegistered = false;
}
@Override
public void shutdown() {
for(UUID uuid : signLinkers.keySet()) {
exitSignLinkMode(Bukkit.getPlayer(uuid));
}
}
/**
* Let a player enter sign linking mode
* @param player The player that has to enter sign linking mode
* Let a player enter sign linking mode.
* @param player The player that has to enter sign linking mode
* @param profile The profile to use for the signs (null for default)
*/
public void enterSignLinkMode(Player player, String profile) {
@@ -46,9 +60,9 @@ public class SignLinkerManager implements Listener {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
}
/**
* Let a player exit sign linking mode
* Let a player exit sign linking mode.
* @param player The player that has to exit sign linking mode
*/
public void exitSignLinkMode(Player player) {
@@ -59,9 +73,9 @@ public class SignLinkerManager implements Listener {
}
plugin.message(player, "linksigns-stopped");
}
/**
* Check if the player is in sign linking mode
* Check if the player is in sign linking mode.
* @param player The player to check
* @return true if the player is in sign linking mode, otherwise false
*/
@@ -70,7 +84,7 @@ public class SignLinkerManager implements Listener {
}
/**
* On player interactions
* On player interactions.
* @param event The PlayerInteractEvent
*/
@EventHandler(priority = EventPriority.HIGH)
@@ -84,7 +98,7 @@ public class SignLinkerManager implements Listener {
BlockIterator blockIterator = new BlockIterator(player, 100);
while(blockIterator.hasNext()) {
Block next = blockIterator.next();
List<GeneralRegion> regions = Utils.getASRegionsByLocation(next.getLocation());
List<GeneralRegion> regions = Utils.getRegions(next.getLocation());
if(regions.size() == 1) {
linker.setRegion(regions.get(0));
return;
@@ -96,7 +110,7 @@ public class SignLinkerManager implements Listener {
plugin.message(player, "linksigns-multipleRegions", Utils.createCommaSeparatedList(names));
plugin.message(player, "linksigns-multipleRegionsAdvice");
return;
}
}
}
// No regions found within the maximum range
plugin.message(player, "linksigns-noRegions");
@@ -109,52 +123,60 @@ public class SignLinkerManager implements Listener {
block = next;
}
}
if(block == null || !(block.getType() == Material.WALL_SIGN || block.getType() == Material.SIGN_POST)) {
if(block == null || !Materials.isSign(block.getType())) {
plugin.message(player, "linksigns-noSign");
return;
}
SignsFeature.RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
if(regionSign != null) {
plugin.message(player, "linksigns-alreadyRegistered", regionSign.getRegion());
return;
}
Sign sign = (Sign)block.getState().getData();
linker.setSign(block.getLocation(), block.getType(), sign.getFacing());
final BlockData blockData = block.getBlockData();
final BlockFace rotation;
if (blockData instanceof Sign) {
rotation = ((Sign) blockData).getRotation();
} else if (blockData instanceof WallSign) {
rotation = ((WallSign) blockData).getFacing();
} else {
rotation = null;
}
linker.setSign(block.getLocation(), block.getType(), rotation);
}
}
}
/**
* Handle disconnection players
* Handle disconnection players.
* @param event The PlayerQuitEvent
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLeave(PlayerQuitEvent event) {
signLinkers.remove(event.getPlayer().getUniqueId());
exitSignLinkMode(event.getPlayer());
}
/**
* Class to keep track of the signlinking data
* Class to keep track of the signlinking data.
*/
private class SignLinker {
private boolean hasSign = false;
private boolean hasRegion = false;
public Player linker = null;
public String profile = null;
public final Player linker;
public final String profile;
public GeneralRegion region = null;
public Location location = null;
public Material type = null;
public BlockFace facing = null;
public SignLinker(Player linker, String profile) {
this.linker = linker;
this.profile = profile;
}
public void setRegion(GeneralRegion region) {
this.region = region;
hasRegion = true;
@@ -163,7 +185,7 @@ public class SignLinkerManager implements Listener {
}
finish();
}
public void setSign(Location location, Material type, BlockFace facing) {
this.location = location;
this.type = type;
@@ -185,21 +207,21 @@ public class SignLinkerManager implements Listener {
}
region.update();
reset();
plugin.message(linker, "linksigns-next");
}
}
public void reset() {
hasSign = false;
hasRegion = false;
}
public boolean isComplete() {
return hasSign && hasRegion;
}
}
}
@@ -1,108 +0,0 @@
package me.wiefferink.areashop.messages;
import org.apache.commons.lang.Validate;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
/**
* Class made by glan3b (Github: https://github.com/glen3b) for the Fanciful project (Github: https://github.com/mkremins/fanciful)
*/
/**
* Represents a wrapper around an array class of an arbitrary reference type,
* which properly implements "value" hash code and equality functions.
* <p>
* This class is intended for use as a key to a map.
* </p>
* @param <E> The type of elements in the array.
* @author Glen Husman
* @see Arrays
*/
public final class ArrayWrapper<E> {
/**
* Creates an array wrapper with some elements.
* @param elements The elements of the array.
*/
public ArrayWrapper(E... elements) {
setArray(elements);
}
private E[] _array;
/**
* Retrieves a reference to the wrapped array instance.
* @return The array wrapped by this instance.
*/
public E[] getArray() {
return _array;
}
/**
* Set this wrapper to wrap a new array instance.
* @param array The new wrapped array.
*/
public void setArray(E[] array) {
Validate.notNull(array, "The array must not be null.");
_array = array;
}
/**
* Determines if this object has a value equivalent to another object.
* @see Arrays#equals(Object[], Object[])
*/
@SuppressWarnings("rawtypes")
@Override
public boolean equals(Object other) {
if(!(other instanceof ArrayWrapper)) {
return false;
}
return Arrays.equals(_array, ((ArrayWrapper)other)._array);
}
/**
* Gets the hash code represented by this objects value.
* @return This object's hash code.
* @see Arrays#hashCode(Object[])
*/
@Override
public int hashCode() {
return Arrays.hashCode(_array);
}
/**
* Converts an iterable element collection to an array of elements.
* The iteration order of the specified object will be used as the array element order.
* @param list The iterable of objects which will be converted to an array.
* @param c The type of the elements of the array.
* @param <T> The type
* @return An array of elements in the specified iterable.
*/
@SuppressWarnings("unchecked")
public static <T> T[] toArray(Iterable<? extends T> list, Class<T> c) {
int size = -1;
if(list instanceof Collection<?>) {
@SuppressWarnings("rawtypes")
Collection coll = (Collection)list;
size = coll.size();
}
if(size < 0) {
size = 0;
// Ugly hack: Count it ourselves
for(@SuppressWarnings("unused") T element : list) {
size++;
}
}
T[] result = (T[])Array.newInstance(c, size);
int i = 0;
for(T element : list) { // Assumes iteration order is consistent
result[i++] = element; // Assign array element at index THEN increment counter
}
return result;
}
}
@@ -1,867 +0,0 @@
package me.wiefferink.areashop.messages;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* FancyMessageFormat converter, a library that enables to convert
* messages in the FancyMessageFormat to Minecraft's bulky tellraw
* format.
* @author NLThijs48 | http://wiefferink.me
* @author Tobias aka Phoenix | http://www.phoenix-iv.de
*/
public class FancyMessageFormat {
private static final char TAG_BEFORE = '[';
private static final char TAG_AFTER = ']';
private static final char END_TAG_INDICATOR = '/';
/**
* The special character that prefixes all basic chat formatting codes.
*/
private static final char SIMPLE_FORMAT_PREFIX_CHAR = '\u00A7';
/**
* Resets all previous chat colors or formats.
*/
private static final char SIMPLE_FORMAT_RESET_CHAR = 'r';
/**
* Lookup table for all continuous tags (marked by [])
*/
private static final HashMap<String, Tag> BRACKET_TAG_LIST = new HashMap<>();
/**
* Lookup table for all interactive tags
*/
private static final HashMap<String, Tag> INTERACTIVE_TAG_LIST = new HashMap<>();
static {
// Enlist all possible tags
// (They go into a HashMap for lookup purposes)
cacheTags(BRACKET_TAG_LIST, Color.class);
cacheTags(BRACKET_TAG_LIST, FormatType.class);
cacheTags(BRACKET_TAG_LIST, FormatCloseTag.class);
cacheTags(BRACKET_TAG_LIST, ControlTag.class);
// Interactive tags
cacheTags(INTERACTIVE_TAG_LIST, ClickType.class);
cacheTags(INTERACTIVE_TAG_LIST, HoverType.class);
}
/**
* Puts all constants in the given Tag class into the given lookup table.
*/
private static <T extends Tag> void cacheTags(HashMap<String, Tag> tagList, Class<T> tags) {
for(Tag tag : tags.getEnumConstants()) {
for(String key : tag.getTags()) {
tagList.put(key, tag);
}
}
}
// ------------------------------------------------------------------------------------------
// ------------------------------- Public / Interface -------------------------------
// ------------------------------------------------------------------------------------------
/**
* Parses the given FancyMessageFormat message to a JSON array that can be
* used with the tellraw command and the like.
* @param message The mesage to convert to JSON
* @return JSON string that can be send to a player
*/
public static String convertToJSON(final String message) {
return convertToJSON(Collections.singleton(message));
}
/**
* Parses the given FancyMessageFormat message to a JSON array that can be
* used with the tellraw command and the like.
* @param inputLines Input message split at line breaks.
* @return JSON string that can be send to a player
*/
public static String convertToJSON(final Iterable<String> inputLines) {
ArrayList<String> lines = cleanInputString(inputLines);
LinkedList<InteractiveMessagePart> message = parse(lines);
StringBuilder sb = new StringBuilder();
if(message.size() == 1) {
sb.append(message.getFirst().toJSON());
} else if(message.size() > 0) {
sb.append("{text=\"\",extra:[");
for(InteractiveMessagePart messagePart : message) {
sb.append(messagePart.toJSON());
sb.append(',');
}
sb.deleteCharAt(sb.length()-1);
sb.append("]}");
}
return sb.toString();
}
/**
* Parses the given FancyMessageFormat message to a String containing control characters
* for formatting that can be used for console outputs, but also for normal player
* messages.
* <p>
* The returned message will only contain colors, bold, italic, underlining and 'magic'
* characters. Hovers and other advanced tellraw tags will be skipped.
* @param message Input message split at line breaks.
* @return Plain message that can be send
*/
public static String convertToConsole(final String message) {
return convertToConsole(Collections.singleton(message));
}
/**
* Parses the given FancyMessageFormat message to a String containing control characters
* for formatting that can be used for console outputs, but also for normal player
* messages.
* <p>
* The returned message will only contain colors, bold, italic, underlining and 'magic'
* characters. Hovers and other advanced tellraw tags will be skipped.
* @param inputLines The raw message lines to process
* @return Plain message that can be send
*/
public static String convertToConsole(final Iterable<String> inputLines) {
if(inputLines == null) {
return null;
}
LinkedList<InteractiveMessagePart> parts = parse(inputLines, false);
StringBuilder result = new StringBuilder();
for(InteractiveMessagePart part : parts) {
result.append(part.toSimpleString());
}
return result.toString();
}
/**
* Insert a message at the specified position
* @param message The current message
* @param insert The message to insert
* @param line The line number to insert at
* @param start The start of the variable to replace
* @param end The end of the variable to replace
*/
public static void insertMessage(List<String> message, List<String> insert, int line, int start, int end) {
String lineContent = message.remove(line);
if(isTaggedInteractive(lineContent)) {
lineContent = lineContent.replace("", "");
message.add(line, lineContent.substring(0, start)+convertToConsole(insert)+lineContent.substring(end));
return;
}
// Find interactive lines meant for this message
List<String> interactives = new ArrayList<>();
while(line < message.size() && isTaggedInteractive(message.get(line))) {
interactives.add(message.remove(line));
}
// Split the line and add the parts
int at = line;
if(start > 0) {
message.add(line, lineContent.substring(0, start));
at++;
message.addAll(at, interactives);
at += interactives.size();
}
message.addAll(at, insert);
at += insert.size();
message.addAll(at, interactives);
at += interactives.size();
if(end < lineContent.length()) {
message.add(at, lineContent.substring(end));
at++;
message.addAll(at, interactives);
}
}
// ------------------------------------------------------------------------------------------
// ------------------------------- Private functions -------------------------------
// ------------------------------------------------------------------------------------------
/**
* <ul>
* <li>Splits lines at line breaks (creating a new line in the Array).
* <li>Removes empty lines at the beginning.
* <li>Removes lines with properties in front of the first text-line.
* </ul>
*/
private static ArrayList<String> cleanInputString(Iterable<String> inputLines) {
// Split lines at line breaks
// In the end we will have a list with one line per element
ArrayList<String> lines = new ArrayList<>();
for(String line : inputLines) {
lines.addAll(Arrays.asList(line.split("\\r?\\n")));
}
// Remove any special lines at the start (a real text line should be first)
while(!lines.isEmpty() && isTaggedInteractive(lines.get(0))) {
lines.remove(0);
}
return lines;
}
private static LinkedList<InteractiveMessagePart> parse(Iterable<String> inputLines) {
return parse(inputLines, true);
}
private static LinkedList<InteractiveMessagePart> parse(final Iterable<String> inputLines, boolean doInteractives) {
LinkedList<InteractiveMessagePart> message = new LinkedList<>();
Color currentColor = null;
Set<FormatType> currentFormatting = new HashSet<>();
lineLoop:
for(String line : inputLines) {
InteractiveMessagePart messagePart;
TaggedContent interactiveTag = getInteractiveTag(line);
boolean isTextLine = interactiveTag == null;
if(!doInteractives && !isTextLine) {
continue;
}
boolean isHoverLine = false;
if(isTextLine) {
messagePart = new InteractiveMessagePart();
message.add(messagePart);
} else /* if Interactive formatting */ {
if(message.isEmpty()) {
continue;
}
messagePart = message.getLast();
Tag tag = interactiveTag.tag;
if(tag instanceof ClickType) {
messagePart.clickType = (ClickType)interactiveTag.tag;
messagePart.clickContent = interactiveTag.subsequentContent;
} else if(tag instanceof HoverType) {
line = interactiveTag.subsequentContent;
isHoverLine = true;
if(messagePart.hoverType != tag) {
// Hover type changed
messagePart.hoverContent = new LinkedList<>();
messagePart.hoverType = (HoverType)tag;
}
// Add hover content below
}
}
if(isTextLine || isHoverLine) {
// Parse inline tags
Color currentLineColor = currentColor;
Set<FormatType> currentLineFormatting = currentFormatting;
LinkedList<TextMessagePart> targetList = messagePart.content;
boolean parseBreak = true;
if(isHoverLine) {
// Reset - use own
currentLineColor = null;
currentLineFormatting = new HashSet<>();
targetList = messagePart.hoverContent;
parseBreak = false;
// Add line break after previous hover line
if(!targetList.isEmpty()) {
targetList.getLast().text += '\n';
}
}
// Split into pieces at places where formatting changes
while(!line.isEmpty()) {
String textToAdd;
TaggedContent nextTag = getNextTag(line, parseBreak);
boolean tagged = nextTag != null;
if(!tagged) {
textToAdd = line;
line = "";
} else {
textToAdd = nextTag.precedingContent;
line = nextTag.subsequentContent;
}
// Add a text part with the correct formatting
if((tagged && nextTag.tag == ControlTag.BREAK) || !textToAdd.isEmpty()) {
TextMessagePart part = new TextMessagePart();
part.text = textToAdd;
part.formatTypes = new HashSet<>(currentLineFormatting);
part.color = currentLineColor;
targetList.add(part);
}
// Handle the change in formatting if a Tag has been detected (this needs to be after creating the InteractiveMessagePart)
if(tagged) {
// Handle the formatting tag
Tag tag = nextTag.tag;
if(tag instanceof Color) {
currentLineColor = (Color)tag;
} else if(tag instanceof FormatType) {
currentLineFormatting.add((FormatType)tag);
} else if(tag instanceof FormatCloseTag) {
currentLineFormatting.remove(((FormatCloseTag)tag).closes);
} else if(tag == ControlTag.BREAK) {
if(!targetList.isEmpty()) {
targetList.getLast().text += '\n';
}
continue lineLoop;
} else if(tag == ControlTag.RESET) {
currentLineFormatting.clear();
currentLineColor = Color.WHITE;
}
}
}
if(!isHoverLine) {
// Adapt global attributes
currentColor = currentLineColor;
if(messagePart.content.size() == 0) { // Prevent interactive parts without content
message.removeLast();
}
}
}
}
return message;
}
/**
* Searches and returns the first continuous tag found in the given String.
* @return The tag (plus its preceding and subsequent content) if found.
* Null if nothing is found.
*/
private static TaggedContent getNextTag(String line, boolean parseBreak) {
Pattern pattern = Pattern.compile("\\[[/a-zA-Z1-9]+\\]|&[1-9abcdeflonskr]");
Matcher matcher = pattern.matcher(line);
// TODO Fix for escape things, and something with parseBreak?
while(matcher.find()) {
Tag tag = null;
if(matcher.group().startsWith("&")) {
for(Color color : Color.class.getEnumConstants()) {
if(color.getNativeFormattingCode() == matcher.group().charAt(1)) {
tag = color;
}
}
for(FormatType format : FormatType.class.getEnumConstants()) {
if(format.getNativeFormattingCode() == matcher.group().charAt(1)) {
tag = format;
}
}
if(matcher.group().charAt(1) == SIMPLE_FORMAT_RESET_CHAR) {
tag = ControlTag.RESET;
}
} else {
tag = BRACKET_TAG_LIST.get(matcher.group().substring(1, matcher.group().length()-1).toLowerCase());
}
// Continue search if we found something like [abc] that is not a tag
if(tag != null) {
return new TaggedContent(line.substring(0, matcher.start()), tag, line.substring(matcher.end()));
}
}
return null;
/*
for(int startIndex = 0; startIndex < line.length(); startIndex++) {
int start = line.indexOf(TAG_BEFORE, startIndex);
if(start != -1) {
int end = line.indexOf(TAG_AFTER, start);
if(end != -1) {
String inBetween = line.substring(start+1, end).toLowerCase();
if(BRACKET_TAG_LIST.containsKey(inBetween)) {
Tag tag = BRACKET_TAG_LIST.get(inBetween);
if(tag == ControlTag.ESCAPE) {
// Ignore next char
line = line.substring(0, start)+line.substring(end+1);
startIndex = start;
} else if(!parseBreak && tag == ControlTag.ESCAPE) {
// Ignore break
startIndex = end+1;
} else {
String previousContent = line.substring(0, start);
String subsequentContent = line.substring(end+1);
return new TaggedContent(previousContent, tag, subsequentContent);
}
} else {
startIndex = start;
}
} else {
return null;
}
} else {
return null;
}
}
return null;
*/
}
/**
* If the given line defines an interactive property (e.g. "hover: myText")
* the tag / property will be returned. Otherwise null is returned.
*/
private static TaggedContent getInteractiveTag(String line) {
for(int index = 0; index < line.length(); index++) {
char c = line.charAt(index);
if(c == ' ' || c == '\t') {
// Ignore (Skip spacing)
} else {
int end = line.indexOf(": ", index);
if(end != -1) {
String inBetween = line.substring(index, end).toLowerCase();
if(INTERACTIVE_TAG_LIST.containsKey(inBetween)) {
Tag tag = INTERACTIVE_TAG_LIST.get(inBetween);
String subsequentContent = line.substring(end+2);
return new TaggedContent(null, tag, subsequentContent);
}
}
return null;
}
}
return null;
}
/**
* Check if a line is an advanced declaration like hover or command
* @param line The line to check
* @return true if the line is interactive, false when it is a text line
*/
public static boolean isTaggedInteractive(String line) {
return getInteractiveTag(line) != null;
}
/**
* Produce a string in double quotes with backslash sequences in all the
* right places.
* @param string A String
* @return A String correctly formatted for insertion in a JSON text.
*/
/*
* Copyright (c) 2002 JSON.org
* Licensed under the Apache License, Version 2.0
*/
private static String quoteStringJson(String string) {
if(string == null || string.length() == 0) {
return "\"\"";
}
char c;
int i;
int len = string.length();
StringBuilder sb = new StringBuilder(len+4);
String t;
sb.append('"');
for(i = 0; i < len; i += 1) {
c = string.charAt(i);
switch(c) {
case '\\':
case '"':
sb.append('\\');
sb.append(c);
break;
case '/':
sb.append('\\');
sb.append(c);
break;
case '\b':
sb.append("\\b");
break;
case '\t':
sb.append("\\t");
break;
case '\n':
sb.append("\\n");
break;
case '\f':
sb.append("\\f");
break;
case '\r':
sb.append("\\r");
break;
default:
if(c < ' ') {
t = "000"+Integer.toHexString(c);
sb.append("\\u");
sb.append(t.substring(t.length()-4));
} else {
sb.append(c);
}
}
}
sb.append('"');
return sb.toString();
}
// ------------------------------------------------------------------------------------------
// ------------------------------- Helper classes -------------------------------
// ------------------------------------------------------------------------------------------
private static class TaggedContent {
final String precedingContent;
final Tag tag;
final String subsequentContent;
public TaggedContent(String pre, Tag tag, String sub) {
this.precedingContent = pre;
this.tag = tag;
this.subsequentContent = sub;
}
}
/**
* Holds a string with basic (non-interactive) formatting.
*/
private static class TextMessagePart {
String text = "";
Color color = null;
Set<FormatType> formatTypes = new HashSet<>();
/**
* Get a simple colored/formatted string
* @return String with formatting
*/
String toSimpleString() {
StringBuilder sb = new StringBuilder();
// Color
if(color != null) {
sb.append(SIMPLE_FORMAT_PREFIX_CHAR);
sb.append(color.getNativeFormattingCode());
}
// Formatting
for(FormatType format : formatTypes) {
sb.append(SIMPLE_FORMAT_PREFIX_CHAR);
sb.append(format.getNativeFormattingCode());
}
// Text
sb.append(text);
return sb.toString();
}
/**
* Get a JSON component for this message part
* @return This part formatted in JSON
*/
String toJSON() {
StringBuilder sb = new StringBuilder();
sb.append('{');
sb.append("text:").append(quoteStringJson(text));
if(color != null && color != Color.WHITE) {
sb.append(",color:").append(color.jsonValue);
}
for(FormatType formatting : formatTypes) {
sb.append(',');
sb.append(formatting.jsonKey).append(':');
sb.append("true");
}
sb.append('}');
return sb.toString();
}
boolean hasFormatting() {
return !(color == Color.WHITE && formatTypes.isEmpty());
}
@Override
public String toString() {
return "TextMessagePart(text:"+text+", color:"+color+", formatting:"+formatTypes.toString()+")";
}
}
/**
* Holds a string with interactive formatting.
*/
private static class InteractiveMessagePart {
LinkedList<TextMessagePart> content = new LinkedList<>();
// Click
ClickType clickType = null;
String clickContent = "";
// Hover
HoverType hoverType = null;
LinkedList<TextMessagePart> hoverContent = null;
/**
* Get a simple colored/formatted string
* @return String with formatting
*/
String toSimpleString() {
StringBuilder sb = new StringBuilder();
for(TextMessagePart part : content) {
sb.append(part.toSimpleString());
}
return sb.toString();
}
/**
* Get a JSON component for this message part
* @return This part formatted in JSON
*/
String toJSON() {
StringBuilder sb = new StringBuilder();
if(content.size() == 1) {
// Add attributes to TextMessagePart object
sb.append(content.getFirst().toJSON());
sb.deleteCharAt(sb.length()-1);
} else {
sb.append('{');
sb.append("text=\"\",extra:[");
for(TextMessagePart textPart : content) {
sb.append(textPart.toJSON());
sb.append(',');
}
sb.deleteCharAt(sb.length()-1);
sb.append(']');
}
if(clickType != null) {
sb.append(',');
sb.append("clickEvent:{");
sb.append("action:").append(clickType.getJsonKey()).append(',');
sb.append("value:").append(quoteStringJson(clickContent));
sb.append('}');
}
if(hoverType != null) {
sb.append(',');
sb.append("hoverEvent:{");
sb.append("action:").append(hoverType.getJsonKey()).append(',');
sb.append("value:");
if(hoverContent.size() == 1) {
TextMessagePart hoverPart = hoverContent.getFirst();
if(hoverPart.hasFormatting()) {
sb.append(hoverPart.toJSON());
} else {
sb.append(quoteStringJson(hoverPart.text));
}
} else {
sb.append('[');
for(TextMessagePart hoverPart : hoverContent) {
sb.append(hoverPart.toJSON());
sb.append(',');
}
sb.deleteCharAt(sb.length()-1);
sb.append(']');
}
sb.append('}');
}
sb.append('}');
return sb.toString();
}
@Override
public String toString() {
return "InteractiveMessagePart(textMessageParts:"+content+", clickType:"+clickType+", clickContent:"+clickContent+", hoverType:"+hoverType+", hoverContent:"+hoverContent+")";
}
}
// --------------------------------------- Tags ---------------------------------------
interface Tag {
/**
* Tag text(s) used in the FancyMessageFormat (The text between '[' and ']')
*/
String[] getTags();
}
/**
* Indicates formatting that is applied until explicitly stopped.
* Can also be used in simple Minecraft messages (Non-JSON).
*/
interface ContinuousTag extends Tag {
/**
* The character that defines upcoming formatting in a native (non-JSON) Minecraft message.
*/
char getNativeFormattingCode();
}
/**
* Indicates formatting that allows cursor interaction. Requires the
* Minecraft JSON / tellraw format.
*/
interface InteractiveMessageTag extends Tag {
String getJsonKey();
}
enum Color implements ContinuousTag {
WHITE('f'),
BLACK('0'),
BLUE('9'),
DARK_BLUE('1', "darkblue"),
GREEN('a'),
DARK_GREEN('2', "darkgreen"),
AQUA('b'),
DARK_AQUA('3', "darkaqua"),
RED('c'),
DARK_RED('4', "darkred"),
LIGHT_PURPLE('d', "lightpurple"),
DARK_PURPLE('5', "darkpurple"),
YELLOW('e'),
GOLD('6'),
GRAY('7', "grey"),
DARK_GRAY('8', "dark_grey", "darkgray", "darkgrey");
final char bytecode;
final String jsonValue;
final String[] tags;
Color(char bytecode, String... extraTags) {
this.bytecode = bytecode;
this.jsonValue = this.name().toLowerCase();
this.tags = new String[extraTags.length+1];
this.tags[0] = this.name().toLowerCase();
System.arraycopy(extraTags, 0, this.tags, 1, extraTags.length);
}
@Override
public String[] getTags() {
return tags;
}
@Override
public char getNativeFormattingCode() {
return bytecode;
}
}
enum FormatType implements ContinuousTag {
BOLD('l', "bold", "b", "bold"),
ITALIC('o', "italic", "i", "italic"),
UNDERLINE('n', "underlined", "u", "underline"),
STRIKETHROUGH('s', "strikethrough", "s", "strike", "strikethrough"),
OBFUSCATE('k', "obfuscated", "obfuscate");
final char bytecode;
final String jsonKey;
final String[] tags;
FormatType(char bytecode, String jsonKey, String... tags) {
this.bytecode = bytecode;
this.jsonKey = jsonKey;
this.tags = tags;
}
@Override
public String[] getTags() {
return tags;
}
@Override
public char getNativeFormattingCode() {
return bytecode;
}
}
enum FormatCloseTag implements Tag {
BOLD_END(FormatType.BOLD),
ITALIC_END(FormatType.ITALIC),
UNDERLINE_END(FormatType.UNDERLINE),
STRIKETHROUGH_END(FormatType.STRIKETHROUGH),
OBFUSCATE_END(FormatType.OBFUSCATE);
/**
* Formatting that is stopped at this point
*/
final FormatType closes;
private final String[] tags;
FormatCloseTag(FormatType openingTag) {
this.closes = openingTag;
// Auto-generate close tags
tags = new String[closes.tags.length];
for(int i = 0; i < tags.length; i++) {
tags[i] = END_TAG_INDICATOR+closes.tags[i];
}
}
@Override
public String[] getTags() {
return tags;
}
}
enum ControlTag implements Tag {
BREAK("break"),
ESCAPE("esc"),
RESET("reset");
private final String[] tags;
ControlTag(String... tags) {
this.tags = tags;
}
@Override
public String[] getTags() {
return tags;
}
}
/**
* Types of clicking
*/
enum ClickType implements InteractiveMessageTag {
LINK("open_url", "link"),
COMMAND("run_command", "command"),
SUGGEST("suggest_command", "suggest");
private final String jsonKey;
private final String[] tags;
ClickType(String jsonKey, String... tags) {
this.jsonKey = jsonKey;
this.tags = tags;
}
@Override
public String[] getTags() {
return tags;
}
@Override
public String getJsonKey() {
return jsonKey;
}
}
enum HoverType implements InteractiveMessageTag {
HOVER;
@Override
public String[] getTags() {
return new String[]{"hover"};
}
@Override
public String getJsonKey() {
return "show_text";
}
}
}
@@ -1,100 +0,0 @@
package me.wiefferink.areashop.messages;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.lang.reflect.*;
import java.util.logging.Level;
/**
* Methods written by the Fanciful project (Github: https://github.com/mkremins/fanciful)
*/
public class FancyMessageSender {
private static Constructor<?> nmsPacketPlayOutChatConstructor;
public static boolean sendJSON(Player player, String jsonString) {
try {
Object handle = Reflection.getHandle(player);
Object connection = Reflection.getField(handle.getClass(), "playerConnection").get(handle);
Reflection.getMethod(connection.getClass(), "sendPacket", Reflection.getNMSClass("Packet")).invoke(connection, createChatPacket(jsonString));
return true;
} catch(IllegalArgumentException e) {
Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
} catch(IllegalAccessException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
} catch(InstantiationException e) {
Bukkit.getLogger().log(Level.WARNING, "Underlying class is abstract.", e);
} catch(InvocationTargetException e) {
Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e);
} catch(NoSuchMethodException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not find method.", e);
} catch(ClassNotFoundException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not find class.", e);
}
return false;
}
// The ChatSerializer's instance of Gson
private static Object nmsChatSerializerGsonInstance;
private static Method fromJsonMethod;
private static Object createChatPacket(String json) throws IllegalArgumentException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
if(nmsChatSerializerGsonInstance == null) {
// Find the field and its value, completely bypassing obfuscation
Class<?> chatSerializerClazz;
String version = Reflection.getVersion();
if(version == null || version.length() < 2) {
throw new RuntimeException("Could not get NMS version, found: "+version);
}
version = version.substring(1, version.length()-1); // Strip v and the . at the end
String[] parts = version.split("_");
if(parts.length < 3) {
throw new RuntimeException("Not enough parts in the version, found: "+version);
}
int majorVersion = Integer.parseInt(parts[0]);
int minorVersion = Integer.parseInt(parts[1]);
int revision = Integer.parseInt(parts[2].substring(1));
if((majorVersion <= 1 && minorVersion < 8) || (majorVersion == 1 && minorVersion == 8 && revision == 1)) {
chatSerializerClazz = Reflection.getNMSClass("ChatSerializer");
} else {
chatSerializerClazz = Reflection.getNMSClass("IChatBaseComponent$ChatSerializer");
}
if(chatSerializerClazz == null) {
throw new ClassNotFoundException("Can't find the ChatSerializer class");
}
for(Field declaredField : chatSerializerClazz.getDeclaredFields()) {
if(Modifier.isFinal(declaredField.getModifiers()) && Modifier.isStatic(declaredField.getModifiers()) && declaredField.getType().getName().endsWith("Gson")) {
// We've found our field
declaredField.setAccessible(true);
nmsChatSerializerGsonInstance = declaredField.get(null);
fromJsonMethod = nmsChatSerializerGsonInstance.getClass().getMethod("fromJson", String.class, Class.class);
break;
}
}
}
// Since the method is so simple, and all the obfuscated methods have the same name, it's easier to reimplement 'IChatBaseComponent a(String)' than to reflectively call it
// Of course, the implementation may change, but fuzzy matches might break with signature changes
Object serializedChatComponent = fromJsonMethod.invoke(nmsChatSerializerGsonInstance, json, Reflection.getNMSClass("IChatBaseComponent"));
if(nmsPacketPlayOutChatConstructor == null) {
try {
nmsPacketPlayOutChatConstructor = Reflection.getNMSClass("PacketPlayOutChat").getDeclaredConstructor(Reflection.getNMSClass("IChatBaseComponent"));
nmsPacketPlayOutChatConstructor.setAccessible(true);
} catch(NoSuchMethodException e) {
Bukkit.getLogger().log(Level.SEVERE, "Could not find Minecraft method or constructor.", e);
} catch(SecurityException e) {
Bukkit.getLogger().log(Level.WARNING, "Could not access constructor.", e);
}
}
return nmsPacketPlayOutChatConstructor.newInstance(serializedChatComponent);
}
}
@@ -1,163 +0,0 @@
package me.wiefferink.areashop.messages;
import com.google.common.base.Charsets;
import me.wiefferink.areashop.AreaShop;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.*;
import java.util.*;
public class LanguageManager {
private AreaShop plugin = null;
private String languages[] = {"EN", "NL"};
private Map<String, List<String>> currentLanguage, defaultLanguage;
/**
* Constructor
* @param plugin The AreaShop plugin
*/
public LanguageManager(AreaShop plugin) {
this.plugin = plugin;
startup();
}
/**
* Save the default language files and open the current and backup language file
*/
public void startup() {
this.saveDefaults();
currentLanguage = loadLanguage(plugin.getConfig().getString("language"));
defaultLanguage = loadLanguage(languages[0]);
}
/**
* Saves the default language files if not already present
*/
public void saveDefaults() {
// Create the language folder if it not exists
File langFolder;
langFolder = new File(plugin.getDataFolder()+File.separator+AreaShop.languageFolder);
if(!langFolder.exists()) {
if(!langFolder.mkdirs()) {
plugin.getLogger().warning("Could not create language directory: "+langFolder.getAbsolutePath());
return;
}
}
// Create the language files, overwrites if a file already exists
// Overriding is necessary because otherwise with an update the new lang
// files would not be used, when translating your own use another
// file name as the default
File langFile;
for(String language : languages) {
langFile = new File(plugin.getDataFolder()+File.separator+AreaShop.languageFolder+File.separator+language+".yml");
try(
InputStream input = plugin.getResource(AreaShop.languageFolder+"/"+language+".yml");
OutputStream output = new FileOutputStream(langFile)
) {
if(input == null) {
plugin.getLogger().warning("Could not save default language to the '"+AreaShop.languageFolder+"' folder: "+language+".yml");
continue;
}
int read;
byte[] bytes = new byte[1024];
while((read = input.read(bytes)) != -1) {
output.write(bytes, 0, read);
}
input.close();
output.close();
} catch(IOException e) {
plugin.getLogger().warning("Something went wrong saving a default language file: "+langFile.getPath());
}
}
}
/**
* Loads the specified language
* @param key The language to load
* @return Map with the messages loaded from the file
*/
public Map<String, List<String>> loadLanguage(String key) {
Map<String, List<String>> result = new HashMap<>();
// Load the strings
File file = new File(plugin.getDataFolder()+File.separator+AreaShop.languageFolder+File.separator+key+".yml");
try(
InputStreamReader reader = new InputStreamReader(new FileInputStream(file), Charsets.UTF_8)
) {
YamlConfiguration ymlFile = YamlConfiguration.loadConfiguration(reader);
if(ymlFile.getKeys(false).isEmpty()) {
plugin.getLogger().warning("Language file "+key+".yml has zero messages.");
return result;
}
for(String messageKey : ymlFile.getKeys(false)) {
if(ymlFile.isList(messageKey)) {
result.put(messageKey, new ArrayList<>(ymlFile.getStringList(messageKey)));
} else {
result.put(messageKey, new ArrayList<>(Collections.singletonList(ymlFile.getString(messageKey))));
}
}
} catch(IOException e) {
plugin.getLogger().warning("Could not load set language file: "+file.getAbsolutePath());
}
return result;
}
/**
* Get the message for a certain key, without doing any processing
* @param key The key of the message to get
* @return The message as a list of strings
*/
public List<String> getRawMessage(String key) {
List<String> message;
if(key.equalsIgnoreCase(Message.CHATLANGUAGEVARIABLE)) {
message = plugin.getChatPrefix();
} else if(currentLanguage.containsKey(key)) {
message = currentLanguage.get(key);
} else {
message = defaultLanguage.get(key);
}
if(message == null) {
return new ArrayList<>();
}
return new ArrayList<>(message);
}
}
@@ -1,390 +0,0 @@
package me.wiefferink.areashop.messages;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.regions.GeneralRegion;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Message {
public static final String VARIABLESTART = "%";
public static final String VARIABLEEND = "%";
public static final String LANGUAGEVARIABLE = "lang:";
public static final String CHATLANGUAGEVARIABLE = "prefix";
public static final int REPLACEMENTLIMIT = 50; // Maximum number of replacement rounds (the replaced value can have variables again)
public static final int MAXIMUMJSONLENGTH = 30000; // Limit of the client is 32767 for the complete message
private static boolean fancyWorks = true;
private List<String> message;
private Object[] replacements;
private String key = null;
/**
* Internal use only
*/
private Message() {
message = new ArrayList<>();
}
/**
* Empty message object
* @return this
*/
public static Message none() {
return new Message();
}
/**
* Construct a message from a language key
* @param key The key of the message to use
* @return this
*/
public static Message fromKey(String key) {
return new Message().setMessageFromKey(key);
}
/**
* Construct a message from a string
* @param message The message to use
* @return this
*/
public static Message fromString(String message) {
return new Message().setMessage(message);
}
/**
* Construct a message from a string list
* @param message The message to use
* @return this
*/
public static Message fromList(List<String> message) {
return new Message().setMessage(message);
}
/**
* Get the message with all replacements done
* @return Message as a list
*/
public List<String> get() {
executeReplacements();
return message;
}
/**
* Get the message with all replacements done
* @param limit the limit to hold to
* @return Message as a list
*/
private List<String> get(Limit limit) {
executeReplacements(limit);
return message;
}
/**
* Get the raw message without replacing anything
* @return The message
*/
public List<String> getRaw() {
return message;
}
/**
* Get a plain string for the message (for example for using in the console)
* @return The message as simple string
*/
public String getPlain() {
executeReplacements();
return FancyMessageFormat.convertToConsole(message);
}
/**
* Add the default prefix to the message
* @param doIt true if the prefix should be added, otherwise false
* @return this
*/
public Message prefix(boolean doIt) {
if(doIt) {
message.add(0, VARIABLESTART+LANGUAGEVARIABLE+CHATLANGUAGEVARIABLE+VARIABLEEND);
}
return this;
}
public Message prefix() {
return prefix(true);
}
/**
* Set the replacements to apply to the message
* @param replacements The replacements to apply
* - GeneralRegion: All region replacements are applied
* - Message: Message is inserted
* - other: index tag is replaced, like %0%
* @return this
*/
public Message replacements(Object... replacements) {
this.replacements = replacements;
return this;
}
/**
* Append lines to the message
* @param lines The lines to append
* @return this
*/
public Message append(List<String> lines) {
message.addAll(lines);
return this;
}
/**
* Append a message to this message
* @param message The message to append
* @return this
*/
public Message append(Message message) {
return append(message.get());
}
/**
* Append lines to the message
* @param line The line to append
* @return this
*/
public Message append(String line) {
message.add(line);
return this;
}
/**
* Send the message to a target
* @param target The target to send the message to (Player, CommandSender, Logger)
* @return this
*/
public Message send(Object target) {
if(message == null || message.size() == 0 || (message.size() == 1 && message.get(0).length() == 0) || target == null) {
return this;
}
executeReplacements();
if(target instanceof Player) {
boolean sendPlain = true;
if(AreaShop.getInstance().getConfig().getBoolean("useFancyMessages") && fancyWorks) {
try {
String jsonString = FancyMessageFormat.convertToJSON(message);
if(jsonString.length() > MAXIMUMJSONLENGTH) {
AreaShop.getInstance().getLogger().severe("Message with key "+key+" could not be send, results in a JSON string that is too big to send to the client, start of the message: "+Utils.getMessageStart(this, 100));
return this;
}
boolean result = FancyMessageSender.sendJSON((Player)target, jsonString);
sendPlain = !result;
fancyWorks = result;
} catch(Exception e) {
fancyWorks = false;
AreaShop.getInstance().getLogger().warning("Sending fancy message did not work, falling back to plain messages. Message key: "+key);
}
}
if(sendPlain) { // Fancy messages disabled or broken
((Player)target).sendMessage(FancyMessageFormat.convertToConsole(message));
}
} else {
String plainMessage = FancyMessageFormat.convertToConsole(message);
if(!AreaShop.getInstance().getConfig().getBoolean("useColorsInConsole")) {
plainMessage = ChatColor.stripColor(plainMessage);
}
if(target instanceof CommandSender) {
((CommandSender)target).sendMessage(plainMessage);
} else if(target instanceof Logger) {
((Logger)target).info(plainMessage);
} else {
AreaShop.getInstance().getLogger().warning("Could not send message, target is wrong: "+plainMessage);
}
}
return this;
}
// INTERNAL METHODS
/**
* Set the internal message
* @param message The message to set
* @return this
*/
private Message setMessage(List<String> message) {
this.message = message;
if(this.message == null) {
this.message = new ArrayList<>();
}
return this;
}
/**
* Set the internal message with a key
* @param key The message key to get the message for
* @return this
*/
private Message setMessageFromKey(String key) {
this.key = key;
return this.setMessage(AreaShop.getInstance().getLanguageManager().getRawMessage(key));
}
/**
* Set the internal message with a string
* @param message The message to set
* @return this
*/
private Message setMessage(String message) {
List<String> list = new ArrayList<>();
list.add(message);
return this.setMessage(list);
}
/**
* Apply all replacements to the message
*/
private void executeReplacements() {
executeReplacements(new Limit(REPLACEMENTLIMIT, this));
}
private void executeReplacements(Limit limit) {
// Replace variables until they are all gone, or when the limit is reached
Pattern variable = Pattern.compile(Pattern.quote(VARIABLESTART)+"[a-zA-Z]+"+Pattern.quote(VARIABLEEND));
try {
boolean shouldReplace = true;
while(shouldReplace) {
List<String> original = new ArrayList<>(message);
limit.left--;
if(limit.reached()) {
if(!limit.notified) {
limit.notified = true;
AreaShop.getInstance().getLogger().severe("Reached replacement limit, probably has replacements loops, problematic message key: "+limit.message.key+", first characters of the message: "+Utils.getMessageStart(limit.message, 100));
}
break;
}
replaceArgumentVariables(limit);
replaceLanguageVariables(limit);
shouldReplace = !message.equals(original);
}
} catch(StackOverflowError e) {
limit.left = 0;
limit.notified = true;
AreaShop.getInstance().getLogger().severe("Too many recursive replacements for message with key: "+limit.message.key+" (probably includes itself as replacement), start of the message: "+Utils.getMessageStart(limit.message, 100));
}
}
/**
* Replace argument variables in a message
* The arguments to apply as replacements:
* - If it is a GeneralRegion the replacements of the region will be applied
* - Else the parameter will replace its number surrounded with VARIABLESTART and VARIABLEEND
*/
private void replaceArgumentVariables(Limit limit) {
if(message == null || message.size() == 0 || replacements == null) {
return;
}
boolean result = false;
for(int i = 0; i < message.size(); i++) {
int number = 0;
for(Object param : replacements) {
if(param != null) {
if(param instanceof GeneralRegion) {
message.set(i, ((GeneralRegion)param).applyAllReplacements(message.get(i)));
} else if(param instanceof Message) {
Pattern variables = Pattern.compile(Pattern.quote(VARIABLESTART)+number+Pattern.quote(VARIABLEEND));
Matcher matches = variables.matcher(message.get(i));
if(matches.find()) {
FancyMessageFormat.insertMessage(message, ((Message)param).get(limit), i, matches.start(), matches.end());
}
number++;
} else {
message.set(i, message.get(i).replace(VARIABLESTART+number+VARIABLEEND, param.toString()));
number++;
}
}
}
}
}
/**
* Replace all language variables in a message
*/
private void replaceLanguageVariables(Limit limit) {
if(message == null || message.size() == 0) {
return;
}
Pattern variables = Pattern.compile(
Pattern.quote(VARIABLESTART)+
Pattern.quote(LANGUAGEVARIABLE)+"[a-zA-Z-]+"+ // Language key
"(\\|(.*?\\|)+)?"+ // Optional message arguments
Pattern.quote(VARIABLEEND)
);
for(int i = 0; i < message.size(); i++) {
Matcher matches = variables.matcher(message.get(i));
if(matches.find()) {
String variable = matches.group();
String key;
Object[] arguments = null;
if(variable.contains("|")) {
key = variable.substring(6, variable.indexOf("|"));
arguments = variable.substring(variable.indexOf("|")+1, variable.length()-1).split("\\|");
} else {
key = variable.substring(6, variable.length()-1);
}
Message insert = Message.fromKey(key);
if(arguments != null) {
insert.replacements(arguments);
}
// Insert message
int startDiff = message.size()-i;
FancyMessageFormat.insertMessage(message, insert.get(limit), i, matches.start(), matches.end());
// Skip to end of insert, language tags already replaced
i = message.size()-startDiff;
}
}
}
@Override
public String toString() {
return "Message(key:"+key+", message:"+message+")";
}
/**
* Class to store a limit
*/
private class Limit {
public int left = 0;
public boolean notified = false;
public Message message;
/**
* Set the initial limit
* @param count The limit to use
*/
public Limit(int count, Message message) {
left = count;
this.message = message;
}
/**
* Check if the limit is reached
* @return true if the limit is reached, otherwise false
*/
public boolean reached() {
return left <= 0;
}
}
}
@@ -1,219 +0,0 @@
package me.wiefferink.areashop.messages;
import org.bukkit.Bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Class made by glan3b (Github: https://github.com/glen3b) for the Fanciful project (Github: https://github.com/mkremins/fanciful)
*/
/**
* A class containing static utility methods and caches which are intended as reflective conveniences.
* Unless otherwise noted, upon failure methods will return {@code null}.
*/
public final class Reflection {
private static String _versionString;
private Reflection() {
}
/**
* Gets the version string from the package name of the CraftBukkit server implementation.
* This is needed to bypass the JAR package name changing on each update.
* @return The version string of the OBC and NMS packages, <em>including the trailing dot</em>.
*/
public synchronized static String getVersion() {
if(_versionString == null) {
if(Bukkit.getServer() == null) {
// The server hasn't started, static initializer call?
return null;
}
String name = Bukkit.getServer().getClass().getPackage().getName();
_versionString = name.substring(name.lastIndexOf('.')+1)+".";
}
return _versionString;
}
/**
* Stores loaded classes from the {@code net.minecraft.server} package.
*/
private static final Map<String, Class<?>> _loadedNMSClasses = new HashMap<String, Class<?>>();
/**
* Stores loaded classes from the {@code org.bukkit.craftbukkit} package (and subpackages).
*/
private static final Map<String, Class<?>> _loadedOBCClasses = new HashMap<String, Class<?>>();
/**
* Gets a {@link Class} object representing a type contained within the {@code net.minecraft.server} versioned package.
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
* @param className The name of the class, excluding the package, within NMS.
* @return The class instance representing the specified NMS class, or {@code null} if it could not be loaded.
*/
public synchronized static Class<?> getNMSClass(String className) {
if(_loadedNMSClasses.containsKey(className)) {
return _loadedNMSClasses.get(className);
}
String fullName = "net.minecraft.server."+getVersion()+className;
Class<?> clazz = null;
try {
clazz = Class.forName(fullName);
} catch(Exception e) {
e.printStackTrace();
_loadedNMSClasses.put(className, null);
return null;
}
_loadedNMSClasses.put(className, clazz);
return clazz;
}
/**
* Gets a {@link Class} object representing a type contained within the {@code org.bukkit.craftbukkit} versioned package.
* The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously).
* @param className The name of the class, excluding the package, within OBC. This name may contain a subpackage name, such as {@code inventory.CraftItemStack}.
* @return The class instance representing the specified OBC class, or {@code null} if it could not be loaded.
*/
public synchronized static Class<?> getOBCClass(String className) {
if(_loadedOBCClasses.containsKey(className)) {
return _loadedOBCClasses.get(className);
}
String fullName = "org.bukkit.craftbukkit."+getVersion()+className;
Class<?> clazz = null;
try {
clazz = Class.forName(fullName);
} catch(Exception e) {
e.printStackTrace();
_loadedOBCClasses.put(className, null);
return null;
}
_loadedOBCClasses.put(className, clazz);
return clazz;
}
/**
* Attempts to get the NMS handle of a CraftBukkit object.
* <p>
* The only match currently attempted by this method is a retrieval by using a parameterless {@code getHandle()} method implemented by the runtime type of the specified object.
* </p>
* @param obj The object for which to retrieve an NMS handle.
* @return The NMS handle of the specified object, or {@code null} if it could not be retrieved using {@code getHandle()}.
*/
public synchronized static Object getHandle(Object obj) {
try {
return getMethod(obj.getClass(), "getHandle").invoke(obj);
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
private static final Map<Class<?>, Map<String, Field>> _loadedFields = new HashMap<Class<?>, Map<String, Field>>();
/**
* Retrieves a {@link Field} instance declared by the specified class with the specified name.
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
* returned will be an instance or static field.
* <p>
* A global caching mechanism within this class is used to store fields. Combined with synchronization, this guarantees that
* no field will be reflectively looked up twice.
* </p>
* <p>
* If a field is deemed suitable for return, {@link Field#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
* </p>
* @param clazz The class which contains the field to retrieve.
* @param name The declared name of the field in the class.
* @return A field object with the specified name declared by the specified class.
* @see Class#getDeclaredField(String)
*/
public synchronized static Field getField(Class<?> clazz, String name) {
Map<String, Field> loaded;
if(!_loadedFields.containsKey(clazz)) {
loaded = new HashMap<String, Field>();
_loadedFields.put(clazz, loaded);
} else {
loaded = _loadedFields.get(clazz);
}
if(loaded.containsKey(name)) {
// If the field is loaded (or cached as not existing), return the relevant value, which might be null
return loaded.get(name);
}
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
loaded.put(name, field);
return field;
} catch(Exception e) {
// Error loading
e.printStackTrace();
// Cache field as not existing
loaded.put(name, null);
return null;
}
}
/**
* Contains loaded methods in a cache.
* The map maps [types to maps of [method names to maps of [parameter types to method instances]]].
*/
private static final Map<Class<?>, Map<String, Map<ArrayWrapper<Class<?>>, Method>>> _loadedMethods = new HashMap<Class<?>, Map<String, Map<ArrayWrapper<Class<?>>, Method>>>();
/**
* Retrieves a {@link Method} instance declared by the specified class with the specified name and argument types.
* Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field
* returned will be an instance or static field.
* <p>
* A global caching mechanism within this class is used to store method. Combined with synchronization, this guarantees that
* no method will be reflectively looked up twice.
* </p>
* <p>
* If a method is deemed suitable for return, {@link Method#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned.
* This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance.
* </p>
* <p>
* This method does <em>not</em> search superclasses of the specified type for methods with the specified signature.
* Callers wishing this behavior should use {@link Class#getDeclaredMethod(String, Class...)}.
* @param clazz The class which contains the method to retrieve.
* @param name The declared name of the method in the class.
* @param args The formal argument types of the method.
* @return A method object with the specified name declared by the specified class.
*/
public synchronized static Method getMethod(Class<?> clazz, String name,
Class<?>... args) {
if(!_loadedMethods.containsKey(clazz)) {
_loadedMethods.put(clazz, new HashMap<String, Map<ArrayWrapper<Class<?>>, Method>>());
}
Map<String, Map<ArrayWrapper<Class<?>>, Method>> loadedMethodNames = _loadedMethods.get(clazz);
if(!loadedMethodNames.containsKey(name)) {
loadedMethodNames.put(name, new HashMap<ArrayWrapper<Class<?>>, Method>());
}
Map<ArrayWrapper<Class<?>>, Method> loadedSignatures = loadedMethodNames.get(name);
ArrayWrapper<Class<?>> wrappedArg = new ArrayWrapper<Class<?>>(args);
if(loadedSignatures.containsKey(wrappedArg)) {
return loadedSignatures.get(wrappedArg);
}
for(Method m : clazz.getMethods()) {
if(m.getName().equals(name) && Arrays.equals(args, m.getParameterTypes())) {
m.setAccessible(true);
loadedSignatures.put(wrappedArg, m);
return m;
}
}
loadedSignatures.put(wrappedArg, null);
return null;
}
}
@@ -1,13 +1,13 @@
package me.wiefferink.areashop.regions;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.Utils;
import me.wiefferink.areashop.events.ask.BuyingRegionEvent;
import me.wiefferink.areashop.events.ask.ResellingRegionEvent;
import me.wiefferink.areashop.events.ask.SellingRegionEvent;
import me.wiefferink.areashop.events.notify.BoughtRegionEvent;
import me.wiefferink.areashop.events.notify.ResoldRegionEvent;
import me.wiefferink.areashop.events.notify.SoldRegionEvent;
import me.wiefferink.areashop.tools.Utils;
import net.milkbowl.vault.economy.EconomyResponse;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
@@ -16,538 +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.HashMap;
import java.util.UUID;
public class BuyRegion extends GeneralRegion {
public class BuyRegion extends TransactionalRegion {
public BuyRegion(AreaShop plugin, YamlConfiguration config) {
super(plugin, config);
}
public BuyRegion(AreaShop plugin, String name, World world) {
super(plugin, name, world);
}
@Override
public RegionType getType() {
return RegionType.BUY;
}
@Override
public RegionState getState() {
if(isSold() && isInResellingMode()) {
return RegionState.RESELL;
} else if(isSold() && !isInResellingMode()) {
return RegionState.SOLD;
} else {
return RegionState.FORSALE;
}
}
/**
* 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;
}
/**
* 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(Player player) {
return player != null && isBuyer(player.getUniqueId());
}
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 == null || result.isEmpty()) {
result = getStringSetting("buy.buyerName");
if(result == null || result.isEmpty()) {
result = "<UNKNOWN>";
}
}
return result;
}
/**
* Check if the region is sold
* @return true if the region is sold, otherwise false
*/
public boolean isSold() {
return getBuyer() != null;
}
/**
* 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 price of the region
* @return The price of the region
*/
public double getPrice() {
return getDoubleSetting("buy.price");
}
/**
* Get the resell price of this region
* @return The resell price if isInResellingMode(), otherwise 0.0
*/
public double getResellPrice() {
return config.getDouble("buy.resellPrice");
}
/**
* 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 formatted string of the resellprice (includes prefix and suffix)
* @return The formatted string of the resellprice
*/
public String getFormattedResellPrice() {
return Utils.formatCurrency(getResellPrice());
}
/**
* Change the price of the region
* @param price The price to set this region to
*/
public void setPrice(double price) {
setSetting("buy.price", price);
}
/**
* Remove the price so that the price will be taken from a group or the default.yml file
*/
public void removePrice() {
setSetting("buy.price", null);
}
/**
* 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);
}
/**
* Stop this region from being in resell mode
*/
public void disableReselling() {
setSetting("buy.resellMode", null);
setSetting("buy.resellPrice", null);
}
/**
* Get the moneyBack percentage
* @return The % of money the player will get back when selling
*/
public double getMoneyBackPercentage() {
return getDoubleSetting("buy.moneyBack");
}
/**
* 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);
}
/**
* 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 HashMap<String, Object> getSpecificReplacements() {
// Fill the replacements map with things specific to a BuyRegion
HashMap<String, Object> result = new HashMap<>();
result.put(AreaShop.tagPrice, getFormattedPrice());
result.put(AreaShop.tagRawPrice, getPrice());
result.put(AreaShop.tagPlayerName, getPlayerName());
result.put(AreaShop.tagPlayerUUID, getBuyer());
result.put(AreaShop.tagResellPrice, getFormattedResellPrice());
result.put(AreaShop.tagRawResellPrice, getResellPrice());
result.put(AreaShop.tagMoneyBackAmount, getFormattedMoneyBackAmount());
result.put(AreaShop.tagRawMoneyBackAmount, getMoneyBackAmount());
double moneyBackPercent = getMoneyBackPercentage();
if((moneyBackPercent%1.0) == 0.0) {
result.put(AreaShop.tagMoneyBackPercentage, (int)moneyBackPercent);
} else {
result.put(AreaShop.tagMoneyBackPercentage, moneyBackPercent);
}
result.put(AreaShop.tagMaxInactiveTime, this.getFormattedInactiveTimeUntilSell());
return result;
}
/**
* 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"));
}
/**
* 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());
}
/**
* Buy a region
* @param player The player that wants to buy the region
* @return true if it succeeded and false if not
*/
@SuppressWarnings("deprecation")
public boolean buy(Player player) {
// Check if the player has permission
if(player.hasPermission("areashop.buy")) {
if(plugin.getEconomy() == null) {
message(player, "general-noEconomy");
return false;
}
if(isInResellingMode()) {
if(!player.hasPermission("areashop.buyresell")) {
message(player, "buy-noPermissionResell");
return false;
}
} else {
if(!player.hasPermission("areashop.buynormal")) {
message(player, "buy-noPermissionNoResell");
return false;
}
}
if(getWorld() == null) {
message(player, "general-noWorld");
return false;
}
if(getRegion() == null) {
message(player, "general-noRegion");
return false;
}
if(!isSold() || (isInResellingMode() && !isBuyer(player))) {
boolean isResell = isInResellingMode();
// Check if the players needs to be in the world or region for buying
if(restrictedToRegion() && (!player.getWorld().getName().equals(getWorldName())
|| !getRegion().contains(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ()))) {
message(player, "buy-restrictedToRegion");
return false;
}
if(restrictedToWorld() && !player.getWorld().getName().equals(getWorldName())) {
message(player, "buy-restrictedToWorld", player.getWorld().getName());
return false;
}
// Check region limits
LimitResult limitResult = this.limitsAllow(RegionType.BUY, player);
AreaShop.debug("LimitResult: " + limitResult.toString());
if(!limitResult.actionAllowed()) {
if(limitResult.getLimitingFactor() == LimitType.TOTAL) {
message(player, "total-maximum", limitResult.getMaximum(), limitResult.getCurrent(), limitResult.getLimitingGroup());
return false;
}
if(limitResult.getLimitingFactor() == LimitType.BUYS) {
message(player, "buy-maximum", limitResult.getMaximum(), limitResult.getCurrent(), limitResult.getLimitingGroup());
return false;
}
// Should not be reached, but is safe like this
return false;
}
private static final String UNKNOWN_BUYER = "<UNKNOWN>";
// Check if the player has enough money
if((!isResell && plugin.getEconomy().has(player, getWorldName(), getPrice())) || (isResell && plugin.getEconomy().has(player, getWorldName(), getResellPrice()))) {
UUID oldOwner = getBuyer();
if(isResell && oldOwner != null) {
// Broadcast and check event
ResellingRegionEvent event = new ResellingRegionEvent(this, player);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
message(player, "general-cancelled", event.getReason());
return false;
}
public BuyRegion(YamlConfiguration config) {
super(config, RegionType.BUY);
}
getFriendsFeature().clearFriends();
double resellPrice = getResellPrice();
// Transfer the money to the previous owner
EconomyResponse r = plugin.getEconomy().withdrawPlayer(player, getWorldName(), getResellPrice());
if(!r.transactionSuccess()) {
message(player, "buy-payError");
AreaShop.debug("Something went wrong with getting money from " + player.getName() + " while buying " + getName() + ": " + r.errorMessage);
return false;
}
r = null;
OfflinePlayer oldOwnerPlayer = Bukkit.getOfflinePlayer(oldOwner);
String oldOwnerName = getPlayerName();
if(oldOwnerPlayer != null && oldOwnerPlayer.getName() != null) {
r = plugin.getEconomy().depositPlayer(oldOwnerPlayer, getWorldName(), getResellPrice());
oldOwnerName = oldOwnerPlayer.getName();
} else if (oldOwnerName != null) {
r = plugin.getEconomy().depositPlayer(oldOwnerName, getWorldName(), getResellPrice());
}
if(r == null || !r.transactionSuccess()) {
plugin.getLogger().warning("Something went wrong with paying '" + oldOwnerName + "' " + getFormattedPrice() + " for his resell of region " + getName() + " to " + player.getName());
}
// Resell is done, disable that now
disableReselling();
// Run commands
this.runEventCommands(RegionEvent.RESELL, true);
// Set the owner
setBuyer(player.getUniqueId());
updateLastActiveTime();
public BuyRegion(String name, World world) {
super(name, world, RegionType.BUY);
}
// Update everything
handleSchematicEvent(RegionEvent.RESELL);
@Override
public RegionState getState() {
if (isSold() && isInResellingMode()) {
return RegionState.RESELL;
} else if (isSold() && !isInResellingMode()) {
return RegionState.SOLD;
} else {
return RegionState.FORSALE;
}
}
// Notify about updates
this.notifyAndUpdate(new ResoldRegionEvent(this, oldOwner));
/**
* 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;
}
// Send message to the player
message(player, "buy-successResale", oldOwnerName);
Player seller = Bukkit.getPlayer(oldOwner);
if(seller != null) {
message(seller, "buy-successSeller", resellPrice);
}
// Run commands
this.runEventCommands(RegionEvent.RESELL, false);
} else {
// Broadcast and check event
BuyingRegionEvent event = new BuyingRegionEvent(this, player);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
message(player, "general-cancelled", event.getReason());
return false;
}
/**
* 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);
}
// Substract the money from the players balance
EconomyResponse r = plugin.getEconomy().withdrawPlayer(player, getWorldName(), getPrice());
if(!r.transactionSuccess()) {
message(player, "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()) {
plugin.getLogger().warning("Something went wrong with paying '" + landlordName + "' " + getFormattedPrice() + " for his sell of region " + getName() + " to " + player.getName());
}
}
/**
* 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());
}
// Run commands
this.runEventCommands(RegionEvent.BOUGHT, true);
// Set the owner
setBuyer(player.getUniqueId());
updateLastActiveTime();
@Deprecated
public boolean isBuyer(UUID player) {
UUID buyer = getBuyer();
return !(buyer == null || player == null) && buyer.equals(player);
}
// Notify about updates
this.notifyAndUpdate(new BoughtRegionEvent(this));
/**
* 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;
}
// Update everything
handleSchematicEvent(RegionEvent.BOUGHT);
/**
* 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");
}
// Send message to the player
message(player, "buy-succes");
// Run commands
this.runEventCommands(RegionEvent.BOUGHT, false);
}
return true;
} else {
// Player has not enough money
if(isResell) {
message(player, "buy-lowMoneyResell", Utils.formatCurrency(plugin.getEconomy().getBalance(player, getWorldName())));
} else {
message(player, "buy-lowMoney", Utils.formatCurrency(plugin.getEconomy().getBalance(player, getWorldName())));
}
}
} else {
if(isBuyer(player)) {
message(player, "buy-yours");
} else {
message(player, "buy-someoneElse");
}
}
} else {
message(player, "buy-noPermission");
}
return false;
}
/**
* 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 != null && 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;
}
}
/**
* 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"));
}
if(plugin.getEconomy() == null) {
return false;
}
/**
* 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());
}
// Broadcast and check event
SellingRegionEvent event = new SellingRegionEvent(this);
Bukkit.getPluginManager().callEvent(event);
if(event.isCancelled()) {
message(executor, "general-cancelled", event.getReason());
return false;
}
/**
* 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);
}
// Run commands
this.runEventCommands(RegionEvent.SOLD, true);
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()) {
plugin.getLogger().warning("Something went wrong with paying back money to " + getPlayerName() + " while selling region " + getName());
}
}
}
/**
* Stop this region from being in resell mode.
*/
public void disableReselling() {
setSetting("buy.resellMode", null);
setSetting("buy.resellPrice", null);
}
message(executor, "sell-sold");
@Override
public Object provideReplacement(String variable) {
switch (variable) {
case AreaShop.tagResellPrice:
return getFormattedResellPrice();
case AreaShop.tagRawResellPrice:
return getResellPrice();
default:
return super.provideReplacement(variable);
}
}
// Remove friends and the owner
getFriendsFeature().clearFriends();
UUID oldBuyer = getBuyer();
setBuyer(null);
removeLastActiveTime();
/**
* 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"));
}
// Notify about updates
this.notifyAndUpdate(new SoldRegionEvent(this, oldBuyer, Math.max(moneyBack, 0)));
/**
* 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());
}
// Update everything
handleSchematicEvent(RegionEvent.SOLD);
/**
* 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;
}
// Run commands
this.runEventCommands(RegionEvent.SOLD, false);
return true;
}
if (plugin.getEconomy() == null) {
message(offlinePlayer, "general-noEconomy");
return false;
}
@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)) {
plugin.getLogger().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;
}
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;
}
}
if (getWorld() == null) {
message(offlinePlayer, "general-noWorld");
return false;
}
if (getRegion() == null) {
message(offlinePlayer, "general-noRegion");
return false;
}
if (isSold() && !(isInResellingMode() && !isBuyer(offlinePlayer))) {
if (isBuyer(offlinePlayer)) {
message(offlinePlayer, "buy-yours");
} else {
message(offlinePlayer, "buy-someoneElse");
}
return false;
}
boolean isResell = isInResellingMode();
// 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;
}
}
// 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;
}
// 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;
}
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;
}
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();
// Set the owner
setBuyer(offlinePlayer.getUniqueId());
updateLastActiveTime();
// Update everything
handleSchematicEvent(RegionEvent.RESELL);
// Notify about updates
this.notifyAndUpdate(new ResoldRegionEvent(this, oldOwner));
// 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;
}
// 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());
}
}
// Set the owner
setBuyer(offlinePlayer.getUniqueId());
updateLastActiveTime();
// Send message to the player
message(offlinePlayer, "buy-succes");
// Update everything
handleSchematicEvent(RegionEvent.BOUGHT);
// Notify about updates
this.notifyAndUpdate(new BoughtRegionEvent(this));
}
return true;
}
/**
* 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;
}
}
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(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());
}
}
}
// 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 handleInactivity() {
return this.sell(true, null);
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,7 @@
# Regions
This package contains the `GeneralRegion` class which represents a region in AreaShop. Such a region is linked to a WorldGuard region and has its own configuration file at `/plugins/AreaShop/regions/<name>.yml`.
The `BuyRegion` and `RentRegion` classes represent buy and rental regions (mutually exclusive) and inherit from `GeneralRegion`.
To group regions there is `RegionGroup`, which allows configuring settings for a lot of regions at once.
Loaded 100 of 163 files, more files were not shown because too many files have changed in this diff. Show more