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(); } }