add code
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'net.ltgt.apt' version '0.10'
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'sh.okx'
|
||||||
|
version '3.0-alpha'
|
||||||
|
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
maven {
|
||||||
|
url 'http://nexus.hc.to/content/repositories/pub_releases'
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
url 'https://hub.spigotmc.org/nexus/content/groups/public/'
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
url 'http://repo.extendedclip.com/content/repositories/placeholderapi/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||||
|
testCompile 'org.mockito:mockito-core:2.+'
|
||||||
|
|
||||||
|
compileOnly 'org.projectlombok:lombok:1.18.2'
|
||||||
|
apt "org.projectlombok:lombok:1.18.2"
|
||||||
|
|
||||||
|
compile 'org.spigotmc:spigot-api:1.13-R0.1-SNAPSHOT'
|
||||||
|
compile('net.milkbowl.vault:VaultAPI:1.6') {
|
||||||
|
exclude group: 'org.bukkit'
|
||||||
|
}
|
||||||
|
compile 'me.clip:placeholderapi:2.9.+'
|
||||||
|
}
|
||||||
Vendored
BIN
Binary file not shown.
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#Tue Aug 21 18:52:26 BST 2018
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
Vendored
+84
@@ -0,0 +1,84 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
rootProject.name = 'Rankup'
|
||||||
|
|
||||||
@@ -0,0 +1,926 @@
|
|||||||
|
package sh.okx.rankup;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.plugin.ServicePriority;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.json.simple.JSONArray;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bStats collects some data for plugin authors.
|
||||||
|
* <p>
|
||||||
|
* Check out https://bStats.org/ to learn more about bStats!
|
||||||
|
*/
|
||||||
|
public class Metrics {
|
||||||
|
|
||||||
|
// The version of this bStats class
|
||||||
|
public static final int B_STATS_VERSION = 1;
|
||||||
|
|
||||||
|
// The url to which the data is sent
|
||||||
|
private static final String URL = "https://bStats.org/submitData";
|
||||||
|
|
||||||
|
// Should failed requests be logged?
|
||||||
|
private static boolean logFailedRequests;
|
||||||
|
|
||||||
|
// The uuid of the server
|
||||||
|
private static String serverUUID;
|
||||||
|
|
||||||
|
// The plugin
|
||||||
|
private final JavaPlugin plugin;
|
||||||
|
|
||||||
|
// A list with all custom charts
|
||||||
|
private final List<CustomChart> charts = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param plugin The plugin which stats should be submitted.
|
||||||
|
*/
|
||||||
|
public Metrics(JavaPlugin plugin) {
|
||||||
|
if (plugin == null) {
|
||||||
|
throw new IllegalArgumentException("Plugin cannot be null!");
|
||||||
|
}
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
// Get the config file
|
||||||
|
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||||
|
File configFile = new File(bStatsFolder, "config.yml");
|
||||||
|
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
|
|
||||||
|
// Check if the config file exists
|
||||||
|
if (!config.isSet("serverUuid")) {
|
||||||
|
|
||||||
|
// Add default values
|
||||||
|
config.addDefault("enabled", true);
|
||||||
|
// Every server gets it's unique random id.
|
||||||
|
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||||
|
// Should failed request be logged?
|
||||||
|
config.addDefault("logFailedRequests", false);
|
||||||
|
|
||||||
|
// Inform the server owners about bStats
|
||||||
|
config.options().header(
|
||||||
|
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
|
||||||
|
"To honor their work, you should not disable it.\n" +
|
||||||
|
"This has nearly no effect on the server performance!\n" +
|
||||||
|
"Check out https://bStats.org/ to learn more :)"
|
||||||
|
).copyDefaults(true);
|
||||||
|
try {
|
||||||
|
config.save(configFile);
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the data
|
||||||
|
serverUUID = config.getString("serverUuid");
|
||||||
|
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||||
|
if (config.getBoolean("enabled", true)) {
|
||||||
|
boolean found = false;
|
||||||
|
// Search for all other bStats Metrics classes to see if we are the first one
|
||||||
|
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||||
|
try {
|
||||||
|
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||||
|
found = true; // We aren't the first
|
||||||
|
break;
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Register our service
|
||||||
|
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
|
||||||
|
if (!found) {
|
||||||
|
// We are the first!
|
||||||
|
startSubmitting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a custom chart.
|
||||||
|
*
|
||||||
|
* @param chart The chart to add.
|
||||||
|
*/
|
||||||
|
public void addCustomChart(CustomChart chart) {
|
||||||
|
if (chart == null) {
|
||||||
|
throw new IllegalArgumentException("Chart cannot be null!");
|
||||||
|
}
|
||||||
|
charts.add(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the Scheduler which submits our data every 30 minutes.
|
||||||
|
*/
|
||||||
|
private void startSubmitting() {
|
||||||
|
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
|
||||||
|
timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!plugin.isEnabled()) { // Plugin was disabled
|
||||||
|
timer.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
|
||||||
|
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
|
||||||
|
Bukkit.getScheduler().runTask(plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
submitData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1000 * 60 * 5, 1000 * 60 * 30);
|
||||||
|
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||||
|
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
|
||||||
|
// WARNING: Just don't do it!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the plugin specific data.
|
||||||
|
* This method is called using Reflection.
|
||||||
|
*
|
||||||
|
* @return The plugin specific data.
|
||||||
|
*/
|
||||||
|
public JSONObject getPluginData() {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
|
||||||
|
String pluginName = plugin.getDescription().getName();
|
||||||
|
String pluginVersion = plugin.getDescription().getVersion();
|
||||||
|
|
||||||
|
data.put("pluginName", pluginName); // Append the name of the plugin
|
||||||
|
data.put("pluginVersion", pluginVersion); // Append the version of the plugin
|
||||||
|
JSONArray customCharts = new JSONArray();
|
||||||
|
for (CustomChart customChart : charts) {
|
||||||
|
// Add the data of the custom charts
|
||||||
|
JSONObject chart = customChart.getRequestJsonObject();
|
||||||
|
if (chart == null) { // If the chart is null, we skip it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
customCharts.add(chart);
|
||||||
|
}
|
||||||
|
data.put("customCharts", customCharts);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the server specific data.
|
||||||
|
*
|
||||||
|
* @return The server specific data.
|
||||||
|
*/
|
||||||
|
private JSONObject getServerData() {
|
||||||
|
// Minecraft specific data
|
||||||
|
int playerAmount = Bukkit.getOnlinePlayers().size();
|
||||||
|
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
|
||||||
|
String bukkitVersion = org.bukkit.Bukkit.getVersion();
|
||||||
|
bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1);
|
||||||
|
|
||||||
|
// OS/Java specific data
|
||||||
|
String javaVersion = System.getProperty("java.version");
|
||||||
|
String osName = System.getProperty("os.name");
|
||||||
|
String osArch = System.getProperty("os.arch");
|
||||||
|
String osVersion = System.getProperty("os.version");
|
||||||
|
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||||
|
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
|
||||||
|
data.put("serverUUID", serverUUID);
|
||||||
|
|
||||||
|
data.put("playerAmount", playerAmount);
|
||||||
|
data.put("onlineMode", onlineMode);
|
||||||
|
data.put("bukkitVersion", bukkitVersion);
|
||||||
|
|
||||||
|
data.put("javaVersion", javaVersion);
|
||||||
|
data.put("osName", osName);
|
||||||
|
data.put("osArch", osArch);
|
||||||
|
data.put("osVersion", osVersion);
|
||||||
|
data.put("coreCount", coreCount);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects the data and sends it afterwards.
|
||||||
|
*/
|
||||||
|
private void submitData() {
|
||||||
|
final JSONObject data = getServerData();
|
||||||
|
|
||||||
|
JSONArray pluginData = new JSONArray();
|
||||||
|
// Search for all other bStats Metrics classes to get their plugin data
|
||||||
|
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||||
|
try {
|
||||||
|
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
continue; // Continue "searching"
|
||||||
|
}
|
||||||
|
// Found one!
|
||||||
|
try {
|
||||||
|
pluginData.add(service.getMethod("getPluginData").invoke(Bukkit.getServicesManager().load(service)));
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.put("plugins", pluginData);
|
||||||
|
|
||||||
|
// Create a new thread for the connection to the bStats server
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// Send the data
|
||||||
|
sendData(data);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Something went wrong! :(
|
||||||
|
if (logFailedRequests) {
|
||||||
|
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the data to the bStats server.
|
||||||
|
*
|
||||||
|
* @param data The data to send.
|
||||||
|
* @throws Exception If the request failed.
|
||||||
|
*/
|
||||||
|
private static void sendData(JSONObject data) throws Exception {
|
||||||
|
if (data == null) {
|
||||||
|
throw new IllegalArgumentException("Data cannot be null!");
|
||||||
|
}
|
||||||
|
if (Bukkit.isPrimaryThread()) {
|
||||||
|
throw new IllegalAccessException("This method must not be called from the main thread!");
|
||||||
|
}
|
||||||
|
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||||
|
|
||||||
|
// Compress the data to save bandwidth
|
||||||
|
byte[] compressedData = compress(data.toString());
|
||||||
|
|
||||||
|
// Add headers
|
||||||
|
connection.setRequestMethod("POST");
|
||||||
|
connection.addRequestProperty("Accept", "application/json");
|
||||||
|
connection.addRequestProperty("Connection", "close");
|
||||||
|
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||||
|
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||||
|
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||||
|
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||||
|
|
||||||
|
// Send data
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
|
||||||
|
outputStream.write(compressedData);
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
connection.getInputStream().close(); // We don't care about the response - Just send our data :)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gzips the given String.
|
||||||
|
*
|
||||||
|
* @param str The string to gzip.
|
||||||
|
* @return The gzipped String.
|
||||||
|
* @throws IOException If the compression failed.
|
||||||
|
*/
|
||||||
|
private static byte[] compress(final String str) throws IOException {
|
||||||
|
if (str == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||||
|
gzip.write(str.getBytes("UTF-8"));
|
||||||
|
gzip.close();
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a custom chart.
|
||||||
|
*/
|
||||||
|
public static abstract class CustomChart {
|
||||||
|
|
||||||
|
// The id of the chart
|
||||||
|
protected final String chartId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param chartId The id of the chart.
|
||||||
|
*/
|
||||||
|
public CustomChart(String chartId) {
|
||||||
|
if (chartId == null || chartId.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("ChartId cannot be null or empty!");
|
||||||
|
}
|
||||||
|
this.chartId = chartId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JSONObject getRequestJsonObject() {
|
||||||
|
JSONObject chart = new JSONObject();
|
||||||
|
chart.put("chartId", chartId);
|
||||||
|
try {
|
||||||
|
JSONObject data = getChartData();
|
||||||
|
if (data == null) {
|
||||||
|
// If the data is null we don't send the chart.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
chart.put("data", data);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
if (logFailedRequests) {
|
||||||
|
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract JSONObject getChartData();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a custom simple pie.
|
||||||
|
*/
|
||||||
|
public static abstract class SimplePie extends CustomChart {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param chartId The id of the chart.
|
||||||
|
*/
|
||||||
|
public SimplePie(String chartId) {
|
||||||
|
super(chartId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the pie.
|
||||||
|
*
|
||||||
|
* @return The value of the pie.
|
||||||
|
*/
|
||||||
|
public abstract String getValue();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSONObject getChartData() {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
String value = getValue();
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
// Null = skip the chart
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
data.put("value", value);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a custom advanced pie.
|
||||||
|
*/
|
||||||
|
public static abstract class AdvancedPie extends CustomChart {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param chartId The id of the chart.
|
||||||
|
*/
|
||||||
|
public AdvancedPie(String chartId) {
|
||||||
|
super(chartId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the values of the pie.
|
||||||
|
*
|
||||||
|
* @param valueMap Just an empty map. The only reason it exists is to make your life easier.
|
||||||
|
* You don't have to create a map yourself!
|
||||||
|
* @return The values of the pie.
|
||||||
|
*/
|
||||||
|
public abstract HashMap<String, Integer> getValues(HashMap<String, Integer> valueMap);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSONObject getChartData() {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
JSONObject values = new JSONObject();
|
||||||
|
HashMap<String, Integer> map = getValues(new HashMap<String, Integer>());
|
||||||
|
if (map == null || map.isEmpty()) {
|
||||||
|
// Null = skip the chart
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
boolean allSkipped = true;
|
||||||
|
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||||
|
if (entry.getValue() == 0) {
|
||||||
|
continue; // Skip this invalid
|
||||||
|
}
|
||||||
|
allSkipped = false;
|
||||||
|
values.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
if (allSkipped) {
|
||||||
|
// Null = skip the chart
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
data.put("values", values);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a custom single line chart.
|
||||||
|
*/
|
||||||
|
public static abstract class SingleLineChart extends CustomChart {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param chartId The id of the chart.
|
||||||
|
*/
|
||||||
|
public SingleLineChart(String chartId) {
|
||||||
|
super(chartId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the chart.
|
||||||
|
*
|
||||||
|
* @return The value of the chart.
|
||||||
|
*/
|
||||||
|
public abstract int getValue();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSONObject getChartData() {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
int value = getValue();
|
||||||
|
if (value == 0) {
|
||||||
|
// Null = skip the chart
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
data.put("value", value);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a custom multi line chart.
|
||||||
|
*/
|
||||||
|
public static abstract class MultiLineChart extends CustomChart {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param chartId The id of the chart.
|
||||||
|
*/
|
||||||
|
public MultiLineChart(String chartId) {
|
||||||
|
super(chartId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the values of the chart.
|
||||||
|
*
|
||||||
|
* @param valueMap Just an empty map. The only reason it exists is to make your life easier.
|
||||||
|
* You don't have to create a map yourself!
|
||||||
|
* @return The values of the chart.
|
||||||
|
*/
|
||||||
|
public abstract HashMap<String, Integer> getValues(HashMap<String, Integer> valueMap);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSONObject getChartData() {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
JSONObject values = new JSONObject();
|
||||||
|
HashMap<String, Integer> map = getValues(new HashMap<String, Integer>());
|
||||||
|
if (map == null || map.isEmpty()) {
|
||||||
|
// Null = skip the chart
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
boolean allSkipped = true;
|
||||||
|
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||||
|
if (entry.getValue() == 0) {
|
||||||
|
continue; // Skip this invalid
|
||||||
|
}
|
||||||
|
allSkipped = false;
|
||||||
|
values.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
if (allSkipped) {
|
||||||
|
// Null = skip the chart
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
data.put("values", values);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a custom simple map chart.
|
||||||
|
*/
|
||||||
|
public static abstract class SimpleMapChart extends CustomChart {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param chartId The id of the chart.
|
||||||
|
*/
|
||||||
|
public SimpleMapChart(String chartId) {
|
||||||
|
super(chartId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the chart.
|
||||||
|
*
|
||||||
|
* @return The value of the chart.
|
||||||
|
*/
|
||||||
|
public abstract Country getValue();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSONObject getChartData() {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
Country value = getValue();
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
// Null = skip the chart
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
data.put("value", value.getCountryIsoTag());
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a custom advanced map chart.
|
||||||
|
*/
|
||||||
|
public static abstract class AdvancedMapChart extends CustomChart {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor.
|
||||||
|
*
|
||||||
|
* @param chartId The id of the chart.
|
||||||
|
*/
|
||||||
|
public AdvancedMapChart(String chartId) {
|
||||||
|
super(chartId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the chart.
|
||||||
|
*
|
||||||
|
* @param valueMap Just an empty map. The only reason it exists is to make your life easier.
|
||||||
|
* You don't have to create a map yourself!
|
||||||
|
* @return The value of the chart.
|
||||||
|
*/
|
||||||
|
public abstract HashMap<Country, Integer> getValues(HashMap<Country, Integer> valueMap);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSONObject getChartData() {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
JSONObject values = new JSONObject();
|
||||||
|
HashMap<Country, Integer> map = getValues(new HashMap<Country, Integer>());
|
||||||
|
if (map == null || map.isEmpty()) {
|
||||||
|
// Null = skip the chart
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
boolean allSkipped = true;
|
||||||
|
for (Map.Entry<Country, Integer> entry : map.entrySet()) {
|
||||||
|
if (entry.getValue() == 0) {
|
||||||
|
continue; // Skip this invalid
|
||||||
|
}
|
||||||
|
allSkipped = false;
|
||||||
|
values.put(entry.getKey().getCountryIsoTag(), entry.getValue());
|
||||||
|
}
|
||||||
|
if (allSkipped) {
|
||||||
|
// Null = skip the chart
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
data.put("values", values);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A enum which is used for custom maps.
|
||||||
|
*/
|
||||||
|
public enum Country {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bStats will use the country of the server.
|
||||||
|
*/
|
||||||
|
AUTO_DETECT("AUTO", "Auto Detected"),
|
||||||
|
|
||||||
|
ANDORRA("AD", "Andorra"),
|
||||||
|
UNITED_ARAB_EMIRATES("AE", "United Arab Emirates"),
|
||||||
|
AFGHANISTAN("AF", "Afghanistan"),
|
||||||
|
ANTIGUA_AND_BARBUDA("AG", "Antigua and Barbuda"),
|
||||||
|
ANGUILLA("AI", "Anguilla"),
|
||||||
|
ALBANIA("AL", "Albania"),
|
||||||
|
ARMENIA("AM", "Armenia"),
|
||||||
|
NETHERLANDS_ANTILLES("AN", "Netherlands Antilles"),
|
||||||
|
ANGOLA("AO", "Angola"),
|
||||||
|
ANTARCTICA("AQ", "Antarctica"),
|
||||||
|
ARGENTINA("AR", "Argentina"),
|
||||||
|
AMERICAN_SAMOA("AS", "American Samoa"),
|
||||||
|
AUSTRIA("AT", "Austria"),
|
||||||
|
AUSTRALIA("AU", "Australia"),
|
||||||
|
ARUBA("AW", "Aruba"),
|
||||||
|
ÅLAND_ISLANDS("AX", "Åland Islands"),
|
||||||
|
AZERBAIJAN("AZ", "Azerbaijan"),
|
||||||
|
BOSNIA_AND_HERZEGOVINA("BA", "Bosnia and Herzegovina"),
|
||||||
|
BARBADOS("BB", "Barbados"),
|
||||||
|
BANGLADESH("BD", "Bangladesh"),
|
||||||
|
BELGIUM("BE", "Belgium"),
|
||||||
|
BURKINA_FASO("BF", "Burkina Faso"),
|
||||||
|
BULGARIA("BG", "Bulgaria"),
|
||||||
|
BAHRAIN("BH", "Bahrain"),
|
||||||
|
BURUNDI("BI", "Burundi"),
|
||||||
|
BENIN("BJ", "Benin"),
|
||||||
|
SAINT_BARTHÉLEMY("BL", "Saint Barthélemy"),
|
||||||
|
BERMUDA("BM", "Bermuda"),
|
||||||
|
BRUNEI("BN", "Brunei"),
|
||||||
|
BOLIVIA("BO", "Bolivia"),
|
||||||
|
BONAIRE_SINT_EUSTATIUS_AND_SABA("BQ", "Bonaire, Sint Eustatius and Saba"),
|
||||||
|
BRAZIL("BR", "Brazil"),
|
||||||
|
BAHAMAS("BS", "Bahamas"),
|
||||||
|
BHUTAN("BT", "Bhutan"),
|
||||||
|
BOUVET_ISLAND("BV", "Bouvet Island"),
|
||||||
|
BOTSWANA("BW", "Botswana"),
|
||||||
|
BELARUS("BY", "Belarus"),
|
||||||
|
BELIZE("BZ", "Belize"),
|
||||||
|
CANADA("CA", "Canada"),
|
||||||
|
COCOS_ISLANDS("CC", "Cocos Islands"),
|
||||||
|
THE_DEMOCRATIC_REPUBLIC_OF_CONGO("CD", "The Democratic Republic Of Congo"),
|
||||||
|
CENTRAL_AFRICAN_REPUBLIC("CF", "Central African Republic"),
|
||||||
|
CONGO("CG", "Congo"),
|
||||||
|
SWITZERLAND("CH", "Switzerland"),
|
||||||
|
CÔTE_D_IVOIRE("CI", "Côte d'Ivoire"),
|
||||||
|
COOK_ISLANDS("CK", "Cook Islands"),
|
||||||
|
CHILE("CL", "Chile"),
|
||||||
|
CAMEROON("CM", "Cameroon"),
|
||||||
|
CHINA("CN", "China"),
|
||||||
|
COLOMBIA("CO", "Colombia"),
|
||||||
|
COSTA_RICA("CR", "Costa Rica"),
|
||||||
|
CUBA("CU", "Cuba"),
|
||||||
|
CAPE_VERDE("CV", "Cape Verde"),
|
||||||
|
CURAÇAO("CW", "Curaçao"),
|
||||||
|
CHRISTMAS_ISLAND("CX", "Christmas Island"),
|
||||||
|
CYPRUS("CY", "Cyprus"),
|
||||||
|
CZECH_REPUBLIC("CZ", "Czech Republic"),
|
||||||
|
GERMANY("DE", "Germany"),
|
||||||
|
DJIBOUTI("DJ", "Djibouti"),
|
||||||
|
DENMARK("DK", "Denmark"),
|
||||||
|
DOMINICA("DM", "Dominica"),
|
||||||
|
DOMINICAN_REPUBLIC("DO", "Dominican Republic"),
|
||||||
|
ALGERIA("DZ", "Algeria"),
|
||||||
|
ECUADOR("EC", "Ecuador"),
|
||||||
|
ESTONIA("EE", "Estonia"),
|
||||||
|
EGYPT("EG", "Egypt"),
|
||||||
|
WESTERN_SAHARA("EH", "Western Sahara"),
|
||||||
|
ERITREA("ER", "Eritrea"),
|
||||||
|
SPAIN("ES", "Spain"),
|
||||||
|
ETHIOPIA("ET", "Ethiopia"),
|
||||||
|
FINLAND("FI", "Finland"),
|
||||||
|
FIJI("FJ", "Fiji"),
|
||||||
|
FALKLAND_ISLANDS("FK", "Falkland Islands"),
|
||||||
|
MICRONESIA("FM", "Micronesia"),
|
||||||
|
FAROE_ISLANDS("FO", "Faroe Islands"),
|
||||||
|
FRANCE("FR", "France"),
|
||||||
|
GABON("GA", "Gabon"),
|
||||||
|
UNITED_KINGDOM("GB", "United Kingdom"),
|
||||||
|
GRENADA("GD", "Grenada"),
|
||||||
|
GEORGIA("GE", "Georgia"),
|
||||||
|
FRENCH_GUIANA("GF", "French Guiana"),
|
||||||
|
GUERNSEY("GG", "Guernsey"),
|
||||||
|
GHANA("GH", "Ghana"),
|
||||||
|
GIBRALTAR("GI", "Gibraltar"),
|
||||||
|
GREENLAND("GL", "Greenland"),
|
||||||
|
GAMBIA("GM", "Gambia"),
|
||||||
|
GUINEA("GN", "Guinea"),
|
||||||
|
GUADELOUPE("GP", "Guadeloupe"),
|
||||||
|
EQUATORIAL_GUINEA("GQ", "Equatorial Guinea"),
|
||||||
|
GREECE("GR", "Greece"),
|
||||||
|
SOUTH_GEORGIA_AND_THE_SOUTH_SANDWICH_ISLANDS("GS", "South Georgia And The South Sandwich Islands"),
|
||||||
|
GUATEMALA("GT", "Guatemala"),
|
||||||
|
GUAM("GU", "Guam"),
|
||||||
|
GUINEA_BISSAU("GW", "Guinea-Bissau"),
|
||||||
|
GUYANA("GY", "Guyana"),
|
||||||
|
HONG_KONG("HK", "Hong Kong"),
|
||||||
|
HEARD_ISLAND_AND_MCDONALD_ISLANDS("HM", "Heard Island And McDonald Islands"),
|
||||||
|
HONDURAS("HN", "Honduras"),
|
||||||
|
CROATIA("HR", "Croatia"),
|
||||||
|
HAITI("HT", "Haiti"),
|
||||||
|
HUNGARY("HU", "Hungary"),
|
||||||
|
INDONESIA("ID", "Indonesia"),
|
||||||
|
IRELAND("IE", "Ireland"),
|
||||||
|
ISRAEL("IL", "Israel"),
|
||||||
|
ISLE_OF_MAN("IM", "Isle Of Man"),
|
||||||
|
INDIA("IN", "India"),
|
||||||
|
BRITISH_INDIAN_OCEAN_TERRITORY("IO", "British Indian Ocean Territory"),
|
||||||
|
IRAQ("IQ", "Iraq"),
|
||||||
|
IRAN("IR", "Iran"),
|
||||||
|
ICELAND("IS", "Iceland"),
|
||||||
|
ITALY("IT", "Italy"),
|
||||||
|
JERSEY("JE", "Jersey"),
|
||||||
|
JAMAICA("JM", "Jamaica"),
|
||||||
|
JORDAN("JO", "Jordan"),
|
||||||
|
JAPAN("JP", "Japan"),
|
||||||
|
KENYA("KE", "Kenya"),
|
||||||
|
KYRGYZSTAN("KG", "Kyrgyzstan"),
|
||||||
|
CAMBODIA("KH", "Cambodia"),
|
||||||
|
KIRIBATI("KI", "Kiribati"),
|
||||||
|
COMOROS("KM", "Comoros"),
|
||||||
|
SAINT_KITTS_AND_NEVIS("KN", "Saint Kitts And Nevis"),
|
||||||
|
NORTH_KOREA("KP", "North Korea"),
|
||||||
|
SOUTH_KOREA("KR", "South Korea"),
|
||||||
|
KUWAIT("KW", "Kuwait"),
|
||||||
|
CAYMAN_ISLANDS("KY", "Cayman Islands"),
|
||||||
|
KAZAKHSTAN("KZ", "Kazakhstan"),
|
||||||
|
LAOS("LA", "Laos"),
|
||||||
|
LEBANON("LB", "Lebanon"),
|
||||||
|
SAINT_LUCIA("LC", "Saint Lucia"),
|
||||||
|
LIECHTENSTEIN("LI", "Liechtenstein"),
|
||||||
|
SRI_LANKA("LK", "Sri Lanka"),
|
||||||
|
LIBERIA("LR", "Liberia"),
|
||||||
|
LESOTHO("LS", "Lesotho"),
|
||||||
|
LITHUANIA("LT", "Lithuania"),
|
||||||
|
LUXEMBOURG("LU", "Luxembourg"),
|
||||||
|
LATVIA("LV", "Latvia"),
|
||||||
|
LIBYA("LY", "Libya"),
|
||||||
|
MOROCCO("MA", "Morocco"),
|
||||||
|
MONACO("MC", "Monaco"),
|
||||||
|
MOLDOVA("MD", "Moldova"),
|
||||||
|
MONTENEGRO("ME", "Montenegro"),
|
||||||
|
SAINT_MARTIN("MF", "Saint Martin"),
|
||||||
|
MADAGASCAR("MG", "Madagascar"),
|
||||||
|
MARSHALL_ISLANDS("MH", "Marshall Islands"),
|
||||||
|
MACEDONIA("MK", "Macedonia"),
|
||||||
|
MALI("ML", "Mali"),
|
||||||
|
MYANMAR("MM", "Myanmar"),
|
||||||
|
MONGOLIA("MN", "Mongolia"),
|
||||||
|
MACAO("MO", "Macao"),
|
||||||
|
NORTHERN_MARIANA_ISLANDS("MP", "Northern Mariana Islands"),
|
||||||
|
MARTINIQUE("MQ", "Martinique"),
|
||||||
|
MAURITANIA("MR", "Mauritania"),
|
||||||
|
MONTSERRAT("MS", "Montserrat"),
|
||||||
|
MALTA("MT", "Malta"),
|
||||||
|
MAURITIUS("MU", "Mauritius"),
|
||||||
|
MALDIVES("MV", "Maldives"),
|
||||||
|
MALAWI("MW", "Malawi"),
|
||||||
|
MEXICO("MX", "Mexico"),
|
||||||
|
MALAYSIA("MY", "Malaysia"),
|
||||||
|
MOZAMBIQUE("MZ", "Mozambique"),
|
||||||
|
NAMIBIA("NA", "Namibia"),
|
||||||
|
NEW_CALEDONIA("NC", "New Caledonia"),
|
||||||
|
NIGER("NE", "Niger"),
|
||||||
|
NORFOLK_ISLAND("NF", "Norfolk Island"),
|
||||||
|
NIGERIA("NG", "Nigeria"),
|
||||||
|
NICARAGUA("NI", "Nicaragua"),
|
||||||
|
NETHERLANDS("NL", "Netherlands"),
|
||||||
|
NORWAY("NO", "Norway"),
|
||||||
|
NEPAL("NP", "Nepal"),
|
||||||
|
NAURU("NR", "Nauru"),
|
||||||
|
NIUE("NU", "Niue"),
|
||||||
|
NEW_ZEALAND("NZ", "New Zealand"),
|
||||||
|
OMAN("OM", "Oman"),
|
||||||
|
PANAMA("PA", "Panama"),
|
||||||
|
PERU("PE", "Peru"),
|
||||||
|
FRENCH_POLYNESIA("PF", "French Polynesia"),
|
||||||
|
PAPUA_NEW_GUINEA("PG", "Papua New Guinea"),
|
||||||
|
PHILIPPINES("PH", "Philippines"),
|
||||||
|
PAKISTAN("PK", "Pakistan"),
|
||||||
|
POLAND("PL", "Poland"),
|
||||||
|
SAINT_PIERRE_AND_MIQUELON("PM", "Saint Pierre And Miquelon"),
|
||||||
|
PITCAIRN("PN", "Pitcairn"),
|
||||||
|
PUERTO_RICO("PR", "Puerto Rico"),
|
||||||
|
PALESTINE("PS", "Palestine"),
|
||||||
|
PORTUGAL("PT", "Portugal"),
|
||||||
|
PALAU("PW", "Palau"),
|
||||||
|
PARAGUAY("PY", "Paraguay"),
|
||||||
|
QATAR("QA", "Qatar"),
|
||||||
|
REUNION("RE", "Reunion"),
|
||||||
|
ROMANIA("RO", "Romania"),
|
||||||
|
SERBIA("RS", "Serbia"),
|
||||||
|
RUSSIA("RU", "Russia"),
|
||||||
|
RWANDA("RW", "Rwanda"),
|
||||||
|
SAUDI_ARABIA("SA", "Saudi Arabia"),
|
||||||
|
SOLOMON_ISLANDS("SB", "Solomon Islands"),
|
||||||
|
SEYCHELLES("SC", "Seychelles"),
|
||||||
|
SUDAN("SD", "Sudan"),
|
||||||
|
SWEDEN("SE", "Sweden"),
|
||||||
|
SINGAPORE("SG", "Singapore"),
|
||||||
|
SAINT_HELENA("SH", "Saint Helena"),
|
||||||
|
SLOVENIA("SI", "Slovenia"),
|
||||||
|
SVALBARD_AND_JAN_MAYEN("SJ", "Svalbard And Jan Mayen"),
|
||||||
|
SLOVAKIA("SK", "Slovakia"),
|
||||||
|
SIERRA_LEONE("SL", "Sierra Leone"),
|
||||||
|
SAN_MARINO("SM", "San Marino"),
|
||||||
|
SENEGAL("SN", "Senegal"),
|
||||||
|
SOMALIA("SO", "Somalia"),
|
||||||
|
SURINAME("SR", "Suriname"),
|
||||||
|
SOUTH_SUDAN("SS", "South Sudan"),
|
||||||
|
SAO_TOME_AND_PRINCIPE("ST", "Sao Tome And Principe"),
|
||||||
|
EL_SALVADOR("SV", "El Salvador"),
|
||||||
|
SINT_MAARTEN_DUTCH_PART("SX", "Sint Maarten (Dutch part)"),
|
||||||
|
SYRIA("SY", "Syria"),
|
||||||
|
SWAZILAND("SZ", "Swaziland"),
|
||||||
|
TURKS_AND_CAICOS_ISLANDS("TC", "Turks And Caicos Islands"),
|
||||||
|
CHAD("TD", "Chad"),
|
||||||
|
FRENCH_SOUTHERN_TERRITORIES("TF", "French Southern Territories"),
|
||||||
|
TOGO("TG", "Togo"),
|
||||||
|
THAILAND("TH", "Thailand"),
|
||||||
|
TAJIKISTAN("TJ", "Tajikistan"),
|
||||||
|
TOKELAU("TK", "Tokelau"),
|
||||||
|
TIMOR_LESTE("TL", "Timor-Leste"),
|
||||||
|
TURKMENISTAN("TM", "Turkmenistan"),
|
||||||
|
TUNISIA("TN", "Tunisia"),
|
||||||
|
TONGA("TO", "Tonga"),
|
||||||
|
TURKEY("TR", "Turkey"),
|
||||||
|
TRINIDAD_AND_TOBAGO("TT", "Trinidad and Tobago"),
|
||||||
|
TUVALU("TV", "Tuvalu"),
|
||||||
|
TAIWAN("TW", "Taiwan"),
|
||||||
|
TANZANIA("TZ", "Tanzania"),
|
||||||
|
UKRAINE("UA", "Ukraine"),
|
||||||
|
UGANDA("UG", "Uganda"),
|
||||||
|
UNITED_STATES_MINOR_OUTLYING_ISLANDS("UM", "United States Minor Outlying Islands"),
|
||||||
|
UNITED_STATES("US", "United States"),
|
||||||
|
URUGUAY("UY", "Uruguay"),
|
||||||
|
UZBEKISTAN("UZ", "Uzbekistan"),
|
||||||
|
VATICAN("VA", "Vatican"),
|
||||||
|
SAINT_VINCENT_AND_THE_GRENADINES("VC", "Saint Vincent And The Grenadines"),
|
||||||
|
VENEZUELA("VE", "Venezuela"),
|
||||||
|
BRITISH_VIRGIN_ISLANDS("VG", "British Virgin Islands"),
|
||||||
|
U_S__VIRGIN_ISLANDS("VI", "U.S. Virgin Islands"),
|
||||||
|
VIETNAM("VN", "Vietnam"),
|
||||||
|
VANUATU("VU", "Vanuatu"),
|
||||||
|
WALLIS_AND_FUTUNA("WF", "Wallis And Futuna"),
|
||||||
|
SAMOA("WS", "Samoa"),
|
||||||
|
YEMEN("YE", "Yemen"),
|
||||||
|
MAYOTTE("YT", "Mayotte"),
|
||||||
|
SOUTH_AFRICA("ZA", "South Africa"),
|
||||||
|
ZAMBIA("ZM", "Zambia"),
|
||||||
|
ZIMBABWE("ZW", "Zimbabwe");
|
||||||
|
|
||||||
|
private String isoTag;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
Country(String isoTag, String name) {
|
||||||
|
this.isoTag = isoTag;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of the country.
|
||||||
|
*
|
||||||
|
* @return The name of the country.
|
||||||
|
*/
|
||||||
|
public String getCountryName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the iso tag of the country.
|
||||||
|
*
|
||||||
|
* @return The iso tag of the country.
|
||||||
|
*/
|
||||||
|
public String getCountryIsoTag() {
|
||||||
|
return isoTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a country by it's iso tag.
|
||||||
|
*
|
||||||
|
* @param isoTag The iso tag of the county.
|
||||||
|
* @return The country with the given iso tag or <code>null</code> if unknown.
|
||||||
|
*/
|
||||||
|
public static Country byIsoTag(String isoTag) {
|
||||||
|
for (Country country : Country.values()) {
|
||||||
|
if (country.getCountryIsoTag().equals(isoTag)) {
|
||||||
|
return country;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a country by a locale.
|
||||||
|
*
|
||||||
|
* @param locale The locale.
|
||||||
|
* @return The country from the giben locale or <code>null</code> if unknown country or
|
||||||
|
* if the locale does not contain a country.
|
||||||
|
*/
|
||||||
|
public static Country byLocale(Locale locale) {
|
||||||
|
return byIsoTag(locale.getCountry());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
package sh.okx.rankup;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import me.clip.placeholderapi.PlaceholderAPI;
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import net.milkbowl.vault.permission.Permission;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
|
import org.bukkit.inventory.InventoryView;
|
||||||
|
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import sh.okx.rankup.commands.InfoCommand;
|
||||||
|
import sh.okx.rankup.commands.RankListCommand;
|
||||||
|
import sh.okx.rankup.commands.RankupCommand;
|
||||||
|
import sh.okx.rankup.gui.Gui;
|
||||||
|
import sh.okx.rankup.gui.GuiListener;
|
||||||
|
import sh.okx.rankup.messages.Message;
|
||||||
|
import sh.okx.rankup.messages.MessageBuilder;
|
||||||
|
import sh.okx.rankup.messages.Variable;
|
||||||
|
import sh.okx.rankup.placeholders.Placeholders;
|
||||||
|
import sh.okx.rankup.ranks.Rank;
|
||||||
|
import sh.okx.rankup.ranks.Rankups;
|
||||||
|
import sh.okx.rankup.ranks.requirements.PlaytimeHoursRequirement;
|
||||||
|
import sh.okx.rankup.ranks.requirements.XpLevelRequirement;
|
||||||
|
import sh.okx.rankup.ranks.requirements.MoneyRequirement;
|
||||||
|
import sh.okx.rankup.ranks.requirements.RequirementRegistry;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class Rankup extends JavaPlugin {
|
||||||
|
@Getter
|
||||||
|
private Permission permissions;
|
||||||
|
@Getter
|
||||||
|
private Economy economy;
|
||||||
|
/**
|
||||||
|
* The registry for listing the requirements to /rankup.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private RequirementRegistry requirementRegistry = new RequirementRegistry();
|
||||||
|
@Getter
|
||||||
|
private FileConfiguration messages;
|
||||||
|
@Getter
|
||||||
|
private FileConfiguration config;
|
||||||
|
@Getter
|
||||||
|
private Rankups rankups;
|
||||||
|
@Getter
|
||||||
|
private Placeholders placeholders;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
registerRequirements();
|
||||||
|
setupPermissions();
|
||||||
|
setupEconomy();
|
||||||
|
reload();
|
||||||
|
|
||||||
|
Metrics metrics = new Metrics(this);
|
||||||
|
metrics.addCustomChart(new Metrics.SimplePie("confirmation") {
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return getConfig().getString("confirmation.type");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
getCommand("rankup").setExecutor(new RankupCommand(this));
|
||||||
|
getCommand("rankup3").setExecutor(new InfoCommand(this));
|
||||||
|
getCommand("ranks").setExecutor(new RankListCommand(this));
|
||||||
|
getServer().getPluginManager().registerEvents(new GuiListener(this), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
closeInventories();
|
||||||
|
PlaceholderAPI.unregisterExpansion(placeholders);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reload() {
|
||||||
|
closeInventories();
|
||||||
|
loadConfigs();
|
||||||
|
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
|
||||||
|
placeholders = new Placeholders(this);
|
||||||
|
placeholders.register();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config.getInt("version") != YamlConfiguration.loadConfiguration(getTextResource("config.yml")).getInt("version")) {
|
||||||
|
getLogger().severe("You are using an outdated config!");
|
||||||
|
getLogger().severe("This means that some things might not work!");
|
||||||
|
getLogger().severe("To update, please rename your config files (or the folder they are in),");
|
||||||
|
getLogger().severe("and run /rankup3 reload to generate a new config file.");
|
||||||
|
getLogger().severe("If that does not work, restart your server.");
|
||||||
|
getLogger().severe("You may then copy in your config values from the old config.");
|
||||||
|
getLogger().severe("Check the changelog on the Rankup spigot page to see the changes.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes all rankup inventories on disable
|
||||||
|
* so players cannot grab items from the inventory
|
||||||
|
* on a plugin reload.
|
||||||
|
*/
|
||||||
|
private void closeInventories() {
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
InventoryView view = player.getOpenInventory();
|
||||||
|
if(view.getType() == InventoryType.CHEST
|
||||||
|
&& view.getTopInventory().getHolder() instanceof Gui) {
|
||||||
|
player.closeInventory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadConfigs() {
|
||||||
|
messages = loadConfig("messages.yml");
|
||||||
|
config = loadConfig("config.yml");
|
||||||
|
rankups = new Rankups(this, loadConfig("rankups.yml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileConfiguration loadConfig(String name) {
|
||||||
|
File file = new File(getDataFolder(), name);
|
||||||
|
if (!file.exists()) {
|
||||||
|
saveResource(name, false);
|
||||||
|
}
|
||||||
|
return YamlConfiguration.loadConfiguration(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerRequirements() {
|
||||||
|
requirementRegistry.addRequirement(new MoneyRequirement(this, "money"));
|
||||||
|
requirementRegistry.addRequirement(new XpLevelRequirement(this, "xp-level"));
|
||||||
|
requirementRegistry.addRequirement(new PlaytimeHoursRequirement(this, "playtime-hours"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupPermissions() {
|
||||||
|
RegisteredServiceProvider<Permission> rsp = getServer().getServicesManager().getRegistration(Permission.class);
|
||||||
|
permissions = rsp.getProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupEconomy() {
|
||||||
|
RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class);
|
||||||
|
if (rsp != null) {
|
||||||
|
economy = rsp.getProvider();
|
||||||
|
} else {
|
||||||
|
getLogger().warning("No economy found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageBuilder getMessage(Rank rank, Message message) {
|
||||||
|
ConfigurationSection messages = rankups.getConfig()
|
||||||
|
.getConfigurationSection(rank.getName());
|
||||||
|
if(messages == null || !messages.isSet(message.getName())) {
|
||||||
|
messages = this.messages;
|
||||||
|
}
|
||||||
|
return MessageBuilder.of(messages, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageBuilder getMessage(Message message) {
|
||||||
|
return MessageBuilder.of(messages, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rankup(Player player) {
|
||||||
|
if(!checkRankup(player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rank oldRank = rankups.getRank(player);
|
||||||
|
Rank rank = rankups.nextRank(oldRank);
|
||||||
|
|
||||||
|
oldRank.applyRequirements(player);
|
||||||
|
|
||||||
|
permissions.playerRemoveGroup(null, player, oldRank.getRank());
|
||||||
|
permissions.playerAddGroup(null, player, rank.getRank());
|
||||||
|
|
||||||
|
getMessage(oldRank, Message.SUCCESS_PUBLIC)
|
||||||
|
.failIfEmpty()
|
||||||
|
.replaceAll(player, oldRank, rank)
|
||||||
|
.broadcast();
|
||||||
|
getMessage(oldRank, Message.SUCCESS_PRIVATE)
|
||||||
|
.failIfEmpty()
|
||||||
|
.replaceAll(player, oldRank, rank)
|
||||||
|
.send(player);
|
||||||
|
|
||||||
|
oldRank.runCommands(player, rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a player can rankup,
|
||||||
|
* and if they can't, sends the player a message and returns false
|
||||||
|
* @param player the player to check if they can rankup
|
||||||
|
* @return true if the player can rankup, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean checkRankup(Player player) {
|
||||||
|
Rank rank = rankups.getRank(player);
|
||||||
|
if (rank == null) { // check if in ladder
|
||||||
|
getMessage(Message.NOT_IN_LADDER)
|
||||||
|
.replace(Variable.PLAYER, player.getName())
|
||||||
|
.send(player);
|
||||||
|
return false;
|
||||||
|
} else if (rank.isLastRank()) { // check if they are at the highest rank
|
||||||
|
getMessage(rank, Message.NO_RANKUP)
|
||||||
|
.replaceAll(player, rank)
|
||||||
|
.send(player);
|
||||||
|
return false;
|
||||||
|
} else if (!rank.checkRequirements(player)) { // check if they can afford it
|
||||||
|
MessageBuilder builder =
|
||||||
|
getMessage(rank, Message.REQUIREMENTS_NOT_MET)
|
||||||
|
.replaceAll(player, rank);
|
||||||
|
if (economy != null) {
|
||||||
|
double balance = economy.getBalance(player);
|
||||||
|
builder = builder
|
||||||
|
.replace(Variable.MONEY, balance)
|
||||||
|
.replace(Variable.MONEY_NEEDED, rank.getRequirement("money").getAmount() - balance);
|
||||||
|
}
|
||||||
|
builder.send(player);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package sh.okx.rankup.commands;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InfoCommand implements CommandExecutor {
|
||||||
|
private String versionMessage;
|
||||||
|
private final Rankup plugin;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if(args.length > 0) {
|
||||||
|
if(args[0].equalsIgnoreCase("reload") && sender.hasPermission("rankup.reload")) {
|
||||||
|
plugin.reload();
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "" + ChatColor.BOLD + "Rankup " + ChatColor.YELLOW + "Reloaded configuration files.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginDescriptionFile description = plugin.getDescription();
|
||||||
|
sender.sendMessage(
|
||||||
|
ChatColor.GREEN + "" + ChatColor.BOLD + description.getName() + " " + description.getVersion() +
|
||||||
|
ChatColor.YELLOW + " by " + ChatColor.BLUE + ChatColor.BOLD + String.join(", ", description.getAuthors()));
|
||||||
|
if(sender.hasPermission("rankup.reload")) {
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "/" + label + " reload " + ChatColor.YELLOW + "Reloads configuration files.");
|
||||||
|
}
|
||||||
|
if(sender.hasPermission("rankup.checkversion")) {
|
||||||
|
if(versionMessage == null) {
|
||||||
|
sender.sendMessage(ChatColor.YELLOW + "Checking version...");
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
|
String message;
|
||||||
|
try {
|
||||||
|
String latest = getLatestVersion();
|
||||||
|
if (description.getVersion().equals(latest)) {
|
||||||
|
message = ChatColor.GREEN + "You are on the latest version.";
|
||||||
|
} else {
|
||||||
|
message = ChatColor.YELLOW + "A new version is available: " + ChatColor.GOLD + latest
|
||||||
|
+ "\nhttps://www.spigotmc.org/resources/rankup.17933/";
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
message = ChatColor.RED + "Error while checking version.";
|
||||||
|
}
|
||||||
|
versionMessage = message;
|
||||||
|
Bukkit.getScheduler().runTask(plugin, () -> sender.sendMessage(versionMessage));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(versionMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLatestVersion() throws IOException {
|
||||||
|
URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=17933");
|
||||||
|
String result = CharStreams.toString(new InputStreamReader(url.openStream(), Charsets.UTF_8));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package sh.okx.rankup.commands;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
import sh.okx.rankup.messages.Message;
|
||||||
|
import sh.okx.rankup.messages.MessageBuilder;
|
||||||
|
import sh.okx.rankup.messages.Variable;
|
||||||
|
import sh.okx.rankup.ranks.Rank;
|
||||||
|
import sh.okx.rankup.ranks.Rankups;
|
||||||
|
import sh.okx.rankup.ranks.requirements.Requirement;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RankListCommand implements CommandExecutor {
|
||||||
|
private final Rankup plugin;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
Rankups rankups = plugin.getRankups();
|
||||||
|
Rank playerRank = null;
|
||||||
|
if(sender instanceof Player) {
|
||||||
|
playerRank = rankups.getRank((Player) sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendHeaderFooter(sender, playerRank, Message.RANKS_HEADER);
|
||||||
|
|
||||||
|
int state = playerRank == null ? 2 : 0;
|
||||||
|
Rank rank = rankups.getFirstRank();
|
||||||
|
do {
|
||||||
|
Rank next = rankups.nextRank(rank);
|
||||||
|
if(rank.equals(playerRank)) {
|
||||||
|
sendMessage(sender, 1, rank, next);
|
||||||
|
state = 2;
|
||||||
|
} else {
|
||||||
|
sendMessage(sender, state, rank, next);
|
||||||
|
}
|
||||||
|
rank = next;
|
||||||
|
} while(!rank.isLastRank());
|
||||||
|
|
||||||
|
sendHeaderFooter(sender, playerRank, Message.RANKS_FOOTER);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendHeaderFooter(CommandSender sender, Rank rank, Message type) {
|
||||||
|
MessageBuilder builder = plugin.getMessage(type)
|
||||||
|
.failIfEmpty();
|
||||||
|
if(rank == null) {
|
||||||
|
builder.replace(Variable.PLAYER, sender.getName());
|
||||||
|
} else {
|
||||||
|
builder.replaceAll(sender, rank);
|
||||||
|
}
|
||||||
|
builder.send(sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMessage(CommandSender player, int state, Rank oldRank, Rank rank) {
|
||||||
|
if(state == 0) {
|
||||||
|
replaceCost(plugin.getMessage(oldRank, Message.RANKS_COMPLETE)
|
||||||
|
.replaceAll(player, oldRank, rank), player, oldRank)
|
||||||
|
.send(player);
|
||||||
|
} else if(state == 1) {
|
||||||
|
replaceCost(plugin.getMessage(oldRank, Message.RANKS_CURRENT)
|
||||||
|
.replaceAll(player, oldRank, rank), player, oldRank)
|
||||||
|
.send(player);
|
||||||
|
} else if(state == 2) {
|
||||||
|
replaceCost(plugin.getMessage(oldRank, Message.RANKS_INCOMPLETE)
|
||||||
|
.replaceAll(player, oldRank, rank), player, oldRank)
|
||||||
|
.send(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessageBuilder replaceCost(MessageBuilder builder, CommandSender sender, Rank rank) {
|
||||||
|
Requirement money = rank.getRequirement("money");
|
||||||
|
if(money == null || plugin.getEconomy() == null) {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
double amount;
|
||||||
|
if(sender instanceof Player && rank.isInRank((Player) sender)) {
|
||||||
|
amount = money.getRemaining((Player) sender);
|
||||||
|
} else {
|
||||||
|
amount = money.getAmount();
|
||||||
|
}
|
||||||
|
DecimalFormat moneyFormat = plugin.getPlaceholders().getMoneyFormat();
|
||||||
|
DecimalFormat percentFormat = plugin.getPlaceholders().getPercentFormat();
|
||||||
|
return builder
|
||||||
|
.replace(Variable.MONEY_NEEDED, moneyFormat.format(amount))
|
||||||
|
.replace(Variable.PERCENT_LEFT, percentFormat.format((amount / money.getAmount()) * 100))
|
||||||
|
.replace(Variable.PERCENT_DONE, percentFormat.format((1-(amount / money.getAmount())) * 100))
|
||||||
|
.replace(Variable.MONEY, moneyFormat.format(money.getAmount()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package sh.okx.rankup.commands;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
import sh.okx.rankup.gui.Gui;
|
||||||
|
import sh.okx.rankup.messages.Message;
|
||||||
|
import sh.okx.rankup.messages.Variable;
|
||||||
|
import sh.okx.rankup.ranks.Rank;
|
||||||
|
import sh.okx.rankup.ranks.Rankups;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
public class RankupCommand implements CommandExecutor {
|
||||||
|
private final Map<Player, Long> confirming = new WeakHashMap<>();
|
||||||
|
private final Rankup plugin;
|
||||||
|
|
||||||
|
public RankupCommand(Rankup plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
// check if player
|
||||||
|
if (!(sender instanceof Player)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Player player = (Player) sender;
|
||||||
|
|
||||||
|
Rankups rankups = plugin.getRankups();
|
||||||
|
Rank rank = rankups.getRank(player);
|
||||||
|
if (!plugin.checkRankup(player)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileConfiguration config = plugin.getConfig();
|
||||||
|
String confirmationType = config.getString("confirmation-type").toLowerCase();
|
||||||
|
|
||||||
|
// if they are on text confirming, rank them up
|
||||||
|
if (confirmationType.equals("text") && confirming.containsKey(player)) {
|
||||||
|
long time = System.currentTimeMillis() - confirming.remove(player);
|
||||||
|
if (time < config.getInt("text.timeout") * 1000) {
|
||||||
|
plugin.rankup(player);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (confirmationType) {
|
||||||
|
case "text":
|
||||||
|
confirming.put(player, System.currentTimeMillis());
|
||||||
|
plugin.getMessage(rank, Message.CONFIRMATION)
|
||||||
|
.replace(Variable.PLAYER, player.getName())
|
||||||
|
.replace(Variable.RANK, rank.getRank())
|
||||||
|
.replace(Variable.RANK_NAME, rank.getName())
|
||||||
|
.send(player);
|
||||||
|
break;
|
||||||
|
case "gui":
|
||||||
|
Gui.of(player, rank, rankups.nextRank(rank), plugin).open(player);
|
||||||
|
break;
|
||||||
|
case "none":
|
||||||
|
plugin.rankup(player);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid confirmation type " + confirmationType);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package sh.okx.rankup.gui;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
import sh.okx.rankup.messages.Message;
|
||||||
|
import sh.okx.rankup.messages.MessageBuilder;
|
||||||
|
import sh.okx.rankup.messages.Variable;
|
||||||
|
import sh.okx.rankup.ranks.Rank;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class Gui implements InventoryHolder {
|
||||||
|
@Getter
|
||||||
|
private Inventory inventory;
|
||||||
|
@Getter
|
||||||
|
private ItemStack rankup;
|
||||||
|
@Getter
|
||||||
|
private ItemStack cancel;
|
||||||
|
|
||||||
|
public void open(Player player) {
|
||||||
|
player.openInventory(inventory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Gui of(Player player, Rank oldRank, Rank rank, Rankup plugin) {
|
||||||
|
ConfigurationSection config = plugin.getConfig().getConfigurationSection("gui");
|
||||||
|
ItemStack[] items = new ItemStack[config.getInt("rows") * 9];
|
||||||
|
addItem(items, config.getConfigurationSection("rankup"), player, oldRank, rank);
|
||||||
|
addItem(items, config.getConfigurationSection("cancel"), player, oldRank, rank);
|
||||||
|
addItem(items, config.getConfigurationSection("fill"), player, oldRank, rank);
|
||||||
|
|
||||||
|
Gui gui = new Gui();
|
||||||
|
gui.rankup = getItem(config.getConfigurationSection("rankup"), player, oldRank, rank);
|
||||||
|
gui.cancel = getItem(config.getConfigurationSection("cancel"), player, oldRank, rank);
|
||||||
|
Inventory inventory = Bukkit.createInventory(gui,
|
||||||
|
items.length,
|
||||||
|
plugin.getMessage(rank, Message.TITLE)
|
||||||
|
.replaceAll(player, oldRank, rank)
|
||||||
|
.toString());
|
||||||
|
inventory.setContents(items);
|
||||||
|
gui.inventory = inventory;
|
||||||
|
return gui;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ItemStack getItem(ConfigurationSection section, Player player, Rank oldRank, Rank rank) {
|
||||||
|
boolean legacy = !Bukkit.getVersion().contains("1.13");
|
||||||
|
|
||||||
|
String materialName = section.getString("material").toUpperCase();
|
||||||
|
// handle default material correctly on older versions
|
||||||
|
if (legacy && materialName.equals("BLACK_STAINED_GLASS_PANE")) {
|
||||||
|
materialName = "STAINED_GLASS_PANE:15";
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack item;
|
||||||
|
if (legacy) {
|
||||||
|
String[] parts = materialName.split(":");
|
||||||
|
Material material = Material.valueOf(parts[0]);
|
||||||
|
|
||||||
|
short type = parts.length > 1 ? Short.parseShort(parts[1]) : 0;
|
||||||
|
item = new ItemStack(material, 1, type);
|
||||||
|
} else {
|
||||||
|
Material material = Material.valueOf(materialName);
|
||||||
|
item = new ItemStack(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemMeta meta = item.getItemMeta();
|
||||||
|
if (section.contains("lore")) {
|
||||||
|
meta.setLore(Arrays.stream(format(section.getString("lore"), player, oldRank, rank).split("\n"))
|
||||||
|
.map(string -> ChatColor.RESET + string)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
if (section.contains("name")) {
|
||||||
|
meta.setDisplayName(ChatColor.RESET + format(section.getString("name"), player, oldRank, rank));
|
||||||
|
}
|
||||||
|
item.setItemMeta(meta);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(String message, Player player, Rank oldRank, Rank rank) {
|
||||||
|
return new MessageBuilder(ChatColor.translateAlternateColorCodes('&', message))
|
||||||
|
.replaceAll(player, oldRank, rank)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addItem(ItemStack[] items, ConfigurationSection section, Player player, Rank oldRank, Rank rank) {
|
||||||
|
ItemStack item = getItem(section, player, oldRank, rank);
|
||||||
|
if (section.getName().equalsIgnoreCase("fill")) {
|
||||||
|
for (int i = 0; i < items.length; i++) {
|
||||||
|
if (items[i] == null) {
|
||||||
|
items[i] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] locations = section.getString("index").split(" ");
|
||||||
|
for (String location : locations) {
|
||||||
|
String[] parts = location.split("-");
|
||||||
|
if (parts.length == 1) {
|
||||||
|
items[Integer.parseInt(parts[0])] = item;
|
||||||
|
} else {
|
||||||
|
for (int i = Integer.parseInt(parts[0]); i <= Integer.parseInt(parts[1]); i++) {
|
||||||
|
items[i] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package sh.okx.rankup.gui;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GuiListener implements Listener {
|
||||||
|
private final Rankup plugin;
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void on(InventoryClickEvent e) {
|
||||||
|
Inventory inventory = e.getInventory();
|
||||||
|
if (inventory == null
|
||||||
|
|| !(inventory.getHolder() instanceof Gui)
|
||||||
|
|| !e.getInventory().equals(e.getClickedInventory())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.setCancelled(true);
|
||||||
|
|
||||||
|
Player player = (Player) e.getWhoClicked();
|
||||||
|
Gui gui = (Gui) inventory.getHolder();
|
||||||
|
|
||||||
|
if (gui.getRankup().isSimilar(e.getCurrentItem())) {
|
||||||
|
Bukkit.getScheduler().runTask(plugin, player::closeInventory);
|
||||||
|
plugin.rankup(player);
|
||||||
|
} else if (gui.getCancel().isSimilar(e.getCurrentItem())) {
|
||||||
|
Bukkit.getScheduler().runTask(plugin, player::closeInventory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package sh.okx.rankup.messages;
|
||||||
|
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
class EmptyMessageBuilder extends MessageBuilder {
|
||||||
|
EmptyMessageBuilder() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageBuilder failIfEmpty() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageBuilder replace(Variable variable, Object value) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(CommandSender sender) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void broadcast() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package sh.okx.rankup.messages;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public enum Message {
|
||||||
|
NOT_IN_LADDER("not-in-ladder"),
|
||||||
|
REQUIREMENTS_NOT_MET("rankup.requirements-not-met"),
|
||||||
|
NO_RANKUP("rankup.no-rankup"),
|
||||||
|
SUCCESS_PUBLIC("rankup.success-public"),
|
||||||
|
SUCCESS_PRIVATE("rankup.success-private"),
|
||||||
|
CONFIRMATION("rankup.confirmation"),
|
||||||
|
TITLE("rankup.title"),
|
||||||
|
RANKS_HEADER("ranks.header"),
|
||||||
|
RANKS_FOOTER("ranks.footer"),
|
||||||
|
RANKS_COMPLETE("rankup.ranks.complete"),
|
||||||
|
RANKS_CURRENT("rankup.ranks.current"),
|
||||||
|
RANKS_INCOMPLETE("rankup.ranks.incomplete");
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
Message(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package sh.okx.rankup.messages;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.ranks.Rank;
|
||||||
|
import sh.okx.rankup.ranks.requirements.Requirement;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
public class MessageBuilder {
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public MessageBuilder(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MessageBuilder of(ConfigurationSection config, Message message) {
|
||||||
|
return new MessageBuilder(ChatColor.translateAlternateColorCodes('&', config.getString(message.getName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageBuilder replace(Variable variable, Object value) {
|
||||||
|
this.message = variable.replace(message, String.valueOf(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageBuilder replaceAll(CommandSender player, Rank rank) {
|
||||||
|
replace(Variable.PLAYER, player.getName());
|
||||||
|
replaceAll(rank);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageBuilder replaceAll(CommandSender player, Rank oldRank, Rank rank) {
|
||||||
|
replace(Variable.PLAYER, player.getName());
|
||||||
|
replaceAll(oldRank, rank);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageBuilder replaceAll(Rank rank) {
|
||||||
|
replace(Variable.RANK, rank.getRank());
|
||||||
|
replace(Variable.RANK_NAME, rank.getName());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageBuilder replaceAll(Rank oldRank, Rank rank) {
|
||||||
|
replace(Variable.RANK, rank.getRank());
|
||||||
|
replace(Variable.RANK_NAME, rank.getName());
|
||||||
|
replace(Variable.OLD_RANK, oldRank.getRank());
|
||||||
|
replace(Variable.OLD_RANK_NAME, oldRank.getName());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageBuilder replaceCost(CommandSender sender, Economy economy, Rank rank) {
|
||||||
|
Requirement money = rank.getRequirement("money");
|
||||||
|
if(money == null || economy == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
replace(Variable.MONEY, money.getAmount());
|
||||||
|
if(sender instanceof Player && rank.isInRank((Player) sender)) {
|
||||||
|
replace(Variable.MONEY_NEEDED, money.getRemaining((Player) sender));
|
||||||
|
} else {
|
||||||
|
replace(Variable.MONEY_NEEDED, money.getAmount());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fails the MessageBuilder if the message is empty.
|
||||||
|
* if this fails, all subsequent calls to that MessageBuilder will do nothing
|
||||||
|
* @return an EmptyMessageBuilder if the message is empty, itself otherwise
|
||||||
|
*/
|
||||||
|
public MessageBuilder failIfEmpty() {
|
||||||
|
if(message.isEmpty()) {
|
||||||
|
return new EmptyMessageBuilder();
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(CommandSender sender) {
|
||||||
|
sender.sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the message to all players
|
||||||
|
* ie, calls MessageBuilder#send(Player) for all players online, and sends the message in the console.
|
||||||
|
*/
|
||||||
|
public void broadcast() {
|
||||||
|
for(Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
send(player);
|
||||||
|
}
|
||||||
|
send(Bukkit.getConsoleSender());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package sh.okx.rankup.messages;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public enum Variable {
|
||||||
|
PLAYER,
|
||||||
|
OLD_RANK,
|
||||||
|
OLD_RANK_NAME,
|
||||||
|
RANK,
|
||||||
|
RANK_NAME,
|
||||||
|
MONEY,
|
||||||
|
MONEY_NEEDED,
|
||||||
|
PERCENT_DONE,
|
||||||
|
PERCENT_LEFT;
|
||||||
|
|
||||||
|
public static Variable getVariable(String name) {
|
||||||
|
for(Variable variable : values()) {
|
||||||
|
if(variable.toString().equalsIgnoreCase(name)) {
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String replace(String message, String value) {
|
||||||
|
Pattern pattern = Pattern.compile("\\{" + this + "}", Pattern.CASE_INSENSITIVE);
|
||||||
|
Matcher matcher = pattern.matcher(message);
|
||||||
|
return matcher.replaceAll(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package sh.okx.rankup.placeholders;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
import sh.okx.rankup.ranks.Rank;
|
||||||
|
import sh.okx.rankup.ranks.Rankups;
|
||||||
|
import sh.okx.rankup.ranks.requirements.Requirement;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class Placeholders extends PlaceholderExpansion {
|
||||||
|
private final Rankup plugin;
|
||||||
|
@Getter
|
||||||
|
private final DecimalFormat moneyFormat;
|
||||||
|
@Getter
|
||||||
|
private final DecimalFormat percentFormat;
|
||||||
|
@Getter
|
||||||
|
private final DecimalFormat simpleFormat;
|
||||||
|
|
||||||
|
public Placeholders(Rankup plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.moneyFormat = new DecimalFormat(plugin.getConfig().getString("placeholders.money-format"));
|
||||||
|
this.percentFormat = new DecimalFormat(plugin.getConfig().getString("placeholders.percent-format"));
|
||||||
|
this.simpleFormat = new DecimalFormat(plugin.getConfig().getString("placeholders.simple-format"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String onPlaceholderRequest(Player player, String params) {
|
||||||
|
if (player == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
params = params.toLowerCase();
|
||||||
|
|
||||||
|
Rankups rankups = plugin.getRankups();
|
||||||
|
Rank rank = rankups.getRank(player);
|
||||||
|
Rank next = null;
|
||||||
|
if (rank != null) {
|
||||||
|
next = rankups.nextRank(rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(params.startsWith("requirement_")) {
|
||||||
|
String[] parts = params.split("_", 3);
|
||||||
|
return getPlaceholderRequirement(player, rank,
|
||||||
|
parts[1], parts.length > 2 ? parts[2] : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (params) {
|
||||||
|
case "current_rank":
|
||||||
|
return orElsePlaceholder(rank, Rank::getRank, "not-in-ladder");
|
||||||
|
case "current_rank_name":
|
||||||
|
return orElsePlaceholder(rank, Rank::getRank, "not-in-ladder");
|
||||||
|
case "current_rank_money":
|
||||||
|
return orElsePlaceholder(rank, r -> simplify(r.getRequirement("money").getAmount()), 0);
|
||||||
|
case "current_rank_money_formatted":
|
||||||
|
return moneyFormat.format(orElsePlaceholder(rank, r -> r.getRequirement("money").getAmount(), 0));
|
||||||
|
case "next_rank":
|
||||||
|
if (rank == null) {
|
||||||
|
return getPlaceholder("not-in-ladder");
|
||||||
|
} else if (next == null) {
|
||||||
|
return getPlaceholder("highest-rank");
|
||||||
|
} else {
|
||||||
|
return next.getRank();
|
||||||
|
}
|
||||||
|
case "next_rank_name":
|
||||||
|
if (rank == null) {
|
||||||
|
return getPlaceholder("not-in-ladder");
|
||||||
|
} else if (next == null) {
|
||||||
|
return getPlaceholder("highest-rank");
|
||||||
|
} else {
|
||||||
|
return next.getName();
|
||||||
|
}
|
||||||
|
case "next_rank_money":
|
||||||
|
return orElsePlaceholder(next, r -> simplify(r.getRequirement("money").getAmount()), 0);
|
||||||
|
case "next_rank_money_formatted":
|
||||||
|
return moneyFormat.format(orElsePlaceholder(next, r -> r.getRequirement("money").getAmount(), 0));
|
||||||
|
case "next_rank_money_left":
|
||||||
|
return orElsePlaceholder(next, r -> simplify(plugin.getEconomy().getBalance(player) - r.getRequirement("money").getAmount()), 0);
|
||||||
|
case "next_rank_money_left_formatted":
|
||||||
|
return moneyFormat.format(orElsePlaceholder(next, r -> plugin.getEconomy().getBalance(player) - r.getRequirement("money").getAmount(), 0));
|
||||||
|
case "next_rank_percent_left":
|
||||||
|
return orElsePlaceholder(next, r -> (1-(plugin.getEconomy().getBalance(player) / r.getRequirement("money").getAmount())) * 100, 0);
|
||||||
|
case "next_rank_percent_left_formatted":
|
||||||
|
return percentFormat.format(orElsePlaceholder(next, r -> (1-(plugin.getEconomy().getBalance(player) / r.getRequirement("money").getAmount())) * 100, 0));
|
||||||
|
case "next_rank_percent_done":
|
||||||
|
return orElsePlaceholder(next, r -> (plugin.getEconomy().getBalance(player) / r.getRequirement("money").getAmount()) * 100, 0);
|
||||||
|
case "next_rank_percent_done_formatted":
|
||||||
|
return percentFormat.format(orElsePlaceholder(next, r -> (plugin.getEconomy().getBalance(player) / r.getRequirement("money").getAmount()) * 100, 0));
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPlaceholderRequirement(Player player, Rank rank, String requirementName, String params) {
|
||||||
|
if(rank == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
Requirement requirement = rank.getRequirement(requirementName);
|
||||||
|
switch(params) {
|
||||||
|
case "":
|
||||||
|
return simpleFormat.format(orElse(requirement, Requirement::getAmount, 0));
|
||||||
|
case "left":
|
||||||
|
return simpleFormat.format(orElse(requirement, r -> r.getRemaining(player), 0));
|
||||||
|
case "percent_left":
|
||||||
|
return percentFormat.format(orElse(requirement, r -> (r.getRemaining(player) / r.getAmount()) * 100, 0));
|
||||||
|
case "percent_done":
|
||||||
|
return percentFormat.format(orElse(requirement, r -> (1-(r.getRemaining(player) / r.getAmount())) * 100, 100));
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Number simplify(Number number) {
|
||||||
|
if (number instanceof Float) {
|
||||||
|
return (float) number % 1 == 0 ? (int) number : number;
|
||||||
|
} else if (number instanceof Double) {
|
||||||
|
return (double) number % 1 == 0 ? (long) number : number;
|
||||||
|
} else {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> String orElsePlaceholder(T t, Function<T, Object> value, Object fallback) {
|
||||||
|
if (t == null) {
|
||||||
|
return getPlaceholder(String.valueOf(fallback));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return String.valueOf(value.apply(t));
|
||||||
|
} catch (NullPointerException ex) {
|
||||||
|
return getPlaceholder(String.valueOf(fallback));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T, R> R orElse(T t, Function<T, R> value, R fallback) {
|
||||||
|
if (t == null) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return value.apply(t);
|
||||||
|
} catch (NullPointerException ex) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPlaceholder(String name) {
|
||||||
|
return plugin.getConfig().getString("placeholders." + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIdentifier() {
|
||||||
|
return "rankup";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthor() {
|
||||||
|
return String.join(", ", plugin.getDescription().getAuthors());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getVersion() {
|
||||||
|
return plugin.getDescription().getVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package sh.okx.rankup.ranks;
|
||||||
|
|
||||||
|
public class Prestige {
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package sh.okx.rankup.ranks;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
import sh.okx.rankup.messages.MessageBuilder;
|
||||||
|
import sh.okx.rankup.messages.Variable;
|
||||||
|
import sh.okx.rankup.ranks.requirements.Requirement;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BinaryOperator;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class Rank {
|
||||||
|
private final Rankup plugin;
|
||||||
|
@Getter
|
||||||
|
private final String name;
|
||||||
|
@Getter
|
||||||
|
private final String next;
|
||||||
|
@Getter
|
||||||
|
private final String rank;
|
||||||
|
private final Set<Requirement> requirements;
|
||||||
|
private final BinaryOperator<Boolean> reducer;
|
||||||
|
private final List<String> commands;
|
||||||
|
|
||||||
|
public static Rank deserialize(Rankup plugin, ConfigurationSection section) {
|
||||||
|
String rank = section.getString("rank");
|
||||||
|
|
||||||
|
Set<Requirement> requirements = new HashSet<>();
|
||||||
|
BinaryOperator<Boolean> reducer = null;
|
||||||
|
ConfigurationSection requirementsSection = section.getConfigurationSection("requirements");
|
||||||
|
if(requirementsSection != null) {
|
||||||
|
for (Map.Entry<String, Object> entry : requirementsSection.getValues(false).entrySet()) {
|
||||||
|
String name = entry.getKey();
|
||||||
|
double amount = Double.parseDouble(String.valueOf(entry.getValue()));
|
||||||
|
|
||||||
|
Requirement requirement = plugin.getRequirementRegistry().newRequirement(name, amount);
|
||||||
|
if (requirement == null) {
|
||||||
|
plugin.getLogger().warning("Unknown requirement " + name);
|
||||||
|
} else {
|
||||||
|
requirements.add(requirement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String operation = section.getString("operation");
|
||||||
|
if (operation == null) {
|
||||||
|
operation = "and";
|
||||||
|
}
|
||||||
|
switch (operation) {
|
||||||
|
case "and":
|
||||||
|
reducer = (a, b) -> a && b;
|
||||||
|
break;
|
||||||
|
case "or":
|
||||||
|
reducer = (a, b) -> a || b;
|
||||||
|
break;
|
||||||
|
case "xor":
|
||||||
|
reducer = (a, b) -> (a && !b) || (b && !a);
|
||||||
|
break;
|
||||||
|
case "none":
|
||||||
|
reducer = (a, b) -> !a && !b;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid operation type for rank " + rank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Rank(plugin,
|
||||||
|
section.getName(),
|
||||||
|
section.getString("next"),
|
||||||
|
rank,
|
||||||
|
requirements,
|
||||||
|
reducer,
|
||||||
|
section.getStringList("commands"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkRequirements(Player player) {
|
||||||
|
return requirements.stream()
|
||||||
|
.map(requirement -> requirement.check(player))
|
||||||
|
.reduce(reducer)
|
||||||
|
.orElse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInRank(Player player) {
|
||||||
|
String[] groups = plugin.getPermissions().getPlayerGroups(player);
|
||||||
|
for (String group : groups) {
|
||||||
|
if(group.equalsIgnoreCase(rank)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastRank() {
|
||||||
|
return next == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Requirement getRequirement(String name) {
|
||||||
|
for(Requirement requirement : requirements) {
|
||||||
|
if(requirement.getName().equalsIgnoreCase(name)) {
|
||||||
|
return requirement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyRequirements(Player player) {
|
||||||
|
for(Requirement requirement : requirements) {
|
||||||
|
requirement.apply(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runCommands(Player player, Rank nextRank) {
|
||||||
|
for (String command : commands) {
|
||||||
|
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), new MessageBuilder(command)
|
||||||
|
.replace(Variable.PLAYER, player.getName())
|
||||||
|
.replace(Variable.OLD_RANK, rank)
|
||||||
|
.replace(Variable.OLD_RANK_NAME, name)
|
||||||
|
.replace(Variable.RANK, nextRank.rank)
|
||||||
|
.replace(Variable.RANK_NAME, nextRank.name)
|
||||||
|
.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if(!(o instanceof Rank)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ((Rank) o).name.equals(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package sh.okx.rankup.ranks;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Rankups {
|
||||||
|
@Getter
|
||||||
|
private final FileConfiguration config;
|
||||||
|
private final Set<Rank> ranks = new HashSet<>();
|
||||||
|
|
||||||
|
public Rankups(Rankup plugin, FileConfiguration config) {
|
||||||
|
this.config = config;
|
||||||
|
for (Map.Entry<String, Object> entry : config.getValues(false).entrySet()) {
|
||||||
|
ConfigurationSection rankSection = (ConfigurationSection) entry.getValue();
|
||||||
|
ranks.add(Rank.deserialize(plugin, rankSection));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rank getFirstRank() {
|
||||||
|
OUTER:
|
||||||
|
for(Rank rank : ranks) {
|
||||||
|
// see if anything ranks up to this
|
||||||
|
for(Rank rank0 : ranks) {
|
||||||
|
if(!rank0.isLastRank() && rank0.getNext().equals(rank.getName())) {
|
||||||
|
continue OUTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// nothing ranks up to this
|
||||||
|
return rank;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rank getRank(String name) {
|
||||||
|
for(Rank rank : ranks) {
|
||||||
|
if(rank.getName().equals(name)) {
|
||||||
|
return rank;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rank getRank(Player player) {
|
||||||
|
return ranks.stream()
|
||||||
|
.filter(rank -> rank.isInRank(player))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rank nextRank(Rank rank) {
|
||||||
|
if(rank.isLastRank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Rank nextRank : ranks) {
|
||||||
|
if (rank.getNext().equalsIgnoreCase(nextRank.getName())) {
|
||||||
|
return nextRank;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// this shouldn't happen but whatever
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public boolean hasNext(Rank start, Rank rank) {
|
||||||
|
// while(!start.isLastRank()) {
|
||||||
|
// start = nextRank(rank);
|
||||||
|
// if(start.equals(rank)) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package sh.okx.rankup.ranks.requirements;
|
||||||
|
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
|
||||||
|
public class MoneyRequirement extends Requirement {
|
||||||
|
public MoneyRequirement(Rankup plugin, String name) {
|
||||||
|
super(plugin, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MoneyRequirement(Requirement clone) {
|
||||||
|
super(clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(Player player) {
|
||||||
|
Economy economy = plugin.getEconomy();
|
||||||
|
double balance = economy.getBalance(player);
|
||||||
|
return balance >= amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Player player) {
|
||||||
|
Economy economy = plugin.getEconomy();
|
||||||
|
economy.withdrawPlayer(player, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getRemaining(Player player) {
|
||||||
|
return Math.max(0, amount - plugin.getEconomy().getBalance(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Requirement clone() {
|
||||||
|
return new MoneyRequirement(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package sh.okx.rankup.ranks.requirements;
|
||||||
|
|
||||||
|
import org.bukkit.Statistic;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
|
||||||
|
public class PlaytimeHoursRequirement extends Requirement {
|
||||||
|
private static final int TICKS_PER_HOUR = 20 * 60 * 60;
|
||||||
|
|
||||||
|
public PlaytimeHoursRequirement(Rankup plugin, String name) {
|
||||||
|
super(plugin, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PlaytimeHoursRequirement(Requirement clone) {
|
||||||
|
super(clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(Player player) {
|
||||||
|
return player.getStatistic(Statistic.PLAY_ONE_MINUTE) * TICKS_PER_HOUR >= amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Player player) {
|
||||||
|
// well, we can't really take hours of playtime away, can we?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getRemaining(Player player) {
|
||||||
|
return amount - (player.getStatistic(Statistic.PLAY_ONE_MINUTE) * TICKS_PER_HOUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Requirement clone() {
|
||||||
|
return new PlaytimeHoursRequirement(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package sh.okx.rankup.ranks.requirements;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
|
||||||
|
public abstract class Requirement implements Cloneable {
|
||||||
|
protected Rankup plugin;
|
||||||
|
@Getter
|
||||||
|
protected String name;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
protected double amount;
|
||||||
|
|
||||||
|
public Requirement(Rankup plugin, String name) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Requirement(Requirement clone) {
|
||||||
|
if(clone != null) {
|
||||||
|
this.plugin = clone.plugin;
|
||||||
|
this.name = clone.name;
|
||||||
|
this.amount = clone.amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a player meets this requirement
|
||||||
|
* @param player the player to check
|
||||||
|
* @return true if they meet the requirement, false otherwise
|
||||||
|
*/
|
||||||
|
public abstract boolean check(Player player);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the effect of this requirement to the player.
|
||||||
|
* For money, this could be taking money away from the player.
|
||||||
|
* You can assume that <code>Requirement#check(Player)</code> has been called,
|
||||||
|
* and has returned true immediately prior to this.
|
||||||
|
* @param player the player to take from
|
||||||
|
*/
|
||||||
|
public abstract void apply(Player player);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the remaining amount needed for <code>Requirement#check(Player)</code> to yield true.
|
||||||
|
* This is not required and is only used in placeholders.
|
||||||
|
* @param player the player to find the remaining amount of
|
||||||
|
* @return the remaining amount needed. Should be non-negative.
|
||||||
|
*/
|
||||||
|
public double getRemaining(Player player) {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
public abstract Requirement clone();
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package sh.okx.rankup.ranks.requirements;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class RequirementRegistry {
|
||||||
|
private Set<Requirement> requirements = new HashSet<>();
|
||||||
|
|
||||||
|
public void addRequirement(Requirement requirement) {
|
||||||
|
requirements.add(requirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Requirement newRequirement(String name, double amount) {
|
||||||
|
for(Requirement requirement : requirements) {
|
||||||
|
if(requirement.getName().equalsIgnoreCase(name)) {
|
||||||
|
Requirement newRequirement = requirement.clone();
|
||||||
|
newRequirement.setAmount(amount);
|
||||||
|
return newRequirement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package sh.okx.rankup.ranks.requirements;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import sh.okx.rankup.Rankup;
|
||||||
|
|
||||||
|
public class XpLevelRequirement extends Requirement {
|
||||||
|
public XpLevelRequirement(Rankup plugin, String name) {
|
||||||
|
super(plugin, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XpLevelRequirement(Requirement clone) {
|
||||||
|
super(clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAmount(double amount) {
|
||||||
|
// experience level should be a whole number
|
||||||
|
super.setAmount(Math.round(amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(Player player) {
|
||||||
|
return player.getLevel() >= amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Player player) {
|
||||||
|
player.setLevel(player.getLevel() - (int) amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getRemaining(Player player) {
|
||||||
|
return Math.max(0, amount - player.getLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Requirement clone() {
|
||||||
|
return new XpLevelRequirement(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# this is used for letting you know that you need to update/change your config file
|
||||||
|
version: 0
|
||||||
|
|
||||||
|
# how people should confirm ranking up
|
||||||
|
# options are: gui, text or none
|
||||||
|
confirmation-type: 'gui'
|
||||||
|
|
||||||
|
gui:
|
||||||
|
rows: 1
|
||||||
|
rankup:
|
||||||
|
material: EMERALD_BLOCK
|
||||||
|
# index can be separated by spaces to show in multiple ways
|
||||||
|
# ie, 0-3 9-12 18-21
|
||||||
|
# you can also just use a single number instead of a range.
|
||||||
|
index: 0-3
|
||||||
|
name: '&a&lConfirm'
|
||||||
|
# lore is optional
|
||||||
|
lore: '&6Rankup to &b{RANK}'
|
||||||
|
cancel:
|
||||||
|
material: REDSTONE_BLOCK
|
||||||
|
index: 5-8
|
||||||
|
name: '&c&lCancel'
|
||||||
|
fill:
|
||||||
|
name: ' '
|
||||||
|
# if you are using a 1.8-1.12 and you want to change this
|
||||||
|
# you can use MATERIAL:data, for example STAINED_GLASS_PANE:8
|
||||||
|
# this works for both the rankup and cancel blocks as well
|
||||||
|
material: BLACK_STAINED_GLASS_PANE
|
||||||
|
|
||||||
|
text:
|
||||||
|
# the time in seconds for a player to
|
||||||
|
# confirm ranking up by typing the command again
|
||||||
|
timeout: 10
|
||||||
|
|
||||||
|
placeholders:
|
||||||
|
# format for money. for more information, see
|
||||||
|
# https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html
|
||||||
|
money-format: "#,##0.##"
|
||||||
|
percent-format: "0.##"
|
||||||
|
# the format used for requirements
|
||||||
|
simple-format: "#.##"
|
||||||
|
# used for current_rank and next_rank placeholders when a player not in anything in rankups.yml
|
||||||
|
not-in-ladder: "None"
|
||||||
|
# used in next_rank placeholders when there is no rankup
|
||||||
|
highest-rank: "None"
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# the messages in this section can be customised for each rankup in rankups.yml.
|
||||||
|
rankup:
|
||||||
|
# NOTE: if you are using requirements for your ranks that are NOT money,
|
||||||
|
# you will want to change this for each rank!
|
||||||
|
requirements-not-met: "&cYou need {MONEY} money to rankup."
|
||||||
|
no-rankup: "&eYou are at the highest rank."
|
||||||
|
# set to an empty string, ie: success-public: ""
|
||||||
|
# to hide that message.
|
||||||
|
success-public: "&a{PLAYER} &ehas ranked up to: &d{RANK}"
|
||||||
|
success-private: "&aYou have ranked up to: &d{RANK}"
|
||||||
|
# used for the text confirmation
|
||||||
|
confirmation: |-
|
||||||
|
&eAre you sure you want to rankup to &a{RANK}&e?
|
||||||
|
&eType &c/rankup &eagain to confirm.
|
||||||
|
# used for the GUI confirmation
|
||||||
|
title: "Rankup to {RANK}"
|
||||||
|
|
||||||
|
# It is HIGHLY RECOMMENDED you override these in rankups.yml
|
||||||
|
# to show the specific requirements for each rank.
|
||||||
|
# however if you are just using money, you can use {MONEY} or {MONEY_NEEDED} or {PERCENT_DONE} or {PERCENT_LEFT}
|
||||||
|
# for example:
|
||||||
|
#ranks:
|
||||||
|
# complete: "&7{OLD_RANK} &8\xbb &7{RANK} &efor &7${MONEY}"
|
||||||
|
# current: "&c{OLD_RANK} &e\xbb &c{RANK} &efor &a${MONEY} &e{PERCENT_DONE}%"
|
||||||
|
# incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &efor &a${MONEY}"
|
||||||
|
ranks:
|
||||||
|
complete: "&7{OLD_RANK} &8\xbb &7{RANK}"
|
||||||
|
current: "&c{OLD_RANK} &e\xbb &c{RANK}"
|
||||||
|
incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}"
|
||||||
|
ranks:
|
||||||
|
# an empty string disables the header/footer
|
||||||
|
header: ''
|
||||||
|
footer: ''
|
||||||
|
|
||||||
|
not-in-ladder: "&cSorry, but we could not find any rankups for the group(s) you are in."
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
name: Rankup
|
||||||
|
version: 3.0-alpha
|
||||||
|
main: sh.okx.rankup.Rankup
|
||||||
|
author: Okx
|
||||||
|
depend: [Vault]
|
||||||
|
softdepend: [PlaceholderAPI]
|
||||||
|
api-version: 1.13
|
||||||
|
|
||||||
|
commands:
|
||||||
|
rankup:
|
||||||
|
permission: rankup.rankup
|
||||||
|
description: Rankup.
|
||||||
|
rankup3:
|
||||||
|
permission: rankup3.info
|
||||||
|
description: View Rankup version and perform some administrative commands.
|
||||||
|
# support the old command
|
||||||
|
aliases: [pru]
|
||||||
|
ranks:
|
||||||
|
permission: rankup.ranks
|
||||||
|
description: List all the ranks.
|
||||||
|
permissions:
|
||||||
|
rankup.*:
|
||||||
|
children:
|
||||||
|
rankup.info: true
|
||||||
|
rankup.rankup: true
|
||||||
|
rankup.checkversion: true
|
||||||
|
rankup.ranks: true
|
||||||
|
rankup.reload: true
|
||||||
|
rankup.ranks: true
|
||||||
|
rankup.info:
|
||||||
|
default: true
|
||||||
|
rankup.rankup:
|
||||||
|
default: true
|
||||||
|
rankup.checkversion:
|
||||||
|
default: op
|
||||||
|
rankup.ranks:
|
||||||
|
default: true
|
||||||
|
rankup.reload:
|
||||||
|
default: op
|
||||||
|
rankup.ranks:
|
||||||
|
default: true
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
Aexample:
|
||||||
|
# the name of the rank in your permissions plugin
|
||||||
|
rank: 'A'
|
||||||
|
# the next rank a player can rank up to.
|
||||||
|
# this must be the name of the configuration section.
|
||||||
|
# for example, the name of this configuration section is "Aexample".
|
||||||
|
# this is not required.
|
||||||
|
next: 'Bexample'
|
||||||
|
# List of requirements to go to the next rank
|
||||||
|
# (ie, this example will charge 1000 money to rankup from A to B)
|
||||||
|
# money: money from the server economy
|
||||||
|
# xp-level: amount of experience levels
|
||||||
|
# playtime-hours: hours a player has played
|
||||||
|
# custom requirements can also be added by other plugins.
|
||||||
|
requirements:
|
||||||
|
money: 1000
|
||||||
|
# What requirements players need to match to /rankup.
|
||||||
|
# this is optional - if you don't use it, it defaults to "and"
|
||||||
|
# n.b. if there are no requirements players will always be able to /rankup.
|
||||||
|
# and: all requirements
|
||||||
|
# or: at least one requirement
|
||||||
|
# xor: only one requirement
|
||||||
|
# none: no requirements
|
||||||
|
operation: and
|
||||||
|
# the console will run these commands when a player ranks up
|
||||||
|
#commands:
|
||||||
|
# this will run when a player ranks up from A to B.
|
||||||
|
#- 'say {PLAYER} well done for ranking up from {OLD_RANK} to {RANK}!'
|
||||||
|
Bexample:
|
||||||
|
rank: 'B'
|
||||||
|
next: 'Cexample'
|
||||||
|
requirements:
|
||||||
|
money: 2500
|
||||||
|
Cexample:
|
||||||
|
rank: 'C'
|
||||||
|
next: 'Dexample'
|
||||||
|
requirements:
|
||||||
|
money: 5000
|
||||||
|
xp-level: 2
|
||||||
|
# you can have a custom messages too.
|
||||||
|
# you can use this to list the requirements needed.
|
||||||
|
rankup:
|
||||||
|
requirements-not-met: '&cYou need 5000 money and 2 levels of XP to rankup to D.'
|
||||||
|
ranks:
|
||||||
|
complete: "&7{OLD_RANK} &8\xbb &7{RANK} &e(5000 money, 2 XP levels)"
|
||||||
|
current: "&c{OLD_RANK} &e\xbb &c{RANK} &e(5000 money, 2 XP levels)"
|
||||||
|
incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &e(5000 money, 2 XP levels)"
|
||||||
|
Dexample:
|
||||||
|
rank: 'D'
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package sh.okx.rankup;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
public class RankupTest {
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user