diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..07bf2f0
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,25 @@
+plugins {
+ id 'java'
+}
+
+group = 'com.extendedclip.papi'
+version = '2.1.0'
+
+repositories {
+ mavenCentral()
+ maven {
+ name = 'spigotmc-repo'
+ url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/'
+ }
+ maven {
+ name = 'papi-repo'
+ url = 'https://repo.extendedclip.com/content/repositories/placeholderapi/'
+ }
+}
+
+dependencies {
+ compileOnly 'org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT'
+ compileOnly 'me.clip:placeholderapi:2.11.3'
+ compileOnly 'org.projectlombok:lombok:1.18.28'
+ annotationProcessor 'org.projectlombok:lombok:1.18.28'
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..033e24c
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..62f495d
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..fcb6fca
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# 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 ;; #(
+ MSYS* | 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
+ if ! command -v java >/dev/null 2>&1
+ then
+ 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
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..93e3f59
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 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!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index d834a40..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
- 4.0.0
-
- com.extendedclip.papi.expansion.player
- player-expansion
- 2.0.5
-
- PAPI-Expansion-Player
- PlaceholderAPI expansion for player placeholders
-
-
- 1.8
- 1.8
- UTF-8
-
-
-
-
- spigot-repo
- https://hub.spigotmc.org/nexus/content/repositories/snapshots/
-
-
- placeholderapi
- https://repo.extendedclip.com/content/repositories/placeholderapi/
-
-
-
-
-
-
- org.spigotmc
- spigot-api
- 1.20-R0.1-SNAPSHOT
- provided
-
-
- me.clip
- placeholderapi
- 2.11.3
- provided
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 2.3.2
-
- ${project.name}
-
-
- true
- true
-
-
-
-
-
-
-
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..266905a
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'Player-Expansion'
\ No newline at end of file
diff --git a/src/main/java/com/extendedclip/papi/expansion/player/PlayerExpansion.java b/src/main/java/com/extendedclip/papi/expansion/player/PlayerExpansion.java
index 062c0f3..97ec6ca 100644
--- a/src/main/java/com/extendedclip/papi/expansion/player/PlayerExpansion.java
+++ b/src/main/java/com/extendedclip/papi/expansion/player/PlayerExpansion.java
@@ -21,469 +21,336 @@
package com.extendedclip.papi.expansion.player;
+import lombok.Getter;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.Configurable;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
-import org.bukkit.Bukkit;
-import org.bukkit.ChatColor;
-import org.bukkit.Material;
-import org.bukkit.OfflinePlayer;
-import org.bukkit.World;
-import org.bukkit.enchantments.Enchantment;
+import me.clip.placeholderapi.expansion.Taskable;
+import org.bukkit.*;
+import org.bukkit.block.BlockFace;
+import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
+import java.text.SimpleDateFormat;
import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.durability;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.format12;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.format24;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.getBiome;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.getCapitalizedBiome;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.getEmptySlots;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.getDirection;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.getTotalExperience;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.getXZDirection;
-import static com.extendedclip.papi.expansion.player.PlayerUtil.itemInHand;
-
-public final class PlayerExpansion extends PlaceholderExpansion implements Configurable {
- private String low;
- private String medium;
- private String high;
-
- private int mediumValue;
- private int highValue;
-
- private String north;
- private String northEast;
- private String east;
- private String southEast;
- private String south;
- private String southWest;
- private String west;
- private String northWest;
- @Override
- public String getIdentifier() {
- return "player";
+public final class PlayerExpansion extends PlaceholderExpansion implements Taskable, Configurable {
+
+ @Getter private final String identifier = "player";
+ @Getter private final String author = "clip";
+ @Getter private final String version = "2.1.0";
+ @Getter private final Map defaults;
+
+ private final SimpleDateFormat dateFormat = PlaceholderAPIPlugin.getDateFormat();
+ private final VersionHelper versionHelper;
+ private final PlayerListener listener;
+
+ private final Map pingColors = new HashMap<>();
+ private final Map directions = new HashMap<>();
+ final Map joinTimes = new HashMap<>();
+ final Map damagesGiven = new HashMap<>();
+
+ public PlayerExpansion() {
+ defaults = new HashMap<>() {{
+ put("ping",new HashMap<>() {{
+ put("-1", "&c");
+ put("0-100", "&a");
+ put("100-150", "&e");
+ put("150-", "&c");
+ }});
+
+ put("direction.north", "N");
+ put("direction.north_east", "NE");
+ put("direction.east", "E");
+ put("direction.south_east", "SE");
+ put("direction.south", "S");
+ put("direction.south_west", "SW");
+ put("direction.west", "W");
+ put("direction.north_west", "NW");
+ }};
+ versionHelper = new VersionHelper();
+ listener = new PlayerListener(this);
}
@Override
- public String getAuthor() {
- return "clip";
- }
+ public void start() {
+ ConfigurationSection ping = getConfigSection("ping");
+ if (ping != null) ping.getValues(false).forEach((range,color)->pingColors.put(range,String.valueOf(color)));
- @Override
- public String getVersion() {
- return "2.0.5";
+
+ ConfigurationSection directionsCfg = getConfigSection("direction");
+ if (directionsCfg != null) directionsCfg.getValues(false).forEach((direction,output)->{
+ try {
+ BlockFace face = BlockFace.valueOf(direction.toUpperCase());
+ directions.put(face,String.valueOf(output));
+ }
+ catch (Exception ignored) {}
+ });
+
+ Bukkit.getServer().getPluginManager().registerEvents(listener,getPlaceholderAPI());
}
@Override
- public Map getDefaults() {
- Map defaults = new HashMap<>();
- defaults.put("ping_color.high", "&c");
- defaults.put("ping_color.medium", "&e");
- defaults.put("ping_color.low", "&a");
- defaults.put("ping_value.medium", 50);
- defaults.put("ping_value.high", 100);
- defaults.put("direction.north", "N");
- defaults.put("direction.north_east", "NE");
- defaults.put("direction.east", "E");
- defaults.put("direction.south_east", "SE");
- defaults.put("direction.south", "S");
- defaults.put("direction.south_west", "SW");
- defaults.put("direction.west", "W");
- defaults.put("direction.north_west", "NW");
- return defaults;
+ public void stop() {
+ HandlerList.unregisterAll(listener);
}
@Override
public String onRequest(OfflinePlayer player, String identifier) {
- final boolean targetedPing = identifier.startsWith("ping_");
final boolean targetedColoredPing = identifier.startsWith("colored_ping_");
- if (targetedPing || targetedColoredPing) {
- final Player target = Bukkit.getPlayer(identifier.substring(targetedPing ? 5 : 13)); // yes, I know, magic value
-
+ if (identifier.startsWith("ping_") || targetedColoredPing) {
+ final Player target = Bukkit.getServer().getPlayer(identifier.substring(targetedColoredPing ? 13 : 5));
return target == null ? "0" : retrievePing(target, targetedColoredPing);
}
- if (player == null) {
- return "";
- }
-
- // offline placeholders
- switch (identifier) {
- case "name":
- return player.getName();
- case "uuid":
- return player.getUniqueId().toString();
- case "has_played_before":
- return bool(player.hasPlayedBefore());
- case "online":
- return bool(player.isOnline());
- case "is_whitelisted":
- return bool(player.isWhitelisted());
- case "is_banned":
- return bool(player.isBanned());
- case "is_op":
- return bool(player.isOp());
- case "first_played":
- case "first_join":
- return String.valueOf(player.getFirstPlayed());
- case "first_played_formatted":
- case "first_join_date":
- return PlaceholderAPIPlugin.getDateFormat().format(new Date(player.getFirstPlayed()));
- case "last_played":
- case "last_join":
- return String.valueOf(player.getLastPlayed());
- case "last_played_formatted":
- case "last_join_date":
- return PlaceholderAPIPlugin.getDateFormat().format(new Date(player.getLastPlayed()));
- case "bed_x":
- return player.getBedSpawnLocation() != null ? String.valueOf(player.getBedSpawnLocation().getX()) : "";
- case "bed_y":
- return player.getBedSpawnLocation() != null ? String.valueOf(player.getBedSpawnLocation().getY()) : "";
- case "bed_z":
- return player.getBedSpawnLocation() != null ? String.valueOf(player.getBedSpawnLocation().getZ()) : "";
- case "bed_world":
- return player.getBedSpawnLocation() != null ? player.getBedSpawnLocation().getWorld()
- .getName() : "";
-
- }
+ if (player == null) return "";
- // online placeholders
- if (!player.isOnline()) {
- return "";
- }
+ Object output = request(player,identifier);
+ return output == null ? null
+ : output instanceof Boolean bool ?
+ bool ? PlaceholderAPIPlugin.booleanTrue()
+ : PlaceholderAPIPlugin.booleanFalse()
+ : String.valueOf(output);
+ }
- Player p = player.getPlayer();
+ public Object request(OfflinePlayer player, String identifier) {
+ return switch (identifier) {
+ // offline placeholders
+ case "name" -> player.getName();
+ case "uuid" -> player.getUniqueId();
+ case "has_played_before" -> player.hasPlayedBefore();
+ case "online" -> player.isOnline();
+ case "is_whitelisted" -> player.isWhitelisted();
+ case "is_banned" -> player.isBanned();
+ case "is_op" -> player.isOp();
+ case "first_played", "first_join" -> player.getFirstPlayed();
+ case "first_played_formatted", "first_join_date" -> dateFormat.format(new Date(player.getFirstPlayed()));
+ case "last_played", "last_join" -> player.getLastPlayed();
+ case "last_played_formatted", "last_join_date" -> dateFormat.format(new Date(player.getLastPlayed()));
+ case "time_since_last_played", "time_since_last_join" -> PlayerUtil.msToSToStr(player.getLastPlayed());
+ case "bed_x", "bed_y", "bed_z", "bed_world" -> PlayerUtil.getLocation(player.getBedSpawnLocation(),identifier.substring(4));
+ default -> {
+ // online placeholders
+ if (!player.isOnline()) yield "";
+
+ Player p = player.getPlayer();
+
+ // to get rid of IDE warnings
+ if (p == null) yield "";
+
+ if (identifier.startsWith("has_permission_"))
+ yield p.hasPermission(identifier.substring(15));
+
+ if (identifier.startsWith("has_potion_effect_")) {
+ String effect = identifier.substring(18);
+ PotionEffectType potion = PotionEffectType.getByName(effect);
+ yield potion != null && p.hasPotionEffect(potion);
+ }
- // to get rid of IDE warnings
- if (p == null) {
- return "";
- }
+ if (identifier.startsWith("potion_effect_level_")) {
+ String effect = identifier.substring(20);
+ PotionEffectType potion = PotionEffectType.getByName(effect);
+ if (potion == null || !p.hasPotionEffect(potion)) yield "0";
+ PotionEffect potionEffect = p.getPotionEffect(potion);
+ yield potionEffect == null ? "0" : potionEffect.getAmplifier();
+ }
- if (identifier.startsWith("has_permission_")) {
- if (identifier.split("has_permission_").length > 1) {
- String perm = identifier.split("has_permission_")[1];
- return bool(p.hasPermission(perm));
- }
- return bool(false);
- }
+ if (identifier.startsWith("has_unlocked_recipe_")) {
+ String recipe = identifier.substring(20);
+ NamespacedKey key = NamespacedKey.fromString(recipe);
+ yield key != null && p.hasDiscoveredRecipe(key);
+ }
- if (identifier.startsWith("has_potioneffect_")) {
- if (identifier.split("has_potioneffect_").length > 1) {
- String effect = identifier.split("has_potioneffect_")[1];
- PotionEffectType potion = PotionEffectType.getByName(effect);
- return bool(p.hasPotionEffect(potion));
- }
- }
+ if (identifier.startsWith("item_in_hand_level_"))
+ yield PlayerUtil.getItemEnchantment(identifier.substring(19),versionHelper.getItemInHand(p));
+ if (identifier.startsWith("item_in_offhand_level_"))
+ yield PlayerUtil.getItemEnchantment(identifier.substring(22),p.getInventory().getItemInOffHand());
+
+ if (identifier.startsWith("locale")) {
+ String localeStr = versionHelper.getLocale(p);
+
+ yield switch (identifier) {
+ case "locale" -> localeStr;
+ case "locale_short" -> localeStr.substring(0, localeStr.indexOf("_"));
+ case "locale_country", "locale_display_country", "locale_display_name" -> {
+ String localeStrISO = localeStr.replace("_", "-");
+ Locale locale = Locale.forLanguageTag(localeStrISO);
+ if (locale == null) yield "";
+ yield switch (identifier) {
+ case "locale_country" -> locale.getCountry();
+ case "locale_display_country" -> locale.getDisplayCountry();
+ case "locale_display_name" -> locale.getDisplayName();
+ default -> null;
+ };
+ }
+ default -> null;
+ };
+ }
- if (identifier.startsWith("item_in_hand_level_")) {
- if (identifier.split("item_in_hand_level_").length > 1) {
- String enchantment = identifier.split("item_in_hand_level_")[1];
- return String.valueOf(itemInHand(p).getEnchantmentLevel(Enchantment.getByName(enchantment)));
+ yield switch (identifier) {
+ case "displayname" -> p.getDisplayName();
+ case "list_name" -> p.getPlayerListName();
+ case "custom_name" -> p.getCustomName() != null ? p.getCustomName() : p.getName();
+ case "gamemode" -> p.getGameMode().name();
+ case "ip" -> p.getAddress() == null ? "" : p.getAddress().getAddress().getHostAddress();
+ case "ping" -> retrievePing(p, false);
+ case "colored_ping" -> retrievePing(p, true);
+
+ case "is_sleeping" -> p.isSleeping();
+ case "is_conversing" -> p.isConversing();
+ case "is_dead" -> p.isDead();
+ case "is_sneaking" -> p.isSneaking();
+ case "is_sprinting" -> p.isSprinting();
+ case "is_leashed" -> p.isLeashed();
+ case "is_inside_vehicle" -> p.isInsideVehicle();
+
+ case "world" -> p.getWorld().getName();
+ case "world_type" -> switch (p.getWorld().getEnvironment()) {
+ case NETHER -> "Nether";
+ case THE_END -> "The End";
+ case NORMAL -> "Overworld";
+ case CUSTOM -> "Custom";
+ };
+ case "biome","biome_capitalized" -> {
+ String biome = String.valueOf(p.getLocation().getBlock().getBiome());
+ if (identifier.equals("biome")) yield biome;
+
+ String[] biomeWords = biome.split("_");
+ for (int i = 0; i < biomeWords.length; i++) {
+ biomeWords[i] = biomeWords[i].substring(0, 1).toUpperCase() + biomeWords[i].substring(1).toLowerCase();
+ }
+ yield String.join(" ", biomeWords);
+ }
+
+ case "x_long", "y_long", "z_long", "yaw", "pitch" -> PlayerUtil.getLocation(p.getLocation(),identifier.contains("_")
+ ? identifier.substring(0,identifier.indexOf("_"))
+ : identifier);
+ case "x","y","z" -> PlayerUtil.getLocation(p.getLocation(),"block_"+identifier);
+ case "block_underneath" -> p.getLocation().clone().subtract(0, 1, 0).getBlock().getType();
+ case "compass_x", "compass_y", "compass_z", "compass_world" -> PlayerUtil.getLocation(p.getCompassTarget(),identifier.substring(8));
+
+ case "light_level" -> p.getLocation().getBlock().getLightLevel();
+
+ case "direction" -> {
+ BlockFace direction = PlayerUtil.getDirection(p);
+ yield directions.getOrDefault(direction,direction.toString());
+ }
+ case "direction_xz" -> PlayerUtil.getXZDirection(p);
+
+ case "allow_flight" -> p.getAllowFlight();
+ case "is_flying" -> p.isFlying();
+ case "fly_speed" -> p.getFlySpeed();
+ case "walk_speed" -> p.getWalkSpeed();
+
+ case "exp" -> p.getExp();
+ case "current_exp" -> PlayerUtil.getTotalExperience(p);
+ case "total_exp" -> p.getTotalExperience();
+ case "exp_to_level" -> p.getExpToLevel();
+ case "level" -> p.getLevel();
+
+ case "absorption" -> versionHelper.getAbsorption(p);
+ case "health" -> p.getHealth();
+ case "health_rounded" -> Math.round(p.getHealth());
+ case "health_scale" -> p.getHealthScale();
+ case "has_health_boost" -> p.hasPotionEffect(PotionEffectType.HEALTH_BOOST);
+ case "health_boost" -> PlayerUtil.getHealthBoost(p);
+ case "health_full" -> p.getHealth()+versionHelper.getAbsorption(p)+PlayerUtil.getHealthBoost(p);
+ case "health_full_rounded" -> Math.round(p.getHealth()+versionHelper.getAbsorption(p)+PlayerUtil.getHealthBoost(p));
+ case "max_health" -> versionHelper.getMaxHealth(p);
+ case "max_health_rounded" -> Math.round(versionHelper.getMaxHealth(p));
+ case "food_level" -> p.getFoodLevel();
+ case "saturation" -> p.getSaturation();
+ case "max_air" -> p.getMaximumAir();
+ case "remaining_air" -> p.getRemainingAir();
+ case "max_no_damage_ticks" -> p.getMaximumNoDamageTicks();
+ case "no_damage_ticks" -> p.getNoDamageTicks();
+ case "last_damage", "last_damage_taken" -> p.getLastDamage();
+ case "last_damage_given" -> damagesGiven.getOrDefault(p,0D);
+
+ case "has_empty_slot" -> p.getInventory().firstEmpty() > -1;
+ case "empty_slots" -> PlayerUtil.getEmptySlots(p);
+ case "can_pickup_items" -> p.getCanPickupItems();
+
+ case "item_in_hand","item_in_hand_name","item_in_hand_data","item_in_hand_durability",
+ "item_in_offhand","item_in_offhand_name","item_in_offhand_data","item_in_offhand_durability",
+ "armor_helmet_name","armor_helmet_data","armor_helmet_durability",
+ "armor_chestplate_name","armor_chestplate_data","armor_chestplate_durability",
+ "armor_leggings_name","armor_leggings_data","armor_leggings_durability",
+ "armor_boots_name","armor_boots_data","armor_boots_durability" -> {
+ if (identifier.startsWith("item_in_hand")) yield requestItem(identifier,12,versionHelper.getItemInHand(p));
+ if (identifier.startsWith("item_in_offhand")) yield requestItem(identifier,15,p.getInventory().getItemInOffHand());
+ if (identifier.startsWith("armor_helmet")) yield requestItem(identifier,12,p.getInventory().getHelmet());
+ if (identifier.startsWith("armor_chestplate")) yield requestItem(identifier,16,p.getInventory().getChestplate());
+ if (identifier.startsWith("armor_leggings")) yield requestItem(identifier,14,p.getInventory().getLeggings());
+ if (identifier.startsWith("armor_boots")) yield requestItem(identifier,11,p.getInventory().getBoots());
+ yield null;
+ }
+
+ case "time_since_join" -> PlayerUtil.msToSToStr(joinTimes.getOrDefault(p,0L));
+ case "sleep_ticks" -> p.getSleepTicks();
+ case "thunder_duration" -> p.getWorld().getThunderDuration();
+ case "ticks_lived" -> p.getTicksLived();
+ case "seconds_lived" -> p.getTicksLived() / 20;
+ case "minutes_lived" -> p.getTicksLived() / 1200;
+
+ case "world_time" -> p.getWorld().getTime();
+ case "world_time_12" -> PlayerUtil.format12(p.getWorld().getTime());
+ case "world_time_24" -> PlayerUtil.format24(p.getWorld().getTime());
+ case "time" -> p.getPlayerTime();
+ case "time_offset" -> p.getPlayerTimeOffset();
+ case "weather_duration" -> p.getWorld().getWeatherDuration();
+
+ default -> null;
+ };
}
- return "0";
- }
- if (identifier.startsWith("item_in_offhand_level_")) {
- if (identifier.split("item_in_offhand_level_").length > 1) {
- String enchantment = identifier.split("item_in_offhand_level_")[1];
- return String.valueOf(p.getInventory().getItemInOffHand().getEnchantmentLevel(Enchantment.getByName(enchantment)));
- }
- return "0";
- }
+ };
+ }
- if (identifier.startsWith("locale")) {
- String localeStr = PlayerUtil.getLocale(p);
- String localeStrISO = localeStr.replace("_", "-");
-
- switch (identifier) {
- case "locale":
- return localeStr;
- case "locale_country":
- Locale locale = Locale.forLanguageTag(localeStrISO);
- if (locale == null)
- return "";
- return locale.getCountry();
- case "locale_display_country":
- locale = Locale.forLanguageTag(localeStrISO);
- if (locale == null)
- return "";
- return locale.getDisplayCountry();
- case "locale_display_name":
- locale = Locale.forLanguageTag(localeStrISO);
- if (locale == null)
- return "";
- return locale.getDisplayName();
- case "locale_short":
- return localeStr.substring(0, localeStr.indexOf("_"));
+ private Object requestItem(String identifier, int substring, ItemStack item) {
+ identifier = identifier.substring(substring);
+ if (item == null) return identifier.isEmpty() || identifier.equals("_name") ? ""
+ : identifier.equals("_data") || identifier.equals("_durability") ? "0" : null;
+ return switch (identifier) {
+ case "" -> item.getType();
+ case "_name" -> {
+ ItemMeta meta = item.getItemMeta();
+ yield item.getType() != Material.AIR && meta != null && meta.hasDisplayName() ? meta.getDisplayName() : "";
}
- }
-
- switch (identifier) {
- case "absorption": {
- if (VersionHelper.HAS_ABSORPTION_METHODS) {
- return Integer.toString((int) p.getAbsorptionAmount());
- } else {
- return "-1";
- }
+ case "_lore" -> {
+ ItemMeta meta = item.getItemMeta();
+ yield meta != null && meta.hasLore() && meta.getLore() != null ? String.join("\n",meta.getLore()) : "";
}
- case "has_empty_slot":
- return bool(p.getInventory().firstEmpty() > -1);
- case "empty_slots":
- return String.valueOf(getEmptySlots(p));
- case "server":
- case "servername":
- return "now available in the server expansion";
- case "displayname":
- return p.getDisplayName();
- case "list_name":
- return p.getPlayerListName();
- case "gamemode":
- return p.getGameMode().name();
- case "direction":
- switch (getDirection(p)) {
- case NORTH:
- return north;
- case NORTH_EAST:
- return northEast;
- case EAST:
- return east;
- case SOUTH_EAST:
- return southEast;
- case SOUTH:
- return south;
- case SOUTH_WEST:
- return southWest;
- case WEST:
- return west;
- case NORTH_WEST:
- return northWest;
- }
- return "";
- case "direction_xz":
- return getXZDirection(p);
- case "world":
- return p.getWorld().getName();
- case "world_type":
- World.Environment environment = p.getWorld().getEnvironment();
- if (environment == World.Environment.NETHER) {
- return "Nether";
- } else if (environment == World.Environment.THE_END) {
- return "The End";
- } else if (environment == World.Environment.NORMAL) {
- return "Overworld";
- }
- return "";
- case "x":
- return String.valueOf(p.getLocation().getBlockX());
- case "x_long":
- return String.valueOf(p.getLocation().getX());
- case "y":
- return String.valueOf(p.getLocation().getBlockY());
- case "y_long":
- return String.valueOf(p.getLocation().getY());
- case "z":
- return String.valueOf(p.getLocation().getBlockZ());
- case "z_long":
- return String.valueOf(p.getLocation().getZ());
- case "yaw":
- return String.valueOf(p.getLocation().getYaw());
- case "pitch":
- return String.valueOf(p.getLocation().getPitch());
- case "biome":
- return getBiome(p);
- case "biome_capitalized":
- return getCapitalizedBiome(p);
- case "light_level":
- return String.valueOf(p.getLocation().getBlock().getLightLevel());
- case "ip":
- return p.getAddress().getAddress().getHostAddress();
- case "allow_flight":
- return bool(p.getAllowFlight());
- case "can_pickup_items":
- return bool(p.getCanPickupItems());
- case "compass_x":
- return p.getCompassTarget() != null ? String.valueOf(p.getCompassTarget().getBlockX()) : "";
- case "compass_y":
- return p.getCompassTarget() != null ? String.valueOf(p.getCompassTarget().getBlockY()) : "";
- case "compass_z":
- return p.getCompassTarget() != null ? String.valueOf(p.getCompassTarget().getBlockZ()) : "";
- case "compass_world":
- return p.getCompassTarget() != null ? p.getCompassTarget().getWorld().getName() : "";
- case "block_underneath":
- return String.valueOf(p.getLocation().clone().subtract(0, 1, 0).getBlock().getType());
- case "custom_name":
- return p.getCustomName() != null ? p.getCustomName() : p.getName();
- case "exp":
- return String.valueOf(p.getExp());
- case "current_exp":
- return String.valueOf(getTotalExperience(p));
- case "total_exp":
- return String.valueOf(p.getTotalExperience());
- case "exp_to_level":
- return String.valueOf(p.getExpToLevel());
- case "level":
- return String.valueOf(p.getLevel());
- case "fly_speed":
- return String.valueOf(p.getFlySpeed());
- case "food_level":
- return String.valueOf(p.getFoodLevel());
- case "health":
- return String.valueOf(p.getHealth());
- case "health_rounded":
- return String.valueOf(Math.round(p.getHealth()));
- case "health_scale":
- return String.valueOf(p.getHealthScale());
- case "has_health_boost":
- return bool(p.hasPotionEffect(PotionEffectType.HEALTH_BOOST));
- case "health_boost": {
- if (p.getHealthScale() > 20) {
- return Double.toString(p.getHealthScale() - 20);
- } else {
- return "0";
- }
+ case "_data", "_durability" -> {
+ int damage = versionHelper.getItemDamage(item);
+ yield identifier.equals("_data") ? damage : item.getType().getMaxDurability() - damage;
}
- case "item_in_hand":
- return String.valueOf(itemInHand(p).getType());
- case "item_in_hand_name":
- return itemInHand(p).getType() != Material.AIR && itemInHand(p).getItemMeta().hasDisplayName() ? itemInHand(p).getItemMeta().getDisplayName() : "";
- case "item_in_hand_data":
- return itemInHand(p).getType() != Material.AIR ? String.valueOf(itemInHand(p).getDurability()) : "0";
- case "item_in_hand_durability":
- return String.valueOf(durability(itemInHand(p)));
- case "item_in_offhand":
- return String.valueOf(p.getInventory().getItemInOffHand().getType());
- case "item_in_offhand_name":
- return p.getInventory().getItemInOffHand().getType() != Material.AIR && p.getInventory().getItemInOffHand().getItemMeta().hasDisplayName() ? p.getInventory().getItemInOffHand().getItemMeta().getDisplayName() : "";
- case "item_in_offhand_data":
- return p.getInventory().getItemInOffHand().getType() != Material.AIR ? String.valueOf(p.getInventory().getItemInOffHand().getDurability()) : "0";
- case "item_in_offhand_durability":
- return String.valueOf(durability(p.getInventory().getItemInOffHand()));
- case "last_damage":
- return String.valueOf(p.getLastDamage());
- case "max_health":
- return String.valueOf(p.getMaxHealth());
- case "max_health_rounded":
- return String.valueOf(Math.round(p.getMaxHealth()));
- case "max_air":
- return String.valueOf(p.getMaximumAir());
- case "max_no_damage_ticks":
- return String.valueOf(p.getMaximumNoDamageTicks());
- case "no_damage_ticks":
- return String.valueOf(p.getNoDamageTicks());
- case "armor_helmet_name":
- return Optional.ofNullable(p.getInventory().getHelmet()).map(a -> a.getItemMeta().getDisplayName()).orElse("");
- case "armor_helmet_data":
- return p.getInventory().getHelmet() != null ? String.valueOf(p.getInventory().getHelmet().getDurability()) : "0";
- case "armor_helmet_durability":
- return String.valueOf(durability(p.getInventory().getHelmet()));
- case "armor_chestplate_name":
- return Optional.ofNullable(p.getInventory().getChestplate()).map(a -> a.getItemMeta().getDisplayName()).orElse("");
- case "armor_chestplate_data":
- return p.getInventory().getChestplate() != null ? String.valueOf(p.getInventory().getChestplate().getDurability()) : "0";
- case "armor_chestplate_durability":
- return String.valueOf(durability(p.getInventory().getChestplate()));
- case "armor_leggings_name":
- return Optional.ofNullable(p.getInventory().getLeggings()).map(a -> a.getItemMeta().getDisplayName()).orElse("");
- case "armor_leggings_data":
- return p.getInventory().getLeggings() != null ? String.valueOf(p.getInventory().getLeggings().getDurability()) : "0";
- case "armor_leggings_durability":
- return String.valueOf(durability(p.getInventory().getLeggings()));
- case "armor_boots_name":
- return Optional.ofNullable(p.getInventory().getBoots()).map(a -> a.getItemMeta().getDisplayName()).orElse("");
- case "armor_boots_data":
- return p.getInventory().getBoots() != null ? String.valueOf(p.getInventory().getBoots().getDurability()) : "0";
- case "armor_boots_durability":
- return String.valueOf(durability(p.getInventory().getBoots()));
- case "ping":
- return retrievePing(p, false);
- case "colored_ping":
- return retrievePing(p, true);
- case "time":
- return String.valueOf(p.getPlayerTime());
- case "time_offset":
- return String.valueOf(p.getPlayerTimeOffset());
- case "remaining_air":
- return String.valueOf(p.getRemainingAir());
- case "saturation":
- return String.valueOf(p.getSaturation());
- case "sleep_ticks":
- return String.valueOf(p.getSleepTicks());
- case "thunder_duration":
- return String.valueOf(p.getWorld().getThunderDuration());
- case "ticks_lived":
- return String.valueOf(p.getTicksLived());
- case "seconds_lived":
- return String.valueOf(p.getTicksLived() / 20);
- case "minutes_lived":
- return String.valueOf((p.getTicksLived() / 20) / 60);
- case "walk_speed":
- return String.valueOf(p.getWalkSpeed());
- case "weather_duration":
- return String.valueOf(p.getWorld().getWeatherDuration());
- case "world_time":
- return String.valueOf(p.getWorld().getTime());
- case "world_time_12":
- return format12(p.getWorld().getTime());
- case "world_time_24":
- return format24(p.getWorld().getTime());
- case "is_flying":
- return bool(p.isFlying());
- case "is_sleeping":
- return bool(p.isSleeping());
- case "is_conversing":
- return bool(p.isConversing());
- case "is_dead":
- return bool(p.isDead());
- case "is_sneaking":
- return bool(p.isSneaking());
- case "is_sprinting":
- return bool(p.isSprinting());
- case "is_leashed":
- return bool(p.isLeashed());
- case "is_inside_vehicle":
- return bool(p.isInsideVehicle());
- }
- // return null for unknown placeholders
- return null;
- }
-
- @Override
- public boolean register() {
- low = this.getString("ping_color.low", "&a");
- medium = this.getString("ping_color.medium", "&e");
- high = this.getString("ping_color.high", "&c");
- mediumValue = this.getInt("ping_value.medium", 50);
- highValue = this.getInt("ping_value.high", 100);
- north = this.getString("direction.north", "N");
- northEast = this.getString("direction.north_east", "NE");
- east = this.getString("direction.east", "E");
- southEast = this.getString("direction.south_east", "SE");
- south = this.getString("direction.south", "S");
- southWest = this.getString("direction.south_west", "SW");
- west = this.getString("direction.west", "W");
- northWest = this.getString("direction.north_west", "NW");
-
-
- return super.register();
- }
-
- public String bool(boolean b) {
- return b ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse();
+ default -> null;
+ };
}
-
private String retrievePing(final Player player, final boolean colored) {
- final int ping = PlayerUtil.getPing(player);
- if (!colored) {
- return String.valueOf(ping);
+ int ping = versionHelper.getPing(player);
+ String pingStr = String.valueOf(ping);
+ if (!colored) return pingStr;
+
+ for (String range : pingColors.keySet()) {
+ if (range.contains("-") && !range.startsWith("-")) {
+ String[] bounds = range.split("-");
+ int bound1 = Integer.parseInt(bounds[0]);
+ if (bounds.length == 1 && ping < bound1) continue;
+ int bound2 = Integer.parseInt(bounds[1]);
+ if (ping < bound1 || ping > bound2) continue;
+ } else if (!pingStr.equals(range)) continue;
+
+ return ChatColor.translateAlternateColorCodes('&', pingColors.get(range)+pingStr);
}
-
- return ChatColor.translateAlternateColorCodes('&', ping > highValue ? high : ping > mediumValue ? medium : low) + ping;
+ return pingStr;
}
}
diff --git a/src/main/java/com/extendedclip/papi/expansion/player/PlayerListener.java b/src/main/java/com/extendedclip/papi/expansion/player/PlayerListener.java
new file mode 100644
index 0000000..96c4c35
--- /dev/null
+++ b/src/main/java/com/extendedclip/papi/expansion/player/PlayerListener.java
@@ -0,0 +1,27 @@
+package com.extendedclip.papi.expansion.player;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+public record PlayerListener(PlayerExpansion expansion) implements Listener {
+
+ @EventHandler
+ public void onJoin(PlayerJoinEvent e) {
+ expansion.joinTimes.put(e.getPlayer(),System.currentTimeMillis());
+ }
+
+ @EventHandler
+ public void onLeave(PlayerQuitEvent e) {
+ expansion.joinTimes.remove(e.getPlayer());
+ }
+
+ @EventHandler
+ public void onDamage(EntityDamageByEntityEvent e) {
+ if (e.getDamager() instanceof Player player) expansion.damagesGiven.put(player,e.getFinalDamage());
+ }
+
+}
diff --git a/src/main/java/com/extendedclip/papi/expansion/player/PlayerUtil.java b/src/main/java/com/extendedclip/papi/expansion/player/PlayerUtil.java
index 3254b91..c7f8d48 100644
--- a/src/main/java/com/extendedclip/papi/expansion/player/PlayerUtil.java
+++ b/src/main/java/com/extendedclip/papi/expansion/player/PlayerUtil.java
@@ -1,20 +1,3 @@
-package com.extendedclip.papi.expansion.player;
-
-import org.bukkit.Bukkit;
-import org.bukkit.Material;
-import org.bukkit.block.BlockFace;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.inventory.PlayerInventory;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Locale;
-import java.util.function.Function;
-
/*
*
* Player-Expansion
@@ -35,6 +18,24 @@
*
*
*/
+
+package com.extendedclip.papi.expansion.player;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.BlockFace;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
public final class PlayerUtil {
public static final int ticksAtMidnight = 18000;
@@ -45,94 +46,10 @@ public final class PlayerUtil {
private static final SimpleDateFormat twelve = new SimpleDateFormat("h:mm aa", Locale.ENGLISH);
private static final BlockFace[] radial = { BlockFace.NORTH, BlockFace.NORTH_EAST, BlockFace.EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH, BlockFace.SOUTH_WEST, BlockFace.WEST, BlockFace.NORTH_WEST };
- private PlayerUtil() { }
-
- private static final Function PLAYER_GET_PING = new Function() {
-
- private Field ping;
- private Method getHandle;
-
- @Override
- public Integer apply(final Player player) {
- if (ping == null) {
- try {
- cacheReflection(player);
- } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException | NoSuchFieldException ex) {
- ex.printStackTrace();
- }
- }
-
- try {
- return ping.getInt(getHandle.invoke(player));
- } catch (final IllegalAccessException | InvocationTargetException ex) {
- ex.printStackTrace();
- }
- return -1;
- }
-
-
- private void cacheReflection(final Player player) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
- getHandle = player.getClass().getDeclaredMethod("getHandle");
- getHandle.setAccessible(true);
-
- final Object entityPlayer = getHandle.invoke(player);
-
- String pingFieldName;
- if (VersionHelper.IS_1_20_OR_NEWER) {
- pingFieldName = "f";
- } else if (VersionHelper.IS_1_17_OR_NEWER) {
- pingFieldName = "e";
- } else {
- pingFieldName = "ping";
- }
+ private PlayerUtil() {}
- ping = entityPlayer.getClass().getDeclaredField(pingFieldName);
- ping.setAccessible(true);
- }
- };
-
- private static final Function PLAYER_GET_LOCALE = new Function() {
-
- private Field locale;
- private Method getHandle;
-
- @Override
- public String apply(final Player player) {
- if (locale == null) {
- try {
- cacheReflection(player);
- } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException | NoSuchFieldException ex) {
- ex.printStackTrace();
- }
- }
-
- try {
- final Object entityPlayer = getHandle.invoke(player);
- return (String) locale.get(entityPlayer);
- } catch (final IllegalAccessException | InvocationTargetException ex) {
- ex.printStackTrace();
- }
- return "en_US";
- }
-
-
- private void cacheReflection(final Player player) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
- getHandle = player.getClass().getDeclaredMethod("getHandle");
- getHandle.setAccessible(true);
-
- final Object entityPlayer = getHandle.invoke(player);
-
- locale = entityPlayer.getClass().getField("locale");
- }
- };
-
-
- public static int getPing(final Player player) {
- return PLAYER_GET_PING.apply(player);
- }
-
- public static String getLocale(final Player player) {
- return PLAYER_GET_LOCALE.apply(player);
+ public static String msToSToStr(double ms) {
+ return String.valueOf(Math.round((System.currentTimeMillis()-ms)/1000));
}
public static String format12(long ticks) {
@@ -164,60 +81,35 @@ public static BlockFace getDirection(Player player) {
public static String getXZDirection(Player player) {
double rotation = player.getLocation().getYaw();
- if (rotation < 0.0D) {
- rotation += 360.0D;
- }
-
- if (Math.abs(rotation) <= 45 || Math.abs(rotation - 360) <= 45) {
- return "+Z";
- } else if (Math.abs(rotation - 90) <= 45) {
- return "-X";
- } else if (Math.abs(rotation - 180) <= 45) {
- return "-Z";
- } else if (Math.abs(rotation - 270) <= 45) {
- return "+X";
- }
+ if (rotation < 0.0D) rotation += 360.0D;
+ if (Math.abs(rotation) <= 45 || Math.abs(rotation - 360) <= 45) return "+Z";
+ if (Math.abs(rotation - 90) <= 45) return "-X";
+ if (Math.abs(rotation - 180) <= 45) return "-Z";
+ if (Math.abs(rotation - 270) <= 45) return "+X";
return "";
}
- public static ItemStack itemInHand(Player p) {
- try {
- return p.getInventory().getItemInMainHand();
- } catch (NoSuchMethodError e) {
- return p.getInventory().getItemInHand();
- }
- }
-
- public static int durability(ItemStack item) {
- return item != null ? item.getType().getMaxDurability() - item.getDurability() : 0;
- }
-
public static int getEmptySlots(Player p) {
int slots = 0;
PlayerInventory inv = p.getInventory();
- for (ItemStack is : inv.getContents()) {
+ for (ItemStack is : inv.getStorageContents())
if (is == null) slots++;
- }
- if (!Bukkit.getBukkitVersion().contains("1.7") && !Bukkit.getBukkitVersion().contains("1.8")) {
- if (inv.getItemInOffHand() == null || inv.getItemInOffHand().getType() == Material.AIR) slots--;
- if (inv.getHelmet() == null) slots--;
- if (inv.getChestplate() == null) slots--;
- if (inv.getLeggings() == null) slots--;
- if (inv.getBoots() == null) slots--;
- }
+ if (Bukkit.getServer().getBukkitVersion().contains("1.7") || Bukkit.getServer().getBukkitVersion().contains("1.8"))
+ return slots;
+
+ if (inv.getItemInOffHand() == null || inv.getItemInOffHand().getType() == Material.AIR) slots--;
+
return slots;
}
private static int getExperienceAtLevel(int level) {
- if (level <= 15) {
- return (level << 1) + 7;
- }
- if (level <= 30) {
- return (level * 5) - 38;
- }
- return (level * 9) - 158;
+ return level <= 15
+ ? (level << 1) + 7
+ : level <= 30
+ ? (level * 5) - 38
+ : (level * 9) - 158;
}
public static int getTotalExperience(Player player) {
@@ -227,21 +119,34 @@ public static int getTotalExperience(Player player) {
currentLevel--;
experience += getExperienceAtLevel(currentLevel);
}
- if (experience < 0) {
- experience = 0;
- }
- return experience;
+ return Math.max(experience, 0);
}
- public static String getBiome(Player p) {
- return String.valueOf(p.getLocation().getBlock().getBiome());
+ public static Object getLocation(Location location, String pos) {
+ if (location == null) return "";
+ return switch (pos) {
+ case "x" -> location.getX();
+ case "y" -> location.getY();
+ case "z" -> location.getZ();
+ case "world" -> location.getWorld() == null ? "" : location.getWorld().getName();
+ case "block_x" -> location.getBlockX();
+ case "block_y" -> location.getBlockY();
+ case "block_z" -> location.getBlockZ();
+ default -> "";
+ };
}
- public static String getCapitalizedBiome(Player p) {
- String[] biomeWords = getBiome(p).split("_");
- for (int i = 0; i < biomeWords.length; i++) {
- biomeWords[i] = biomeWords[i].substring(0, 1).toUpperCase() + biomeWords[i].substring(1).toLowerCase();
- }
- return String.join(" ", biomeWords);
+ @SuppressWarnings("deprecation")
+ public static String getItemEnchantment(String enchant, ItemStack item) {
+ Enchantment enchantment = Enchantment.getByName(enchant);
+ if (enchantment == null) return "0";
+ return String.valueOf(item.getEnchantmentLevel(enchantment));
}
+
+ public static int getHealthBoost(Player player) {
+ if (!player.hasPotionEffect(PotionEffectType.HEALTH_BOOST)) return 0;
+ PotionEffect potionEffect = player.getPotionEffect(PotionEffectType.HEALTH_BOOST);
+ return potionEffect == null ? 0 : potionEffect.getAmplifier()*2+2;
+ }
+
}
diff --git a/src/main/java/com/extendedclip/papi/expansion/player/VersionHelper.java b/src/main/java/com/extendedclip/papi/expansion/player/VersionHelper.java
index c3ec0ea..41bb849 100644
--- a/src/main/java/com/extendedclip/papi/expansion/player/VersionHelper.java
+++ b/src/main/java/com/extendedclip/papi/expansion/player/VersionHelper.java
@@ -1,8 +1,17 @@
package com.extendedclip.papi.expansion.player;
-import com.google.common.primitives.Ints;
+import me.clip.placeholderapi.PlaceholderAPIPlugin;
import org.bukkit.Bukkit;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.Damageable;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -11,27 +20,49 @@
*/
public final class VersionHelper {
- private static final int VERSION = getCurrentVersion();
+ private final int VERSION = getCurrentVersion();
- /**
- *
- * Damageable#getAbsorptionAmount
- *
- */
- public static final boolean HAS_ABSORPTION_METHODS = VERSION >= 1_15_0;
+ private final boolean IS_1_9_OR_NEWER = VERSION >= 1_9_0;
+ private final boolean HAS_LOCALE_METHOD = VERSION >= 1_12_0;
+ private final boolean IS_1_17_OR_NEWER = VERSION >= 1_17_0;
- public static final boolean IS_1_17_OR_NEWER = VERSION >= 1_17_0;
+ private Method getHandle;
+ private Field ping;
+ private Field locale;
- public static final boolean IS_1_20_OR_NEWER = VERSION >= 1_20_0;
+ public VersionHelper() {
+ if (IS_1_17_OR_NEWER) return;
+ try {
+ String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
+ getHandle = Class.forName("org.bukkit.craftbukkit."+ version +".entity.CraftPlayer").getDeclaredMethod("getHandle");
+ getHandle.setAccessible(true);
- private VersionHelper() { }
+ Class> entityPlayerClass = Class.forName("net.minecraft.server."+version+".EntityPlayer");
+ try {
+ ping = entityPlayerClass.getDeclaredField("ping");
+ ping.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ PlaceholderAPIPlugin.getInstance().getLogger().log(Level.SEVERE, "Could not get access ping field! Player ping placeholders won't work.");
+ }
+
+ if (HAS_LOCALE_METHOD) return;
+ try {
+ locale = entityPlayerClass.getField("locale");
+ locale.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ PlaceholderAPIPlugin.getInstance().getLogger().log(Level.SEVERE, "Could not get access locale field! Player locale placeholders won't work.");
+ }
+ } catch (Exception e) {
+ PlaceholderAPIPlugin.getInstance().getLogger().log(Level.SEVERE, "Could not load NMS classes correctly! Ping and Locale placeholders may not work.");
+ }
+ }
/**
* Gets the current server version
*
* @return A protocol like number representing the version, for example 1.16.5 - 1165
*/
- private static int getCurrentVersion() {
+ private int getCurrentVersion() {
// No need to cache since will only run once
final Matcher matcher = Pattern.compile("(?\\d+\\.\\d+)(?\\.\\d+)?").matcher(Bukkit.getBukkitVersion());
final StringBuilder stringBuilder = new StringBuilder();
@@ -44,14 +75,43 @@ private static int getCurrentVersion() {
.append((patch == null) ? "0" : patch.replace(".", ""));
}
- final Integer version = Ints.tryParse(stringBuilder.toString());
-
- // Should never fail
- if (version == null) {
+ try {
+ return Integer.parseInt(stringBuilder.toString());
+ } catch (NumberFormatException e) {
throw new IllegalArgumentException("Could not retrieve server version!");
}
+ }
- return version;
+ public int getPing(Player player) {
+ if (IS_1_17_OR_NEWER) return player.getPing();
+ try {return (int) ping.get(getHandle.invoke(player));}
+ catch (IllegalAccessException | InvocationTargetException e) {return -1;}
+ }
+
+ public String getLocale(Player player) {
+ if (HAS_LOCALE_METHOD) return player.getLocale();
+ try {return (String) locale.get(getHandle.invoke(player));}
+ catch (IllegalAccessException | InvocationTargetException e) {return "en_US";}
+ }
+
+ public double getAbsorption(Player player) {
+ return VERSION >= 1_15_0 ? player.getAbsorptionAmount() : -1;
+ }
+ @SuppressWarnings("deprecation")
+ public double getMaxHealth(Player p) {
+ return IS_1_9_OR_NEWER ? Objects.requireNonNull(p.getAttribute(Attribute.GENERIC_MAX_HEALTH)).getValue() : p.getMaxHealth();
+ }
+
+ @SuppressWarnings("deprecation")
+ public ItemStack getItemInHand(Player p) {
+ return IS_1_9_OR_NEWER ? p.getInventory().getItemInMainHand() : p.getInventory().getItemInHand();
+ }
+ @SuppressWarnings("deprecation")
+ public int getItemDamage(ItemStack item) {
+ return VERSION >= 1_13_0 ? item.getItemMeta() instanceof Damageable damageable
+ ? damageable.getDamage()
+ : 0
+ : item.getDurability();
}
}