Performance Improvements (#340)

* Performance Improvements

* More Optimizations

* Even More Optimizations & Cleanups

* Almost a recode I guess
This commit is contained in:
Crypto Morin 2020-07-16 09:32:22 -07:00 committed by GitHub
parent f9f59f1f96
commit 54d5757d0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 876 additions and 875 deletions

36
pom.xml

@ -5,8 +5,8 @@
<groupId>me.clip</groupId> <groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId> <artifactId>placeholderapi</artifactId>
<version>2.10.7-DEV-${BUILD_NUMBER}</version> <version>2.10.7-DEV-${BUILD_NUMBER}</version>
<name>PlaceholderAPI</name> <name>PlaceholderAPI</name>
<description>An awesome placeholder provider!</description> <description>An awesome placeholder provider!</description>
<url>http://extendedclip.com</url> <url>http://extendedclip.com</url>
@ -33,7 +33,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot</artifactId>
<version>1.16.1-R0.1-SNAPSHOT</version> <version>1.16.1-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -45,7 +45,7 @@
<dependency> <dependency>
<groupId>org.bstats</groupId> <groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId> <artifactId>bstats-bukkit</artifactId>
<version>1.5</version> <version>1.7</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>me.rayzr522</groupId> <groupId>me.rayzr522</groupId>
@ -84,27 +84,27 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version> <version>3.2.4</version>
<configuration>
<minimizeJar>false</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>me.clip.placeholderapi.util</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.code.gson</pattern>
<shadedPattern>me.clip.placeholderapi.libs.gson</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
<goals> <goals>
<goal>shade</goal> <goal>shade</goal>
</goals> </goals>
<configuration>
<minimizeJar>false</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>me.clip.placeholderapi.metrics</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.code.gson</pattern>
<shadedPattern>me.clip.placeholderapi.libs.gson</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>

@ -20,14 +20,13 @@
*/ */
package me.clip.placeholderapi; package me.clip.placeholderapi;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import me.clip.placeholderapi.events.ExpansionRegisterEvent; import me.clip.placeholderapi.events.ExpansionRegisterEvent;
import me.clip.placeholderapi.events.ExpansionUnregisterEvent; import me.clip.placeholderapi.events.ExpansionUnregisterEvent;
import me.clip.placeholderapi.expansion.Cacheable;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.expansion.Relational; import me.clip.placeholderapi.expansion.Relational;
import me.clip.placeholderapi.util.Msg;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
@ -35,18 +34,17 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static me.clip.placeholderapi.util.Msg.color; import static me.clip.placeholderapi.util.Msg.color;
public class PlaceholderAPI { public class PlaceholderAPI {
protected static final Map<String, PlaceholderHook> PLACEHOLDERS = new ConcurrentHashMap<>();
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("[%]([^%]+)[%]"); private static final Pattern PERCENT_PLACEHOLDER_PATTERN = Pattern.compile("[%]([^%]+)[%]");
private static final Pattern BRACKET_PLACEHOLDER_PATTERN = Pattern.compile("[{]([^{}]+)[}]"); private static final Pattern BRACKET_PLACEHOLDER_PATTERN = Pattern.compile("[{]([^{}]+)[}]");
private static final Pattern RELATIONAL_PLACEHOLDER_PATTERN = Pattern.compile("[%](rel_)([^%]+)[%]"); private static final Pattern RELATIONAL_PLACEHOLDER_PATTERN = Pattern.compile("[%](rel_)([^%]+)[%]");
private static final Map<String, PlaceholderHook> placeholders = new HashMap<>();
private PlaceholderAPI() { private PlaceholderAPI() {
} }
@ -58,9 +56,7 @@ public class PlaceholderAPI {
* @return true if identifier is already registered * @return true if identifier is already registered
*/ */
public static boolean isRegistered(String identifier) { public static boolean isRegistered(String identifier) {
return getRegisteredIdentifiers().stream() return PLACEHOLDERS.containsKey(identifier.toLowerCase(Locale.ENGLISH));
.filter(id -> id.equalsIgnoreCase(identifier))
.findFirst().orElse(null) != null;
} }
/** /**
@ -73,15 +69,11 @@ public class PlaceholderAPI {
* registered for the specified identifier * registered for the specified identifier
*/ */
public static boolean registerPlaceholderHook(String identifier, PlaceholderHook placeholderHook) { public static boolean registerPlaceholderHook(String identifier, PlaceholderHook placeholderHook) {
Validate.notNull(identifier, "Identifier can not be null"); Validate.notEmpty(identifier, "Placeholder identifier cannot be null or empty");
Validate.notNull(placeholderHook, "Placeholderhook can not be null"); Objects.requireNonNull(placeholderHook, "Placeholder hook cannot be null");
if (isRegistered(identifier)) {
return false;
}
placeholders.put(identifier.toLowerCase(), placeholderHook);
if (isRegistered(identifier)) return false;
PLACEHOLDERS.put(identifier.toLowerCase(Locale.ENGLISH), placeholderHook);
return true; return true;
} }
@ -93,8 +85,8 @@ public class PlaceholderAPI {
* placeholder hook registered for the identifier specified * placeholder hook registered for the identifier specified
*/ */
public static boolean unregisterPlaceholderHook(String identifier) { public static boolean unregisterPlaceholderHook(String identifier) {
Validate.notNull(identifier, "Identifier can not be null"); Validate.notEmpty(identifier, "Identifier cannot be null");
return placeholders.remove(identifier.toLowerCase()) != null; return PLACEHOLDERS.remove(identifier.toLowerCase(Locale.ENGLISH)) != null;
} }
/** /**
@ -103,7 +95,7 @@ public class PlaceholderAPI {
* @return All registered placeholder identifiers * @return All registered placeholder identifiers
*/ */
public static Set<String> getRegisteredIdentifiers() { public static Set<String> getRegisteredIdentifiers() {
return ImmutableSet.copyOf(placeholders.keySet()); return ImmutableSet.copyOf(PLACEHOLDERS.keySet());
} }
/** /**
@ -112,15 +104,16 @@ public class PlaceholderAPI {
* @return Copy of the internal placeholder map * @return Copy of the internal placeholder map
*/ */
public static Map<String, PlaceholderHook> getPlaceholders() { public static Map<String, PlaceholderHook> getPlaceholders() {
return ImmutableMap.copyOf(placeholders); return ImmutableMap.copyOf(PLACEHOLDERS);
} }
public static Set<PlaceholderExpansion> getExpansions() { public static Set<PlaceholderExpansion> getExpansions() {
Set<PlaceholderExpansion> set = getPlaceholders().values().stream() Set<PlaceholderExpansion> expansions = new HashSet<>();
.filter(PlaceholderExpansion.class::isInstance).map(PlaceholderExpansion.class::cast) for (PlaceholderHook expansion : PLACEHOLDERS.values()) {
.collect(Collectors.toCollection(HashSet::new)); if (expansion.isExpansion()) expansions.add((PlaceholderExpansion) expansion);
}
return ImmutableSet.copyOf(set); return ImmutableSet.copyOf(expansions);
} }
/** /**
@ -130,7 +123,7 @@ public class PlaceholderAPI {
* @return true if String contains any registered placeholder identifiers, false otherwise * @return true if String contains any registered placeholder identifiers, false otherwise
*/ */
public static boolean containsPlaceholders(String text) { public static boolean containsPlaceholders(String text) {
return text != null && PLACEHOLDER_PATTERN.matcher(text).find(); return !Strings.isNullOrEmpty(text) && PERCENT_PLACEHOLDER_PATTERN.matcher(text).find();
} }
/** /**
@ -140,7 +133,7 @@ public class PlaceholderAPI {
* @return true if String contains any registered placeholder identifiers, false otherwise * @return true if String contains any registered placeholder identifiers, false otherwise
*/ */
public static boolean containsBracketPlaceholders(String text) { public static boolean containsBracketPlaceholders(String text) {
return text != null && BRACKET_PLACEHOLDER_PATTERN.matcher(text).find(); return !Strings.isNullOrEmpty(text) && BRACKET_PLACEHOLDER_PATTERN.matcher(text).find();
} }
/** /**
@ -177,7 +170,7 @@ public class PlaceholderAPI {
* @return String containing all translated placeholders * @return String containing all translated placeholders
*/ */
public static List<String> setPlaceholders(OfflinePlayer player, List<String> text) { public static List<String> setPlaceholders(OfflinePlayer player, List<String> text) {
return setPlaceholders(player, text, PLACEHOLDER_PATTERN, true); return setPlaceholders(player, text, PERCENT_PLACEHOLDER_PATTERN, true);
} }
/** /**
@ -190,7 +183,7 @@ public class PlaceholderAPI {
* @return String containing all translated placeholders * @return String containing all translated placeholders
*/ */
public static List<String> setPlaceholders(OfflinePlayer player, List<String> text, boolean colorize) { public static List<String> setPlaceholders(OfflinePlayer player, List<String> text, boolean colorize) {
return setPlaceholders(player, text, PLACEHOLDER_PATTERN, colorize); return setPlaceholders(player, text, PERCENT_PLACEHOLDER_PATTERN, colorize);
} }
/** /**
@ -219,13 +212,13 @@ public class PlaceholderAPI {
* @return String containing all translated placeholders * @return String containing all translated placeholders
*/ */
public static List<String> setPlaceholders(OfflinePlayer player, List<String> text, Pattern pattern, boolean colorize) { public static List<String> setPlaceholders(OfflinePlayer player, List<String> text, Pattern pattern, boolean colorize) {
if (text == null) { if (text == null) return null;
return null; List<String> lines = new ArrayList<>();
}
return text.stream() for (String line : text) {
.map(line -> setPlaceholders(player, line, pattern, colorize)) lines.add(setPlaceholders(player, line, pattern, colorize));
.collect(Collectors.toList()); }
return lines;
} }
/** /**
@ -262,7 +255,7 @@ public class PlaceholderAPI {
* @return String containing all translated placeholders * @return String containing all translated placeholders
*/ */
public static String setPlaceholders(OfflinePlayer player, String text) { public static String setPlaceholders(OfflinePlayer player, String text) {
return setPlaceholders(player, text, PLACEHOLDER_PATTERN); return PlaceholderReplacer.evaluatePlaceholders(player, text, PlaceholderReplacer.Closure.PERCENT, false);
} }
/** /**
@ -275,7 +268,7 @@ public class PlaceholderAPI {
* @return The text containing the parsed placeholders * @return The text containing the parsed placeholders
*/ */
public static String setPlaceholders(OfflinePlayer player, String text, boolean colorize) { public static String setPlaceholders(OfflinePlayer player, String text, boolean colorize) {
return setPlaceholders(player, text, PLACEHOLDER_PATTERN, colorize); return setPlaceholders(player, text, PERCENT_PLACEHOLDER_PATTERN, colorize);
} }
/** /**
@ -304,43 +297,51 @@ public class PlaceholderAPI {
* @return The text containing the parsed placeholders * @return The text containing the parsed placeholders
*/ */
public static String setPlaceholders(OfflinePlayer player, String text, Pattern pattern, boolean colorize) { public static String setPlaceholders(OfflinePlayer player, String text, Pattern pattern, boolean colorize) {
if (text == null) { if (text == null) return null;
return null; if (PLACEHOLDERS.isEmpty()) return colorize ? color(text) : text;
}
if (placeholders.isEmpty()) {
return colorize ? color(text) : text;
}
final Matcher matcher = pattern.matcher(text);
final Map<String, PlaceholderHook> hooks = getPlaceholders();
Matcher matcher = pattern.matcher(text);
while (matcher.find()) { while (matcher.find()) {
final String format = matcher.group(1); String format = matcher.group(1);
final int index = format.indexOf("_"); int index = format.indexOf('_');
if (index <= 0 || index >= format.length()) continue;
if (index <= 0 || index >= format.length()) { // We don't need to use getPlaceholders() because we know what we're doing and we won't modify the map.
continue; // And instead of looking for the element twice using contains() and get() we only get it and check if it's null.
} String identifier = format.substring(0, index).toLowerCase(Locale.ENGLISH);
PlaceholderHook handler = PLACEHOLDERS.get(identifier);
final String identifier = format.substring(0, index).toLowerCase(); if (handler != null) {
final String params = format.substring(index + 1); String params = format.substring(index + 1);
final PlaceholderHook hook = hooks.get(identifier); String value = handler.onRequest(player, params);
if (hook == null) { if (value != null) {
continue; text = text.replaceAll(Pattern.quote(matcher.group()), Matcher.quoteReplacement(value));
} }
final String value = hook.onRequest(player, params);
if (value != null) {
text = text.replaceAll(Pattern.quote(matcher.group()), Matcher.quoteReplacement(value));
} }
} }
return colorize ? color(text) : text; return colorize ? color(text) : text;
} }
/**
* Optimized version of {@link #setPlaceholders(OfflinePlayer, String, Pattern, boolean)}
*
* @param player player to parse the placeholders against.
* @param text the text to translate.
* @param closure the closing points of a placeholder. %, {, [ etc...
* @param colorize if we should colorize this text using the common & symbol.
* @return the translated text.
*/
public static String setPlaceholders(OfflinePlayer player, String text, PlaceholderReplacer.Closure closure, boolean colorize) {
if (text == null) return null;
if (text.isEmpty()) return "";
if (PLACEHOLDERS.isEmpty()) return colorize ? color(text) : text;
// We don't want to dirty our class.
return PlaceholderReplacer.evaluatePlaceholders(player, text, closure, colorize);
}
/** /**
* Translate placeholders in the provided List based on the relation of the two provided players. * Translate placeholders in the provided List based on the relation of the two provided players.
* <br>The pattern of a valid placeholder is {@literal %rel_<identifier>_<param>%}. * <br>The pattern of a valid placeholder is {@literal %rel_<identifier>_<param>%}.
@ -365,13 +366,12 @@ public class PlaceholderAPI {
* @return The text containing the parsed relational placeholders * @return The text containing the parsed relational placeholders
*/ */
public static List<String> setRelationalPlaceholders(Player one, Player two, List<String> text, boolean colorize) { public static List<String> setRelationalPlaceholders(Player one, Player two, List<String> text, boolean colorize) {
if (text == null) { if (text == null) return null;
return null; List<String> lines = new ArrayList<>();
for (String line : text) {
lines.add(setRelationalPlaceholders(one, two, line, colorize));
} }
return lines;
return text.stream()
.map(line -> setRelationalPlaceholders(one, two, line, colorize))
.collect(Collectors.toList());
} }
/** /**
@ -397,43 +397,31 @@ public class PlaceholderAPI {
* @param colorize If color codes (&[0-1a-fk-o]) should be translated * @param colorize If color codes (&[0-1a-fk-o]) should be translated
* @return The text containing the parsed relational placeholders * @return The text containing the parsed relational placeholders
*/ */
@SuppressWarnings("DuplicatedCode")
public static String setRelationalPlaceholders(Player one, Player two, String text, boolean colorize) { public static String setRelationalPlaceholders(Player one, Player two, String text, boolean colorize) {
if (text == null) { if (text == null) return null;
return null; if (PLACEHOLDERS.isEmpty()) return colorize ? color(text) : text;
}
if (placeholders.isEmpty()) {
return colorize ? Msg.color(text) : text;
}
final Matcher matcher = RELATIONAL_PLACEHOLDER_PATTERN.matcher(text);
final Map<String, PlaceholderHook> hooks = getPlaceholders();
Matcher matcher = RELATIONAL_PLACEHOLDER_PATTERN.matcher(text);
while (matcher.find()) { while (matcher.find()) {
final String format = matcher.group(2); String format = matcher.group(2);
final int index = format.indexOf("_"); int index = format.indexOf('_');
if (index <= 0 || index >= format.length()) continue;
if (index <= 0 || index >= format.length()) { String identifier = format.substring(0, index).toLowerCase(Locale.ENGLISH);
continue; PlaceholderHook handler = PLACEHOLDERS.get(identifier);
}
String identifier = format.substring(0, index).toLowerCase(); if (handler.isRelational()) {
String params = format.substring(index + 1); Relational relational = (Relational) handler;
final PlaceholderHook hook = hooks.get(identifier); String params = format.substring(index + 1);
String value = relational.onPlaceholderRequest(one, two, params);
if (!(hook instanceof Relational)) { if (value != null) {
continue; text = text.replaceAll(Pattern.quote(matcher.group()), Matcher.quoteReplacement(value));
} }
final String value = ((Relational) hook).onPlaceholderRequest(one, two, params);
if (value != null) {
text = text.replaceAll(Pattern.quote(matcher.group()), Matcher.quoteReplacement(value));
} }
} }
return colorize ? Msg.color(text) : text; return colorize ? color(text) : text;
} }
/** /**
@ -441,43 +429,34 @@ public class PlaceholderAPI {
*/ */
protected static void unregisterAll() { protected static void unregisterAll() {
unregisterAllProvidedExpansions(); unregisterAllProvidedExpansions();
placeholders.clear(); PLACEHOLDERS.clear();
} }
/** /**
* Unregister all expansions provided by PlaceholderAPI * Unregister all expansions provided by PlaceholderAPI
*/ */
public static void unregisterAllProvidedExpansions() { public static void unregisterAllProvidedExpansions() {
final Set<PlaceholderHook> set = new HashSet<>(placeholders.values()); if (PLACEHOLDERS.isEmpty()) return;
for (PlaceholderHook hook : set) { for (PlaceholderHook handler : PLACEHOLDERS.values()) {
if (hook instanceof PlaceholderExpansion) { if (handler.isExpansion()) {
final PlaceholderExpansion expansion = (PlaceholderExpansion) hook; PlaceholderExpansion expansion = (PlaceholderExpansion) handler;
if (!expansion.persist()) unregisterExpansion(expansion);
if (!expansion.persist()) {
unregisterExpansion(expansion);
}
}
if (hook instanceof Cacheable) {
((Cacheable) hook).clear();
} }
} }
} }
public static boolean registerExpansion(PlaceholderExpansion ex) { public static boolean registerExpansion(PlaceholderExpansion expansion) {
ExpansionRegisterEvent ev = new ExpansionRegisterEvent(ex); ExpansionRegisterEvent event = new ExpansionRegisterEvent(expansion);
Bukkit.getPluginManager().callEvent(ev); Bukkit.getPluginManager().callEvent(event);
if (ev.isCancelled()) { if (event.isCancelled()) return false;
return false;
}
return registerPlaceholderHook(ex.getIdentifier(), ex); return registerPlaceholderHook(expansion.getIdentifier(), expansion);
} }
public static boolean unregisterExpansion(PlaceholderExpansion ex) { public static boolean unregisterExpansion(PlaceholderExpansion expansion) {
if (unregisterPlaceholderHook(ex.getIdentifier())) { if (unregisterPlaceholderHook(expansion.getIdentifier())) {
Bukkit.getPluginManager().callEvent(new ExpansionUnregisterEvent(ex)); Bukkit.getPluginManager().callEvent(new ExpansionUnregisterEvent(expansion));
return true; return true;
} }
@ -490,7 +469,7 @@ public class PlaceholderAPI {
* @return The pattern for {@literal %<identifier>_<params>%} * @return The pattern for {@literal %<identifier>_<params>%}
*/ */
public static Pattern getPlaceholderPattern() { public static Pattern getPlaceholderPattern() {
return PLACEHOLDER_PATTERN; return PERCENT_PLACEHOLDER_PATTERN;
} }
/** /**
@ -532,19 +511,19 @@ public class PlaceholderAPI {
} }
public static String setPlaceholders(Player player, String text) { public static String setPlaceholders(Player player, String text) {
return setPlaceholders(player, text, PLACEHOLDER_PATTERN, true); return setPlaceholders(player, text, PERCENT_PLACEHOLDER_PATTERN, true);
} }
public static String setPlaceholders(Player player, String text, boolean colorize) { public static String setPlaceholders(Player player, String text, boolean colorize) {
return setPlaceholders(player, text, PLACEHOLDER_PATTERN, colorize); return setPlaceholders(player, text, PERCENT_PLACEHOLDER_PATTERN, colorize);
} }
public static List<String> setPlaceholders(Player player, List<String> text) { public static List<String> setPlaceholders(Player player, List<String> text) {
return setPlaceholders(player, text, PLACEHOLDER_PATTERN, true); return setPlaceholders(player, text, PERCENT_PLACEHOLDER_PATTERN, true);
} }
public static List<String> setPlaceholders(Player player, List<String> text, boolean colorize) { public static List<String> setPlaceholders(Player player, List<String> text, boolean colorize) {
return setPlaceholders(player, text, PLACEHOLDER_PATTERN, colorize); return setPlaceholders(player, text, PERCENT_PLACEHOLDER_PATTERN, colorize);
} }
public static String setBracketPlaceholders(Player player, String text) { public static String setBracketPlaceholders(Player player, String text) {

@ -21,66 +21,63 @@
package me.clip.placeholderapi; package me.clip.placeholderapi;
import me.clip.placeholderapi.commands.CommandHandler; import me.clip.placeholderapi.commands.CommandHandler;
import me.clip.placeholderapi.commands.CompletionHandler;
import me.clip.placeholderapi.configuration.PlaceholderAPIConfig; import me.clip.placeholderapi.configuration.PlaceholderAPIConfig;
import me.clip.placeholderapi.expansion.ExpansionManager; import me.clip.placeholderapi.expansion.ExpansionManager;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.expansion.Version; import me.clip.placeholderapi.expansion.Version;
import me.clip.placeholderapi.expansion.cloud.ExpansionCloudManager; import me.clip.placeholderapi.expansion.cloud.ExpansionCloudManager;
import me.clip.placeholderapi.external.EZPlaceholderHook; import me.clip.placeholderapi.external.EZPlaceholderHook;
import me.clip.placeholderapi.listeners.ApacheListener;
import me.clip.placeholderapi.listeners.PlaceholderListener; import me.clip.placeholderapi.listeners.PlaceholderListener;
import me.clip.placeholderapi.listeners.ServerLoadEventListener; import me.clip.placeholderapi.listeners.ServerLoadEventListener;
import me.clip.placeholderapi.updatechecker.UpdateChecker;
import me.clip.placeholderapi.util.TimeUtil; import me.clip.placeholderapi.util.TimeUtil;
import me.clip.placeholderapi.util.UpdateChecker;
import org.bstats.bukkit.Metrics; import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* Yes I have a shit load of work to do... * Yes I have a shit load of work to do...
* *
* @author Ryan McCarthy * @author Ryan McCarthy
*/ */
public class PlaceholderAPIPlugin extends JavaPlugin { public class PlaceholderAPIPlugin extends JavaPlugin {
private static final Version serverVersion;
private static PlaceholderAPIPlugin instance; private static PlaceholderAPIPlugin instance;
private static SimpleDateFormat dateFormat; private static DateTimeFormatter dateFormat;
private static String booleanTrue; private static String booleanTrue;
private static String booleanFalse; private static String booleanFalse;
private static Version serverVersion;
static {
// It's not possible to be null or throw an index exception unless it's a bug.
String nmsVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
boolean spigot;
try {
Class.forName("org.spigotmc.SpigotConfig");
spigot = true;
} catch (ExceptionInInitializerError | ClassNotFoundException ignored) {
spigot = false;
}
serverVersion = new Version(nmsVersion, spigot);
}
private PlaceholderAPIConfig config; private PlaceholderAPIConfig config;
private ExpansionManager expansionManager; private ExpansionManager expansionManager;
private ExpansionCloudManager expansionCloud; private ExpansionCloudManager expansionCloud;
private long startTime; private long startTime;
private static Version getVersion() {
String v = "unknown";
boolean spigot = false;
try {
v = Bukkit.getServer().getClass().getPackage().getName()
.split("\\.")[3];
} catch (ArrayIndexOutOfBoundsException ex) {
}
try {
Class.forName("org.spigotmc.SpigotConfig");
Class.forName("net.md_5.bungee.api.chat.BaseComponent");
spigot = true;
} catch (ExceptionInInitializerError | ClassNotFoundException ignored) {
}
return new Version(v, spigot);
}
/** /**
* Gets the static instance of the main class for PlaceholderAPI. This class is not the actual API * Gets the static instance of the main class for PlaceholderAPI. This class is not the actual API
* class, this is the main class that extends JavaPlugin. For most API methods, use static methods * class, this is the main class that extends JavaPlugin. For most API methods, use static methods
@ -98,9 +95,8 @@ public class PlaceholderAPIPlugin extends JavaPlugin {
* *
* @return date format * @return date format
*/ */
public static SimpleDateFormat getDateFormat() { public static DateTimeFormatter getDateFormat() {
return dateFormat != null ? dateFormat : new SimpleDateFormat( return dateFormat != null ? dateFormat : DateTimeFormatter.ofPattern("MM/dd/yy HH:mm:ss");
"MM/dd/yy HH:mm:ss");
} }
/** /**
@ -122,26 +118,26 @@ public class PlaceholderAPIPlugin extends JavaPlugin {
} }
public static Version getServerVersion() { public static Version getServerVersion() {
return serverVersion != null ? serverVersion : getVersion(); return serverVersion;
}
@Override
public void onLoad() {
startTime = System.currentTimeMillis();
instance = this;
serverVersion = getVersion();
config = new PlaceholderAPIConfig(this);
expansionManager = new ExpansionManager(this);
} }
@Override @Override
public void onEnable() { public void onEnable() {
startTime = System.currentTimeMillis();
instance = this;
config = new PlaceholderAPIConfig(this);
config.loadDefConfig(); config.loadDefConfig();
setupOptions(); setupOptions();
Objects.requireNonNull(getCommand("placeholderapi")).setExecutor(new CommandHandler()); expansionManager = new ExpansionManager(this);
new PlaceholderListener(this); new PlaceholderListener(this);
PluginCommand command = getCommand("placeholderapi");
command.setExecutor(new CommandHandler());
command.setTabCompleter(new CompletionHandler());
new ApacheListener(this);
try { try {
Class.forName("org.bukkit.event.server.ServerLoadEvent"); Class.forName("org.bukkit.event.server.ServerLoadEvent");
new ServerLoadEventListener(this); new ServerLoadEventListener(this);
@ -150,7 +146,7 @@ public class PlaceholderAPIPlugin extends JavaPlugin {
getLogger().info("Placeholder expansion registration initializing..."); getLogger().info("Placeholder expansion registration initializing...");
//fetch any hooks that may have registered externally onEnable first otherwise they will be lost //fetch any hooks that may have registered externally onEnable first otherwise they will be lost
final Map<String, PlaceholderHook> alreadyRegistered = PlaceholderAPI.getPlaceholders(); Map<String, PlaceholderHook> alreadyRegistered = PlaceholderAPI.PLACEHOLDERS;
getExpansionManager().registerAllExpansions(); getExpansionManager().registerAllExpansions();
if (alreadyRegistered != null && !alreadyRegistered.isEmpty()) { if (alreadyRegistered != null && !alreadyRegistered.isEmpty()) {
@ -159,13 +155,8 @@ public class PlaceholderAPIPlugin extends JavaPlugin {
}, 1); }, 1);
} }
if (config.checkUpdates()) { if (config.checkUpdates()) new UpdateChecker(this).fetch();
new UpdateChecker(this).fetch(); if (config.isCloudEnabled()) enableCloud();
}
if (config.isCloudEnabled()) {
enableCloud();
}
setupMetrics(); setupMetrics();
getServer().getScheduler().runTaskLater(this, this::checkHook, 40); getServer().getScheduler().runTaskLater(this, this::checkHook, 40);
@ -175,14 +166,13 @@ public class PlaceholderAPIPlugin extends JavaPlugin {
public void onDisable() { public void onDisable() {
disableCloud(); disableCloud();
PlaceholderAPI.unregisterAll(); PlaceholderAPI.unregisterAll();
expansionManager = null;
Bukkit.getScheduler().cancelTasks(this); Bukkit.getScheduler().cancelTasks(this);
serverVersion = null;
expansionManager = null;
instance = null; instance = null;
} }
public void reloadConf(CommandSender s) { public void reloadConf(CommandSender s) {
boolean cloudEnabled = this.expansionCloud != null;
PlaceholderAPI.unregisterAllProvidedExpansions(); PlaceholderAPI.unregisterAllProvidedExpansions();
reloadConfig(); reloadConfig();
setupOptions(); setupOptions();
@ -190,79 +180,70 @@ public class PlaceholderAPIPlugin extends JavaPlugin {
if (!config.isCloudEnabled()) { if (!config.isCloudEnabled()) {
disableCloud(); disableCloud();
} else if (!cloudEnabled) { } else if (this.expansionCloud != null) {
enableCloud(); enableCloud();
} }
s.sendMessage(ChatColor.translateAlternateColorCodes('&', s.sendMessage(ChatColor.translateAlternateColorCodes('&', PlaceholderAPI.PLACEHOLDERS.size() + " &aplaceholder hooks successfully registered!"));
PlaceholderAPI.getRegisteredIdentifiers().size()
+ " &aplaceholder hooks successfully registered!"));
} }
@SuppressWarnings("deprecation")
private void checkHook() { private void checkHook() {
Map<String, PlaceholderHook> loaded = PlaceholderAPI.getPlaceholders(); for (PlaceholderHook hook : PlaceholderAPI.PLACEHOLDERS.values()) {
if (hook instanceof EZPlaceholderHook) {
loaded.values().forEach(h -> { String pluginName = ((EZPlaceholderHook) hook).getPluginName();
if (h instanceof EZPlaceholderHook) {
String author; String author;
try { try {
author = Bukkit.getPluginManager().getPlugin(((EZPlaceholderHook) h).getPluginName()).getDescription().getAuthors().toString(); author = Bukkit.getPluginManager().getPlugin(pluginName).getDescription().getAuthors().toString();
} catch (Exception ex) { } catch (Exception ex) {
author = "the author of the hook's plugin"; author = "the author of the hook's plugin";
} }
getLogger().severe(((EZPlaceholderHook) h).getPluginName() + getLogger().severe(pluginName +
" is currently using a deprecated method to hook into PlaceholderAPI. Placeholders for that plugin no longer work. " + " is currently using a deprecated method to hook into PlaceholderAPI. Placeholders for that plugin no longer work. " +
"Please consult {author} and urge them to update it ASAP.".replace("{author}", author)); "Please consult " + author + " and urge them to update it ASAP.");
// disable the hook on startup // disable the hook on startup
PlaceholderAPI.unregisterPlaceholderHook(((EZPlaceholderHook) h).getPlaceholderName()); PlaceholderAPI.unregisterPlaceholderHook(((EZPlaceholderHook) hook).getPlaceholderName());
} }
}); }
} }
private void setupOptions() { private void setupOptions() {
booleanTrue = config.booleanTrue(); booleanTrue = config.booleanTrue();
if (booleanTrue == null) booleanTrue = "true";
if (booleanTrue == null) {
booleanTrue = "true";
}
booleanFalse = config.booleanFalse(); booleanFalse = config.booleanFalse();
if (booleanFalse == null) booleanFalse = "false";
if (booleanFalse == null) {
booleanFalse = "false";
}
try { try {
dateFormat = new SimpleDateFormat(config.dateFormat()); dateFormat = DateTimeFormatter.ofPattern(config.dateFormat());
} catch (Exception e) { } catch (Exception ignored) {
dateFormat = new SimpleDateFormat("MM/dd/yy HH:mm:ss"); dateFormat = DateTimeFormatter.ofPattern("MM/dd/yy HH:mm:ss");
} }
} }
private void setupMetrics() { private void setupMetrics() {
Metrics m = new Metrics(this); // This is NOT the plugin resource ID. it's the bStats ID.
m.addCustomChart(new Metrics.SimplePie("using_expansion_cloud", () -> getExpansionCloud() != null ? "yes" : "no")); Metrics metrics = new Metrics(this, 438);
metrics.addCustomChart(new Metrics.SimplePie("using_expansion_cloud",
() -> getExpansionCloud() != null ? "yes" : "no"));
m.addCustomChart(new Metrics.SimplePie("using_spigot", () -> getServerVersion().isSpigot() ? "yes" : "no")); metrics.addCustomChart(new Metrics.SimplePie("using_spigot",
() -> getServerVersion().isSpigot() ? "yes" : "no"));
m.addCustomChart(new Metrics.AdvancedPie("expansions_used", () -> { metrics.addCustomChart(new Metrics.AdvancedPie("expansions_used", () -> {
Map<String, Integer> map = new HashMap<>(); Map<String, Integer> map = new HashMap<>();
Map<String, PlaceholderHook> hooks = PlaceholderAPI.getPlaceholders(); for (PlaceholderHook hook : PlaceholderAPI.PLACEHOLDERS.values()) {
if (hook.isExpansion()) {
if (!hooks.isEmpty()) { PlaceholderExpansion ex = (PlaceholderExpansion) hook;
map.put(ex.getRequiredPlugin() == null ? ex.getIdentifier()
for (PlaceholderHook hook : hooks.values()) { : ex.getRequiredPlugin(), 1);
if (hook instanceof PlaceholderExpansion) {
PlaceholderExpansion expansion = (PlaceholderExpansion) hook;
map.put(expansion.getRequiredPlugin() == null ? expansion.getIdentifier() : expansion.getRequiredPlugin(), 1);
}
} }
} }
return map;
return map;
})); }));
} }
@ -276,10 +257,7 @@ public class PlaceholderAPIPlugin extends JavaPlugin {
} }
public void disableCloud() { public void disableCloud() {
if (expansionCloud != null) { if (expansionCloud != null) expansionCloud = null;
expansionCloud.clean();
expansionCloud = null;
}
} }
/** /**
@ -305,6 +283,6 @@ public class PlaceholderAPIPlugin extends JavaPlugin {
} }
public long getUptimeMillis() { public long getUptimeMillis() {
return (System.currentTimeMillis() - startTime); return System.currentTimeMillis() - startTime;
} }
} }

@ -20,13 +20,14 @@
*/ */
package me.clip.placeholderapi; package me.clip.placeholderapi;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.expansion.Relational;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
public abstract class PlaceholderHook { public abstract class PlaceholderHook {
/** /**
* called when a placeholder value is requested from this hook * Called when a placeholder value is requested from this hook.
* *
* @param player {@link OfflinePlayer} to request the placeholder value for, null if not needed for a * @param player {@link OfflinePlayer} to request the placeholder value for, null if not needed for a
* player * player
@ -41,8 +42,12 @@ public abstract class PlaceholderHook {
return onPlaceholderRequest(null, params); return onPlaceholderRequest(null, params);
} }
public PlaceholderAPIPlugin getPlaceholderAPI() {
return PlaceholderAPIPlugin.getInstance();
}
/** /**
* called when a placeholder is requested from this hook * Called when a placeholder is requested from this hook.
* *
* @param player {@link Player} to request the placeholder value for, null if not needed for a player * @param player {@link Player} to request the placeholder value for, null if not needed for a player
* @param params String passed to the hook to determine what value to return * @param params String passed to the hook to determine what value to return
@ -51,4 +56,12 @@ public abstract class PlaceholderHook {
public String onPlaceholderRequest(Player player, String params) { public String onPlaceholderRequest(Player player, String params) {
return null; return null;
} }
public boolean isExpansion() {
return this instanceof PlaceholderExpansion;
}
public boolean isRelational() {
return this instanceof Relational;
}
} }

@ -0,0 +1,150 @@
/*
* PlaceholderAPI
* Copyright (C) 2019 Ryan McCarthy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package me.clip.placeholderapi;
import com.google.common.collect.ImmutableSet;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import java.util.Set;
/**
* This is certainly hard to understand and maintain, but it's fully optimized.
* It's almost x5 times faster than the RegEx method for normal sized strings. This performance gap gets smaller
* for smaller strings.
*
* @author Crypto Morin
*/
public class PlaceholderReplacer {
/**
* Cached available color codes. Technically the uppercase of each letter can be used too, but no one really uses the uppercase ones.
*/
private static final Set<Character> COLOR_CODES = ImmutableSet.of('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'k', 'l', 'm', 'o', 'r', 'x');
/**
* Translates placeholders for a string using pure character loops.
* Might cause problems in really rare conditions.
*
* @param player the player to translate the string for.
* @param str the string to translate.
* @param closure the type of the placeholder closing points.
* @param colorize if this message should be colorized as well.
* @return a translated string.
*/
public static String evaluatePlaceholders(OfflinePlayer player, String str, Closure closure, boolean colorize) {
char[] chars = str.toCharArray();
StringBuilder builder = new StringBuilder(chars.length);
// This won't cause memory leaks. It's inside a method. And we want to use setLength instead of
// creating a new string builder to use the maximum capacity and avoid initializing new objects.
StringBuilder identifier = new StringBuilder(50);
PlaceholderHook handler = null;
// Stages:
// Stage -1: Look for the color code in the next character.
// Stage 0: No closures detected, or the detected identifier is invalid. We're going forward while appending the characters normally.
// Stage 1: The closure has been detected, looking for the placeholder identifier...
// Stage 2: Detected the identifier and the parameter. Translating the placeholder...
int stage = 0;
for (char ch : chars) {
if (stage == -1 && COLOR_CODES.contains(ch)) {
builder.append(ChatColor.COLOR_CHAR).append(ch);
stage = 0;
continue;
}
// Check if the placeholder starts or ends.
if (ch == closure.start || ch == closure.end) {
// If the placeholder ends.
if (stage == 2) {
String parameter = identifier.toString();
String translated = handler.onRequest(player, parameter);
if (translated == null) {
String name = handler.isExpansion() ? ((PlaceholderExpansion) handler).getIdentifier() : "";
builder.append(closure.start).append(name).append('_').append(parameter).append(closure.end);
} else builder.append(translated);
identifier.setLength(0);
stage = 0;
continue;
} else if (stage == 1) { // If it just started | Double closures | If it's still hasn't detected the indentifier, reset.
builder.append(closure.start).append(identifier);
}
identifier.setLength(0);
stage = 1;
continue;
}
// Placeholder identifier started.
if (stage == 1) {
// Compare the current character with the idenfitier's.
// We reached the end of our identifier.
if (ch == '_') {
handler = PlaceholderAPI.PLACEHOLDERS.get(identifier.toString());
if (handler == null) {
builder.append(closure.start).append(identifier).append('_');
stage = 0;
} else {
identifier.setLength(0);
stage = 2;
}
continue;
}
// Keep building the identifier name.
identifier.append(ch);
continue;
}
// Building the placeholder parameter.
if (stage == 2) {
identifier.append(ch);
continue;
}
// Nothing placeholder related was found.
if (colorize && ch == '&') {
stage = -1;
continue;
}
builder.append(ch);
}
if (identifier != null) {
if (stage > 0) builder.append(closure.end);
builder.append(identifier);
}
return builder.toString();
}
public enum Closure {
PERCENT('%', '%'), BRACKETS('[', ']'), BRACES('{', '}');
public char start, end;
Closure(char start, char end) {
this.start = start;
this.end = end;
}
}
}

@ -1,52 +1,42 @@
package me.clip.placeholderapi.commands; package me.clip.placeholderapi.commands;
import com.google.common.collect.ImmutableSet;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
public abstract class Command { public abstract class Command {
private static final Options EMPTY_OPTIONS = new Options(null, 0, null); private static final Options EMPTY_OPTIONS = new Options(null, 0);
private final String match; private final String match;
private final String usage; private final String usage;
private final int minimumArguments; private final int minimumArguments;
private final Set<String> permissions; /**
* Commands should not have multiple permissions. This can lead to a lot of confusions.
* This is also a lot more appropriate for maintainability, I saw a lot of commands regitered with wrong permissions.
* We will use the main command name to parse our permission.
*/
private final String permission;
protected Command(@NotNull final String match) { protected Command(String match) {
this(match, EMPTY_OPTIONS); this(match, EMPTY_OPTIONS);
} }
protected Command(@NotNull final String match, @NotNull final Options options) { protected Command(String match, Options options) {
this.match = match; this.match = match;
this.usage = options.usage == null ? "/papi " + match + " <required args> [optional args]" : options.usage; this.usage = options.usage == null ? "/papi " + match + " <required args> [optional args]" : options.usage;
this.permissions = options.permissions == null ? Collections.emptySet() : ImmutableSet.copyOf(options.permissions); this.permission = "placeholderapi." + match.replace(' ', '.');
this.minimumArguments = options.minimumArguments; this.minimumArguments = options.minimumArguments;
} }
protected static Options usage(@NotNull final String usage, final int minimumArguments) { protected static Options options(String usage, int minimumArguments) {
return new Options(usage, minimumArguments, null); return new Options(usage, minimumArguments);
} }
protected static Options permissions(@NotNull final String... permissions) {
return new Options(null, 0, permissions);
}
protected static Options options(@NotNull final String usage, final int minimumArguments,
@NotNull final String... permissions) {
return new Options(usage, minimumArguments, permissions);
}
@NotNull
public String getMatch() { public String getMatch() {
return match; return match;
} }
@NotNull
public String getUsage() { public String getUsage() {
return usage; return usage;
} }
@ -55,28 +45,23 @@ public abstract class Command {
return minimumArguments; return minimumArguments;
} }
@NotNull public String getPermission() {
public Set<String> getPermissions() { return permission;
return permissions;
} }
public abstract void execute(@NotNull final CommandSender sender, @NotNull final String[] args); public abstract void execute(CommandSender sender, String[] args);
@NotNull public List<String> handleCompletion(CommandSender sender, String[] args) {
public List<String> handleCompletion(@NotNull final CommandSender sender, @NotNull final String[] args) {
return Collections.emptyList(); return Collections.emptyList();
} }
private static class Options { private static class Options {
private final String usage; private final String usage;
private final int minimumArguments; private final int minimumArguments;
private final String[] permissions;
private Options(@Nullable final String usage, final int minimumArguments, private Options(String usage, int minimumArguments) {
@Nullable final String[] permissions) {
this.usage = usage; this.usage = usage;
this.minimumArguments = minimumArguments; this.minimumArguments = minimumArguments;
this.permissions = permissions;
} }
} }
} }

@ -1,25 +1,33 @@
package me.clip.placeholderapi.commands; package me.clip.placeholderapi.commands;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.command.*; import me.clip.placeholderapi.commands.command.*;
import me.clip.placeholderapi.commands.command.ecloud.EcloudInfoCommand;
import me.clip.placeholderapi.commands.command.ecloud.EcloudListCommand;
import me.clip.placeholderapi.commands.command.ecloud.*; import me.clip.placeholderapi.commands.command.ecloud.*;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Pattern;
public final class CommandHandler implements CommandExecutor { public final class CommandHandler implements CommandExecutor {
private static final Command DEFAULT = new VersionCommand(); private static final Command DEFAULT = new VersionCommand();
private static final List<Command> COMMANDS = Lists.newArrayList( protected static final List<Command> COMMANDS = Lists.newArrayList(
DEFAULT,
new HelpCommand(),
new InfoCommand(),
new ListCommand(),
new RegisterCommand(),
new UnregisterCommand(),
new ReloadCommand(),
new BcParseCommand(),
new ParseCommand(),
new ParseRelCommand(),
new EcloudCommand(),
new EcloudClearCommand(), new EcloudClearCommand(),
new EcloudDownloadCommand(), new EcloudDownloadCommand(),
new EcloudInfoCommand(), new EcloudInfoCommand(),
@ -28,74 +36,57 @@ public final class CommandHandler implements CommandExecutor {
new EcloudRefreshCommand(), new EcloudRefreshCommand(),
new EcloudStatusCommand(), new EcloudStatusCommand(),
new EcloudVersionInfoCommand(), new EcloudVersionInfoCommand(),
new EcloudCommand(), new EcloudDisableCommand(),
new BcParseCommand(), new EcloudEnableCommand()
new ParseCommand(),
new ParseRelCommand(),
new DisableEcloudCommand(),
new EnableCloudCommand(),
new HelpCommand(),
new InfoCommand(),
new ListCommand(),
new RegisterCommand(),
new ReloadCommand(),
DEFAULT,
new UnregisterCommand()
); );
static { static {
COMMANDS.sort((command1, command2) -> { COMMANDS.sort((command1, command2) -> {
final int comparison = Integer.compare(command1.getMatch().length(), command2.getMatch().length()); int comparison = Integer.compare(command1.getMatch().length(), command2.getMatch().length());
if (comparison == 1) return -1; if (comparison == 1) return -1;
if (comparison == -1) return 1; if (comparison == -1) return 1;
return 0; return 0;
}); });
Objects.requireNonNull(PlaceholderAPIPlugin.getInstance().getCommand("placeholderapi"))
.setTabCompleter(new CompletionHandler(COMMANDS));
} }
private static final Pattern SPACE_PATTERN = Pattern.compile(" "); private static String[] splitArguments(String joinedArguments, String command) {
joinedArguments = StringUtils.remove(joinedArguments, command).trim();
String[] args = StringUtils.split(joinedArguments);
return args.length == 1 && args[0].isEmpty() ? new String[0] : args;
}
@Override @Override
public boolean onCommand(@NotNull final CommandSender sender, @NotNull final org.bukkit.command.Command bukkitCommand, public boolean onCommand(@NotNull CommandSender sender, @NotNull org.bukkit.command.Command bukkitCommand, @NotNull String name, String[] args) {
@NotNull final String name, @NotNull String[] args) {
if (args.length == 0) { if (args.length == 0) {
DEFAULT.execute(sender, args); DEFAULT.execute(sender, args);
return true; return true;
} }
final String joined = String.join(" ", args).toLowerCase(); String joined = String.join(" ", args).toLowerCase();
final Optional<Command> optional = COMMANDS.stream() Optional<Command> optional = COMMANDS.stream()
.filter(command -> joined.startsWith(command.getMatch())) .filter(command -> joined.startsWith(command.getMatch()))
.findFirst(); .findFirst();
if (!optional.isPresent()) { if (!optional.isPresent()) {
sender.sendMessage("Specified command is not valid."); Msg.msg(sender, "&cUnknown command.");
return true; return true;
} }
final Command command = optional.get(); Command command = optional.get();
String permission = command.getPermission();
if (!command.getPermissions().isEmpty() && command.getPermissions().stream().noneMatch(sender::hasPermission)) { if (!sender.hasPermission(permission)) {
sender.sendMessage("You do not have the permission to execute specified command."); Msg.msg(sender, "&cYou do not have the permission to use this command.");
return true; return true;
} }
args = splitArguments(joined, command.getMatch()); args = splitArguments(joined, command.getMatch());
if (args.length < command.getMinimumArguments()) { if (args.length < command.getMinimumArguments()) {
Msg.msg(sender, command.getUsage()); Msg.msg(sender, command.getUsage());
return true; return true;
} }
command.execute(sender, args); command.execute(sender, args);
return true; return true;
} }
static String[] splitArguments(@NotNull final String joinedArguments, @NotNull final String command) {
final String[] args = SPACE_PATTERN.split(joinedArguments.replace(command, "").trim());
return args.length == 1 && args[0].isEmpty() ? new String[]{} : args;
}
} }

@ -1,32 +1,37 @@
package me.clip.placeholderapi.commands; package me.clip.placeholderapi.commands;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter; import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Locale;
import java.util.stream.Collectors;
public final class CompletionHandler implements TabCompleter { public final class CompletionHandler implements TabCompleter {
private final List<Command> commands; private static String[] splitArguments(String[] args, String command) {
int skip = StringUtils.split(command).length;
CompletionHandler(@NotNull final List<Command> commands) { return Arrays.stream(args).skip(skip).toArray(String[]::new);
this.commands = commands;
} }
// it makes me physically cringe trying to understand why bukkit uses a list instead of a set for this // it makes me physically cringe trying to understand why bukkit uses a list instead of a set for this
@NotNull // It's because of the list order. Even if they wanted to change that, they couldn't for the sake of backward compatibility. ~Crypto
@Override @Override
public List<String> onTabComplete(@NotNull final CommandSender sender, @NotNull final org.bukkit.command.Command bukkitCommand, public List<String> onTabComplete(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command bukkitCommand, @NotNull String name, String[] args) {
@NotNull final String name, @NotNull final String[] args) { String joined = String.join(" ", args).toLowerCase(Locale.ENGLISH);
final String joined = String.join(" ", args).toLowerCase();
final Optional<Command> optional = commands.stream()
.filter(command -> joined.startsWith(command.getMatch()))
.findAny();
return optional if (args.length > 1) {
.map(command -> command.handleCompletion(sender, CommandHandler.splitArguments(joined, command.getMatch()))) return CommandHandler.COMMANDS.stream()
.orElse(Collections.emptyList()); .filter(command -> sender.hasPermission(command.getPermission()) && joined.startsWith(command.getMatch()))
.findFirst()
.map(command -> command.handleCompletion(sender, splitArguments(args, command.getMatch())))
.orElse(Collections.emptyList());
}
return CommandHandler.COMMANDS.stream()
.filter(command -> sender.hasPermission(command.getPermission()) && (args[0].isEmpty() || command.getMatch().startsWith(joined)))
.map(Command::getMatch).collect(Collectors.toList());
} }
} }

@ -8,40 +8,34 @@ import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public final class BcParseCommand extends Command { public final class BcParseCommand extends Command {
public BcParseCommand() { public BcParseCommand() {
super("bcparse", options("&cYou must specify a player.", 1, "placeholderapi.parse")); super("bcparse", options("&cYou must specify a player.", 1));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final OfflinePlayer player; OfflinePlayer player;
final String input = args[0]; String input = args[0];
if (input.equalsIgnoreCase("me")) { if (input.equalsIgnoreCase("me")) {
if (sender instanceof Player) { if (sender instanceof Player) {
player = (Player) sender; player = (Player) sender;
} else { } else {
Msg.msg(sender, "&cThis command must target a player when used by console"); Msg.msg(sender, "&cThis command must target a player when used by console");
return; return;
} }
} else { } else {
if (Bukkit.getPlayer(input) != null) { player = Bukkit.getPlayer(input);
player = Bukkit.getPlayer(input); if (player == null) player = Bukkit.getOfflinePlayer(input);
} else { if (player == null || !player.hasPlayedBefore()) {
player = Bukkit.getOfflinePlayer(input); Msg.msg(sender, "&cCould not find player&8: &f" + input);
return;
} }
} }
if (player == null || !player.hasPlayedBefore()) { String parse = StringUtils.join(args, " ", 2, args.length);
Msg.msg(sender, "&cFailed to find player: &f" + input);
return;
}
final String parse = StringUtils.join(args, " ", 2, args.length);
Msg.broadcast("&r" + PlaceholderAPI.setPlaceholders(player, parse)); Msg.broadcast("&r" + PlaceholderAPI.setPlaceholders(player, parse));
} }
} }

@ -6,7 +6,6 @@ import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -22,16 +21,18 @@ public final class EcloudCommand extends Command {
"placeholders", "placeholders",
"refresh", "refresh",
"status", "status",
"versioninfo" "versioninfo",
"enable",
"disable"
); );
public EcloudCommand() { public EcloudCommand() {
super("ecloud", permissions("placeholderapi.ecloud")); super("ecloud");
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance(); PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance();
if (args.length == 0) { if (args.length == 0) {
Msg.msg(sender, "&bExpansion cloud commands", Msg.msg(sender, "&bExpansion cloud commands",
@ -57,7 +58,6 @@ public final class EcloudCommand extends Command {
if (plugin.getExpansionCloud() == null) { if (plugin.getExpansionCloud() == null) {
Msg.msg(sender, "&7The expansion cloud is not enabled!"); Msg.msg(sender, "&7The expansion cloud is not enabled!");
return; return;
} }
@ -69,9 +69,9 @@ public final class EcloudCommand extends Command {
sender.sendMessage("Specified command is not valid."); sender.sendMessage("Specified command is not valid.");
} }
@NotNull
@Override @Override
public List<String> handleCompletion(@NotNull final CommandSender sender, @NotNull final String[] args) { public List<String> handleCompletion(CommandSender sender, String[] args) {
if (args.length == MAXIMUM_ARGUMENTS) { if (args.length == MAXIMUM_ARGUMENTS) {
return StringUtil.copyPartialMatches(args[0], COMPLETIONS, new ArrayList<>(COMPLETIONS.size())); return StringUtil.copyPartialMatches(args[0], COMPLETIONS, new ArrayList<>(COMPLETIONS.size()));
} }

@ -4,15 +4,14 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.Command; import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public final class HelpCommand extends Command { public final class HelpCommand extends Command {
public HelpCommand() { public HelpCommand() {
super("help", permissions("placeholderapi.ecloud")); super("help");
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
Msg.msg(sender, "PlaceholderAPI &aHelp &e(&f" + PlaceholderAPIPlugin.getInstance().getDescription().getVersion() + "&e)", Msg.msg(sender, "PlaceholderAPI &aHelp &e(&f" + PlaceholderAPIPlugin.getInstance().getDescription().getVersion() + "&e)",
"&b/papi", "&b/papi",
"&fView plugin info/version info", "&fView plugin info/version info",

@ -7,7 +7,6 @@ import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -17,13 +16,13 @@ public final class InfoCommand extends Command {
private static final int MINIMUM_ARGUMENTS = 1; private static final int MINIMUM_ARGUMENTS = 1;
public InfoCommand() { public InfoCommand() {
super("info", options("&cIncorrect usage! &7/papi info <expansion>", MINIMUM_ARGUMENTS, "placeholderapi.info")); super("info", options("&cIncorrect usage! &7/papi info <expansion>", MINIMUM_ARGUMENTS));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final String requestedExpansion = args[0]; String requestedExpansion = args[0];
final PlaceholderExpansion ex = PlaceholderAPIPlugin.getInstance().getExpansionManager().getRegisteredExpansion(requestedExpansion); PlaceholderExpansion ex = PlaceholderAPIPlugin.getInstance().getExpansionManager().getRegisteredExpansion(requestedExpansion);
if (ex == null) { if (ex == null) {
Msg.msg(sender, "&cThere is no expansion loaded with the identifier: &f" + requestedExpansion); Msg.msg(sender, "&cThere is no expansion loaded with the identifier: &f" + requestedExpansion);
@ -54,11 +53,11 @@ public final class InfoCommand extends Command {
} }
} }
@NotNull
@Override @Override
public List<String> handleCompletion(@NotNull final CommandSender sender, @NotNull final String[] args) { public List<String> handleCompletion(CommandSender sender, String[] args) {
if (args.length == MINIMUM_ARGUMENTS) { if (args.length == MINIMUM_ARGUMENTS) {
final Set<String> completions = PlaceholderAPI.getRegisteredIdentifiers(); Set<String> completions = PlaceholderAPI.getRegisteredIdentifiers();
return StringUtil.copyPartialMatches(args[0], completions, new ArrayList<>(completions.size())); return StringUtil.copyPartialMatches(args[0], completions, new ArrayList<>(completions.size()));
} }

@ -4,19 +4,18 @@ import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.commands.Command; import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public final class ListCommand extends Command { public final class ListCommand extends Command {
public ListCommand() { public ListCommand() {
super("list", permissions("placeholderapi.list")); super("list");
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final Set<String> registered = PlaceholderAPI.getRegisteredIdentifiers(); Set<String> registered = PlaceholderAPI.getRegisteredIdentifiers();
if (registered.isEmpty()) { if (registered.isEmpty()) {
Msg.msg(sender, "&7There are no placeholder hooks currently registered!"); Msg.msg(sender, "&7There are no placeholder hooks currently registered!");
return; return;

@ -7,41 +7,53 @@ import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public final class ParseCommand extends Command { public final class ParseCommand extends Command {
public ParseCommand() { public ParseCommand() {
super("parse", options("&cYou must specify a player.", 1, "placeholderapi.parse")); super("parse", options("&cYou must specify a player.", 1));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final OfflinePlayer player; OfflinePlayer player;
final String input = args[0]; String input = args[0];
if (input.equalsIgnoreCase("me")) { if (input.equalsIgnoreCase("me")) {
if (sender instanceof Player) { if (sender instanceof Player) {
player = (Player) sender; player = (Player) sender;
} else { } else {
Msg.msg(sender, "&cThis command must target a player when used by console"); Msg.msg(sender, "&cThis command must target a player when used by console");
return; return;
} }
} else { } else {
if (Bukkit.getPlayer(input) != null) { player = Bukkit.getPlayer(input);
player = Bukkit.getPlayer(input); if (player == null) player = Bukkit.getOfflinePlayer(input);
} else { if (player == null || !player.hasPlayedBefore()) {
player = Bukkit.getOfflinePlayer(input); Msg.msg(sender, "&cCould not find player&8: &f" + input);
return;
} }
} }
if (player == null || !player.hasPlayedBefore()) { String parse = StringUtils.join(args, " ", 1, args.length);
Msg.msg(sender, "&cFailed to find player: &f" + input);
return;
}
final String parse = StringUtils.join(args, " ", 1, args.length);
Msg.msg(sender, "&r" + PlaceholderAPI.setPlaceholders(player, parse)); Msg.msg(sender, "&r" + PlaceholderAPI.setPlaceholders(player, parse));
} }
@Override
public List<String> handleCompletion(CommandSender sender, String[] args) {
if (args.length == 1) {
List<String> players = Bukkit.getOnlinePlayers().stream().map(HumanEntity::getName).collect(Collectors.toList());
players.add("me");
if (args[0].isEmpty()) return players;
else return players.stream().filter(name -> name.startsWith(args[0])).collect(Collectors.toList());
}
if (args.length == 2) return Collections.singletonList("<message>");
return new ArrayList<>();
}
} }

@ -7,30 +7,27 @@ import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public final class ParseRelCommand extends Command { public final class ParseRelCommand extends Command {
public ParseRelCommand() { public ParseRelCommand() {
super("parserel", options("&cYou must specify at least two players.", 2, "placeholderapi.parse")); super("parserel", options("&cYou must specify at least two players.", 2));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final Player one = Bukkit.getPlayer(args[0]); Player one = Bukkit.getPlayer(args[0]);
if (one == null) { if (one == null) {
Msg.msg(sender, args[0] + " &cis not online!"); Msg.msg(sender, args[0] + " &cis not online!");
return; return;
} }
final Player two = Bukkit.getPlayer(args[1]); Player two = Bukkit.getPlayer(args[1]);
if (two == null) { if (two == null) {
Msg.msg(sender, args[1] + " &cis not online!"); Msg.msg(sender, args[1] + " &cis not online!");
return; return;
} }
final String parse = StringUtils.join(args, " ", 1, args.length); String parse = StringUtils.join(args, " ", 1, args.length);
Msg.msg(sender, "&r" + PlaceholderAPI.setRelationalPlaceholders(one, two, parse)); Msg.msg(sender, "&r" + PlaceholderAPI.setRelationalPlaceholders(one, two, parse));
} }
} }

@ -4,22 +4,21 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.Command; import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public final class RegisterCommand extends Command { public final class RegisterCommand extends Command {
public RegisterCommand() { public RegisterCommand() {
super("register", options("&cAn expansion file name must be specified!", 1,"placeholderapi.register")); super("register", options("&cAn expansion file name must be specified!", 1));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final String fileName = args[0].replace(".jar", ""); String fileName = StringUtils.remove(args[0], ".jar");
final PlaceholderExpansion expansion = PlaceholderAPIPlugin.getInstance().getExpansionManager().registerExpansion(fileName); PlaceholderExpansion expansion = PlaceholderAPIPlugin.getInstance().getExpansionManager().registerExpansion(fileName);
if (expansion == null) { if (expansion == null) {
Msg.msg(sender, "&cFailed to register expansion from " + fileName); Msg.msg(sender, "&cFailed to register expansion from " + fileName);
return; return;
} }

@ -4,15 +4,14 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.Command; import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public final class ReloadCommand extends Command { public final class ReloadCommand extends Command {
public ReloadCommand() { public ReloadCommand() {
super("reload", permissions("placeholderapi.reload")); super("reload");
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
Msg.msg(sender, "&fPlaceholder&7API &bconfiguration reloaded!"); Msg.msg(sender, "&fPlaceholder&7API &bconfiguration reloaded!");
PlaceholderAPIPlugin.getInstance().reloadConf(sender); PlaceholderAPIPlugin.getInstance().reloadConf(sender);
} }

@ -7,7 +7,6 @@ import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -17,18 +16,17 @@ public final class UnregisterCommand extends Command {
private static final int MINIMUM_ARGUMENTS = 1; private static final int MINIMUM_ARGUMENTS = 1;
public UnregisterCommand() { public UnregisterCommand() {
super("unregister", options("&cAn expansion name must be specified!", MINIMUM_ARGUMENTS, "placeholderapi.register")); super("unregister", options("&cAn expansion name must be specified!", MINIMUM_ARGUMENTS));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final String requestedExpansion = args[0]; String requestedExpansion = args[0];
final PlaceholderExpansion expansion = PlaceholderAPIPlugin.getInstance().getExpansionManager() PlaceholderExpansion expansion = PlaceholderAPIPlugin.getInstance().getExpansionManager()
.getRegisteredExpansion(requestedExpansion); .getRegisteredExpansion(requestedExpansion);
if (expansion == null) { if (expansion == null) {
Msg.msg(sender, "&cFailed to find expansion: &f" + requestedExpansion); Msg.msg(sender, "&cFailed to find expansion: &f" + requestedExpansion);
return; return;
} }
@ -39,12 +37,11 @@ public final class UnregisterCommand extends Command {
} }
} }
@NotNull
@Override
public List<String> handleCompletion(@NotNull final CommandSender sender, @NotNull final String[] args) {
if (args.length == MINIMUM_ARGUMENTS) {
final Set<String> completions = PlaceholderAPI.getRegisteredIdentifiers();
@Override
public List<String> handleCompletion(CommandSender sender, String[] args) {
if (args.length == MINIMUM_ARGUMENTS) {
Set<String> completions = PlaceholderAPI.getRegisteredIdentifiers();
return StringUtil.copyPartialMatches(args[0], completions, new ArrayList<>(completions.size())); return StringUtil.copyPartialMatches(args[0], completions, new ArrayList<>(completions.size()));
} }

@ -7,7 +7,6 @@ import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -34,8 +33,8 @@ public final class VersionCommand extends Command {
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final PluginDescriptionFile description = PlaceholderAPIPlugin.getInstance().getDescription(); PluginDescriptionFile description = PlaceholderAPIPlugin.getInstance().getDescription();
Msg.msg(sender, "PlaceholderAPI &7version &b&o" + description.getVersion(), Msg.msg(sender, "PlaceholderAPI &7version &b&o" + description.getVersion(),
"&fCreated by&7: &b" + description.getAuthors(), "&fCreated by&7: &b" + description.getAuthors(),
@ -43,9 +42,9 @@ public final class VersionCommand extends Command {
"&fEcloud commands: &b/papi ecloud"); "&fEcloud commands: &b/papi ecloud");
} }
@NotNull
@Override @Override
public List<String> handleCompletion(@NotNull final CommandSender sender, @NotNull final String[] args) { public List<String> handleCompletion(CommandSender sender, String[] args) {
if (args.length == 1) { if (args.length == 1) {
return StringUtil.copyPartialMatches(args[0], COMPLETIONS, new ArrayList<>(COMPLETIONS.size())); return StringUtil.copyPartialMatches(args[0], COMPLETIONS, new ArrayList<>(COMPLETIONS.size()));
} }

@ -4,15 +4,14 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.Command; import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public final class EcloudClearCommand extends Command { public final class EcloudClearCommand extends Command {
public EcloudClearCommand() { public EcloudClearCommand() {
super("ecloud clear", permissions("placeholderapi.ecloud")); super("ecloud clear");
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
PlaceholderAPIPlugin.getInstance().getExpansionCloud().clean(); PlaceholderAPIPlugin.getInstance().getExpansionCloud().clean();
Msg.msg(sender, "&aThe cache has been cleared!!"); Msg.msg(sender, "&aThe cache has been cleared!!");
} }

@ -1,29 +1,25 @@
package me.clip.placeholderapi.commands.command; package me.clip.placeholderapi.commands.command.ecloud;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.Command; import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public final class DisableEcloudCommand extends Command { public final class EcloudDisableCommand extends Command {
public DisableEcloudCommand() { public EcloudDisableCommand() {
super("disablecloud", permissions("placeholderapi.ecloud")); super("ecloud disable");
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance(); PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance();
if (plugin.getExpansionCloud() == null) { if (plugin.getExpansionCloud() == null) {
Msg.msg(sender, "&7The cloud is already disabled!"); Msg.msg(sender, "&7The cloud is already disabled!");
return; return;
} }
plugin.disableCloud(); plugin.disableCloud();
plugin.getPlaceholderAPIConfig().setCloudEnabled(false); plugin.getPlaceholderAPIConfig().setCloudEnabled(false);
Msg.msg(sender, "&aThe cloud has been disabled!"); Msg.msg(sender, "&aThe cloud has been disabled!");
return;
} }
} }

@ -9,25 +9,24 @@ import me.clip.placeholderapi.expansion.cloud.ExpansionCloudManager;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public final class EcloudDownloadCommand extends Command { public final class EcloudDownloadCommand extends Command {
public EcloudDownloadCommand() { public EcloudDownloadCommand() {
super("ecloud download", options("&cAn expansion name must be specified!", 1, "placeholderapi.ecloud")); super("ecloud download", options("&cAn expansion name must be specified!", 1));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance(); PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance();
final String input = args[0]; String input = args[0];
final CloudExpansion expansion = plugin.getExpansionCloud().getCloudExpansion(input); CloudExpansion expansion = plugin.getExpansionCloud().getCloudExpansion(input);
if (expansion == null) { if (expansion == null) {
Msg.msg(sender, "&cNo expansion found with the name: &f" + input); Msg.msg(sender, "&cNo expansion found with the name: &f" + input);
return; return;
} }
final PlaceholderExpansion loaded = plugin.getExpansionManager().getRegisteredExpansion(input); PlaceholderExpansion loaded = plugin.getExpansionManager().getRegisteredExpansion(input);
if (loaded != null && loaded.isRegistered()) { if (loaded != null && loaded.isRegistered()) {
PlaceholderAPI.unregisterPlaceholderHook(loaded.getIdentifier()); PlaceholderAPI.unregisterPlaceholderHook(loaded.getIdentifier());
} }
@ -46,10 +45,23 @@ public final class EcloudDownloadCommand extends Command {
} }
Msg.msg(sender, "&aDownload starting for expansion: &f" + expansion.getName() + " &aversion: &f" + version); Msg.msg(sender, "&aDownload starting for expansion: &f" + expansion.getName() + " &aversion: &f" + version);
final String player = ((sender instanceof Player) ? sender.getName() : null); String player = ((sender instanceof Player) ? sender.getName() : null);
final ExpansionCloudManager cloud = plugin.getExpansionCloud(); ExpansionCloudManager cloud = plugin.getExpansionCloud();
cloud.downloadExpansion(player, expansion, version); cloud.downloadExpansion(player, expansion, version);
cloud.clean(); cloud.clean();
cloud.fetch(plugin.getPlaceholderAPIConfig().cloudAllowUnverifiedExpansions()); cloud.fetch(plugin.getPlaceholderAPIConfig().cloudAllowUnverifiedExpansions());
} }
// @Override
// public List<String> handleCompletion(CommandSender sender, String[] args) {
// List<String> downloads = new ArrayList<>();
// if (!PlaceholderAPI.isRegistered("player")) downloads.add("player");
//
// for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
// String identifier = plugin.getName();
// if (!PlaceholderAPI.isRegistered(identifier)) downloads.add(identifier);
// }
//
// return downloads;
// }
} }

@ -1,22 +1,20 @@
package me.clip.placeholderapi.commands.command; package me.clip.placeholderapi.commands.command.ecloud;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.Command; import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public final class EnableCloudCommand extends Command { public final class EcloudEnableCommand extends Command {
public EnableCloudCommand() { public EcloudEnableCommand() {
super("enablecloud", permissions("placeholderapi.ecloud")); super("ecloud enable");
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance(); PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance();
if (plugin.getExpansionCloud() != null) { if (plugin.getExpansionCloud() != null) {
Msg.msg(sender, "&7The cloud is already enabled!"); Msg.msg(sender, "&7The cloud is already enabled!");
return; return;
} }

@ -7,19 +7,18 @@ import me.clip.placeholderapi.util.Msg;
import me.rayzr522.jsonmessage.JSONMessage; import me.rayzr522.jsonmessage.JSONMessage;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import static me.clip.placeholderapi.util.Msg.color; import static me.clip.placeholderapi.util.Msg.color;
public final class EcloudInfoCommand extends Command { public final class EcloudInfoCommand extends Command {
public EcloudInfoCommand() { public EcloudInfoCommand() {
super("ecloud info", options("&cAn expansion name must be specified!", 1, "placeholderapi.ecloud")); super("ecloud info", options("&cAn expansion name must be specified!", 1));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final String input = args[0]; String input = args[0];
final CloudExpansion expansion = PlaceholderAPIPlugin.getInstance().getExpansionCloud().getCloudExpansion(input); CloudExpansion expansion = PlaceholderAPIPlugin.getInstance().getExpansionCloud().getCloudExpansion(input);
if (expansion == null) { if (expansion == null) {
Msg.msg(sender, "&cNo expansion found by the name: &f" + input); Msg.msg(sender, "&cNo expansion found by the name: &f" + input);
@ -35,7 +34,7 @@ public final class EcloudInfoCommand extends Command {
return; return;
} }
final Player p = (Player) sender; Player p = (Player) sender;
Msg.msg(sender, "&bExpansion&7: &f" + expansion.getName(), Msg.msg(sender, "&bExpansion&7: &f" + expansion.getName(),
"&bAuthor: &f" + expansion.getAuthor(), "&bAuthor: &f" + expansion.getAuthor(),
@ -43,7 +42,7 @@ public final class EcloudInfoCommand extends Command {
); );
// latest version // latest version
final JSONMessage latestVersion = JSONMessage JSONMessage latestVersion = JSONMessage
.create(color("&bLatest version: &f" + expansion.getLatestVersion())); .create(color("&bLatest version: &f" + expansion.getLatestVersion()));
latestVersion.tooltip(color("&bReleased: &f" + expansion.getTimeSinceLastUpdate() latestVersion.tooltip(color("&bReleased: &f" + expansion.getTimeSinceLastUpdate()
+ "\n&bUpdate information: &f" + expansion.getVersion().getReleaseNotes() + "\n&bUpdate information: &f" + expansion.getVersion().getReleaseNotes()
@ -51,7 +50,7 @@ public final class EcloudInfoCommand extends Command {
latestVersion.send(p); latestVersion.send(p);
// versions // versions
final JSONMessage versions = JSONMessage JSONMessage versions = JSONMessage
.create(color("&bVersions available: &f" + expansion.getVersions().size())); .create(color("&bVersions available: &f" + expansion.getVersions().size()));
versions.tooltip(color(String.join("&b, &f", expansion.getAvailableVersions()))); versions.tooltip(color(String.join("&b, &f", expansion.getAvailableVersions())));
versions.suggestCommand( versions.suggestCommand(
@ -60,7 +59,7 @@ public final class EcloudInfoCommand extends Command {
// placeholders // placeholders
if (expansion.getPlaceholders() != null) { if (expansion.getPlaceholders() != null) {
final JSONMessage placeholders = JSONMessage JSONMessage placeholders = JSONMessage
.create(color("&bPlaceholders: &f" + expansion.getPlaceholders().size())); .create(color("&bPlaceholders: &f" + expansion.getPlaceholders().size()));
placeholders.tooltip(color(String.join("&b, &f", expansion.getPlaceholders()))); placeholders.tooltip(color(String.join("&b, &f", expansion.getPlaceholders())));
placeholders.suggestCommand("/papi ecloud placeholders " + expansion.getName()); placeholders.suggestCommand("/papi ecloud placeholders " + expansion.getName());

@ -9,7 +9,6 @@ import me.rayzr522.jsonmessage.JSONMessage;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -25,13 +24,12 @@ public final class EcloudListCommand extends Command {
); );
public EcloudListCommand() { public EcloudListCommand() {
super("ecloud list", options("&cIncorrect usage! &7/papi ecloud list <all/author/installed> (page)", super("ecloud list", options("&cIncorrect usage! &7/papi ecloud list <all/author/installed> (page)", MINIMUM_ARGUMENTS));
MINIMUM_ARGUMENTS, "placeholderapi.ecloud"));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance(); PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance();
int page = 1; int page = 1;
String author; String author;
@ -103,7 +101,7 @@ public final class EcloudListCommand extends Command {
Msg.msg(sender, "&6Gold = Expansions which need updated"); Msg.msg(sender, "&6Gold = Expansions which need updated");
if (!(sender instanceof Player)) { if (!(sender instanceof Player)) {
final Map<String, CloudExpansion> expansions = new HashMap<>(); Map<String, CloudExpansion> expansions = new HashMap<>();
for (CloudExpansion exp : ex.values()) { for (CloudExpansion exp : ex.values()) {
if (exp == null || exp.getName() == null) { if (exp == null || exp.getName() == null) {
@ -113,7 +111,7 @@ public final class EcloudListCommand extends Command {
expansions.put(exp.getName(), exp); expansions.put(exp.getName(), exp);
} }
final List<String> ce = expansions.keySet().stream().sorted().collect(Collectors.toList()); List<String> ce = expansions.keySet().stream().sorted().collect(Collectors.toList());
int i = (int) ex.keySet().toArray()[0]; int i = (int) ex.keySet().toArray()[0];
@ -122,7 +120,7 @@ public final class EcloudListCommand extends Command {
continue; continue;
} }
final CloudExpansion expansion = expansions.get(name); CloudExpansion expansion = expansions.get(name);
Msg.msg(sender, Msg.msg(sender,
"&b" + i + "&7: " + (expansion.shouldUpdate() ? "&6" "&b" + i + "&7: " + (expansion.shouldUpdate() ? "&6"
@ -134,11 +132,11 @@ public final class EcloudListCommand extends Command {
return; return;
} }
final Player p = (Player) sender; Player p = (Player) sender;
final Map<String, CloudExpansion> expansions = new HashMap<>(); Map<String, CloudExpansion> expansions = new HashMap<>();
for (final CloudExpansion exp : ex.values()) { for (CloudExpansion exp : ex.values()) {
if (exp == null || exp.getName() == null) { if (exp == null || exp.getName() == null) {
continue; continue;
} }
@ -146,7 +144,7 @@ public final class EcloudListCommand extends Command {
expansions.put(exp.getName(), exp); expansions.put(exp.getName(), exp);
} }
final List<String> ce = expansions.keySet().stream().sorted().collect(Collectors.toList()); List<String> ce = expansions.keySet().stream().sorted().collect(Collectors.toList());
int i = page > 1 ? page * 10 : 0; int i = page > 1 ? page * 10 : 0;
@ -155,8 +153,8 @@ public final class EcloudListCommand extends Command {
continue; continue;
} }
final CloudExpansion expansion = expansions.get(name); CloudExpansion expansion = expansions.get(name);
final StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (expansion.shouldUpdate()) { if (expansion.shouldUpdate()) {
sb.append("&6Click to update to the latest version of this expansion\n\n"); sb.append("&6Click to update to the latest version of this expansion\n\n");
@ -172,13 +170,13 @@ public final class EcloudListCommand extends Command {
sb.append("&bLast updated&7: &f").append(expansion.getTimeSinceLastUpdate()).append(" ago\n"); sb.append("&bLast updated&7: &f").append(expansion.getTimeSinceLastUpdate()).append(" ago\n");
sb.append("\n").append(expansion.getDescription()); sb.append("\n").append(expansion.getDescription());
final String msg = color( String msg = color(
"&b" + (i + 1) + "&7: " + (expansion.shouldUpdate() ? "&6" "&b" + (i + 1) + "&7: " + (expansion.shouldUpdate() ? "&6"
: (expansion.hasExpansion() ? "&a" : "")) + expansion.getName()); : (expansion.hasExpansion() ? "&a" : "")) + expansion.getName());
final String hover = color(sb.toString()); String hover = color(sb.toString());
final JSONMessage line = JSONMessage.create(msg); JSONMessage line = JSONMessage.create(msg);
line.tooltip(hover); line.tooltip(hover);
if (expansion.shouldUpdate() || !expansion.hasExpansion()) { if (expansion.shouldUpdate() || !expansion.hasExpansion()) {
@ -192,9 +190,9 @@ public final class EcloudListCommand extends Command {
} }
} }
@NotNull
@Override @Override
public List<String> handleCompletion(@NotNull final CommandSender sender, @NotNull final String[] args) { public List<String> handleCompletion(CommandSender sender, String[] args) {
if (args.length == MINIMUM_ARGUMENTS) { if (args.length == MINIMUM_ARGUMENTS) {
return StringUtil.copyPartialMatches(args[0], COMPLETIONS, new ArrayList<>(COMPLETIONS.size())); return StringUtil.copyPartialMatches(args[0], COMPLETIONS, new ArrayList<>(COMPLETIONS.size()));
} }

@ -8,27 +8,26 @@ import me.clip.placeholderapi.util.Msg;
import me.rayzr522.jsonmessage.JSONMessage; import me.rayzr522.jsonmessage.JSONMessage;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public final class EcloudPlaceholdersCommand extends Command { public final class EcloudPlaceholdersCommand extends Command {
public EcloudPlaceholdersCommand() { public EcloudPlaceholdersCommand() {
super("ecloud placeholders", options("&cAn expansion name must be specified!", 1, "placeholderapi.ecloud")); super("ecloud placeholders", options("&cAn expansion name must be specified!", 1));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance(); PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance();
final String input = args[0]; String input = args[0];
final CloudExpansion expansion = plugin.getExpansionCloud().getCloudExpansion(input); CloudExpansion expansion = plugin.getExpansionCloud().getCloudExpansion(input);
if (expansion == null) { if (expansion == null) {
Msg.msg(sender, "&cNo expansion found by the name: &f" + input); Msg.msg(sender, "&cNo expansion found by the name: &f" + input);
return; return;
} }
final List<String> placeholders = expansion.getPlaceholders(); List<String> placeholders = expansion.getPlaceholders();
if (placeholders == null) { if (placeholders == null) {
Msg.msg(sender, "&cThe expansion: &f" + expansion.getName() Msg.msg(sender, "&cThe expansion: &f" + expansion.getName()
+ " &cdoes not have any placeholders listed.", + " &cdoes not have any placeholders listed.",
@ -45,15 +44,15 @@ public final class EcloudPlaceholdersCommand extends Command {
return; return;
} }
final Player p = (Player) sender; Player p = (Player) sender;
final JSONMessage message = JSONMessage.create(Msg.color("&bPlaceholders: &f" + placeholders.size())); JSONMessage message = JSONMessage.create(Msg.color("&bPlaceholders: &f" + placeholders.size()));
message.then("\n"); message.then("\n");
for (int i = 0; i < placeholders.size(); i++) { for (int i = 0; i < placeholders.size(); i++) {
message.then(i == placeholders.size() - 1 ? placeholders.get(i) : Msg.color(placeholders.get(i) + "&b, &f")); message.then(i == placeholders.size() - 1 ? placeholders.get(i) : Msg.color(placeholders.get(i) + "&b, &f"));
try { try {
message.tooltip(PlaceholderAPI.setPlaceholders(p, placeholders.get(i))); message.tooltip(PlaceholderAPI.setPlaceholders(p, placeholders.get(i)));
} catch (final Exception ignored) { } catch (Exception ignored) {
// Ignored exception // Ignored exception
} }
} }

@ -5,17 +5,16 @@ import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.expansion.cloud.ExpansionCloudManager; import me.clip.placeholderapi.expansion.cloud.ExpansionCloudManager;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public final class EcloudRefreshCommand extends Command { public final class EcloudRefreshCommand extends Command {
public EcloudRefreshCommand() { public EcloudRefreshCommand() {
super("ecloud refresh", permissions("placeholderapi.ecloud")); super("ecloud refresh");
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance(); PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance();
final ExpansionCloudManager cloud = plugin.getExpansionCloud(); ExpansionCloudManager cloud = plugin.getExpansionCloud();
Msg.msg(sender, "&aRefresh task started. Use &f/papi ecloud list all &ain a few!!"); Msg.msg(sender, "&aRefresh task started. Use &f/papi ecloud list all &ain a few!!");
cloud.clean(); cloud.clean();
cloud.fetch(plugin.getPlaceholderAPIConfig().cloudAllowUnverifiedExpansions()); cloud.fetch(plugin.getPlaceholderAPIConfig().cloudAllowUnverifiedExpansions());

@ -4,16 +4,15 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.Command; import me.clip.placeholderapi.commands.Command;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public final class EcloudStatusCommand extends Command { public final class EcloudStatusCommand extends Command {
public EcloudStatusCommand() { public EcloudStatusCommand() {
super("ecloud status", permissions("placeholderapi.ecloud")); super("ecloud status");
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance(); PlaceholderAPIPlugin plugin = PlaceholderAPIPlugin.getInstance();
Msg.msg(sender, "&bThere are &f" + plugin.getExpansionCloud().getCloudExpansions().size() Msg.msg(sender, "&bThere are &f" + plugin.getExpansionCloud().getCloudExpansions().size()
+ " &bexpansions available on the cloud.", + " &bexpansions available on the cloud.",
"&7A total of &f" + plugin.getExpansionCloud().getCloudAuthorCount() "&7A total of &f" + plugin.getExpansionCloud().getCloudAuthorCount()

@ -7,24 +7,22 @@ import me.clip.placeholderapi.util.Msg;
import me.rayzr522.jsonmessage.JSONMessage; import me.rayzr522.jsonmessage.JSONMessage;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public final class EcloudVersionInfoCommand extends Command { public final class EcloudVersionInfoCommand extends Command {
public EcloudVersionInfoCommand() { public EcloudVersionInfoCommand() {
super("ecloud versioninfo", options("&cIncorrect usage! &7/papi ecloud versioninfo <name> <version>", super("ecloud versioninfo", options("&cIncorrect usage! &7/papi ecloud versioninfo <name> <version>", 2));
2, "placeholderapi.ecloud"));
} }
@Override @Override
public void execute(@NotNull final CommandSender sender, @NotNull final String[] args) { public void execute(CommandSender sender, String[] args) {
final String input = args[0]; String input = args[0];
final CloudExpansion expansion = PlaceholderAPIPlugin.getInstance().getExpansionCloud().getCloudExpansion(input); CloudExpansion expansion = PlaceholderAPIPlugin.getInstance().getExpansionCloud().getCloudExpansion(input);
if (expansion == null) { if (expansion == null) {
Msg.msg(sender, "&cNo expansion found by the name: &f" + input); Msg.msg(sender, "&cNo expansion found by the name: &f" + input);
return; return;
} }
final CloudExpansion.Version version = expansion.getVersion(args[1]); CloudExpansion.Version version = expansion.getVersion(args[1]);
if (version == null) { if (version == null) {
Msg.msg(sender, "&cThe version specified does not exist for expansion: &f" + expansion.getName()); Msg.msg(sender, "&cThe version specified does not exist for expansion: &f" + expansion.getName());
return; return;
@ -39,10 +37,10 @@ public final class EcloudVersionInfoCommand extends Command {
return; return;
} }
final Player p = (Player) sender; Player p = (Player) sender;
final JSONMessage download = JSONMessage.create(Msg.color("&7Click to download this version")); JSONMessage download = JSONMessage.create(Msg.color("&7Click to download this version"));
download.suggestCommand( download.suggestCommand(
"/papi ecloud download " + expansion.getName() + " " + version.getVersion()); "/papi ecloud download " + expansion.getName() + ' ' + version.getVersion());
download.send(p); download.send(p);
} }
} }

@ -1,8 +0,0 @@
package me.clip.placeholderapi.exceptions;
public final class NoDefaultCommandException extends RuntimeException {
public NoDefaultCommandException(final String message) {
super(message);
}
}

@ -28,7 +28,6 @@ package me.clip.placeholderapi.expansion;
* @author Ryan McCarthy * @author Ryan McCarthy
*/ */
public interface Cacheable { public interface Cacheable {
/** /**
* Called when the implementing class is unregistered from PlaceholderAPI * Called when the implementing class is unregistered from PlaceholderAPI
*/ */

@ -30,11 +30,10 @@ import org.bukkit.entity.Player;
* @author Ryan McCarthy * @author Ryan McCarthy
*/ */
public interface Cleanable { public interface Cleanable {
/** /**
* Called when a player leaves the server * Called when a player leaves the server
* *
* @param p (@link Player} who left the server * @param player (@link Player} who left the server
*/ */
void cleanup(Player p); void cleanup(Player player);
} }

@ -20,6 +20,7 @@
*/ */
package me.clip.placeholderapi.expansion; package me.clip.placeholderapi.expansion;
import com.google.common.base.Strings;
import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.PlaceholderHook; import me.clip.placeholderapi.PlaceholderHook;
@ -41,15 +42,13 @@ public final class ExpansionManager {
public ExpansionManager(PlaceholderAPIPlugin instance) { public ExpansionManager(PlaceholderAPIPlugin instance) {
plugin = instance; plugin = instance;
File f = new File(PlaceholderAPIPlugin.getInstance().getDataFolder(), "expansions"); File f = new File(plugin.getDataFolder(), "expansions");
if (!f.exists()) { if (!f.exists()) f.mkdirs();
f.mkdirs();
}
} }
public PlaceholderExpansion getRegisteredExpansion(String name) { public PlaceholderExpansion getRegisteredExpansion(String name) {
for (Entry<String, PlaceholderHook> hook : PlaceholderAPI.getPlaceholders().entrySet()) { for (Entry<String, PlaceholderHook> hook : PlaceholderAPI.getPlaceholders().entrySet()) {
if (hook.getValue() instanceof PlaceholderExpansion) { if (hook.getValue().isExpansion()) {
if (name.equalsIgnoreCase(hook.getKey())) { if (name.equalsIgnoreCase(hook.getKey())) {
return (PlaceholderExpansion) hook.getValue(); return (PlaceholderExpansion) hook.getValue();
} }
@ -60,31 +59,28 @@ public final class ExpansionManager {
} }
public boolean registerExpansion(PlaceholderExpansion expansion) { public boolean registerExpansion(PlaceholderExpansion expansion) {
if (expansion == null || expansion.getIdentifier() == null) { if (expansion == null || expansion.getIdentifier() == null) return false;
return false;
}
if (expansion instanceof Configurable) { if (expansion instanceof Configurable) {
Map<String, Object> defaults = ((Configurable) expansion).getDefaults(); Map<String, Object> defaults = ((Configurable) expansion).getDefaults();
String pre = "expansions." + expansion.getIdentifier() + "."; String pre = expansion.getPathStarter();
FileConfiguration cfg = plugin.getConfig(); FileConfiguration cfg = plugin.getConfig();
boolean save = false; boolean save = false;
if (defaults != null) { if (defaults != null) {
for (Entry<String, Object> entries : defaults.entrySet()) { for (Entry<String, Object> entry : defaults.entrySet()) {
if (entries.getKey() == null || entries.getKey().isEmpty()) { String key = entry.getKey();
continue; if (Strings.isNullOrEmpty(key)) continue;
}
if (entries.getValue() == null) { if (entry.getValue() == null) {
if (cfg.contains(pre + entries.getKey())) { if (cfg.contains(pre + key)) {
save = true; save = true;
cfg.set(pre + entries.getKey(), null); cfg.set(pre + key, null);
} }
} else { } else {
if (!cfg.contains(pre + entries.getKey())) { if (!cfg.contains(pre + key)) {
save = true; save = true;
cfg.set(pre + entries.getKey(), entries.getValue()); cfg.set(pre + key, entry.getValue());
} }
} }
} }
@ -107,17 +103,11 @@ public final class ExpansionManager {
} }
} }
if (!expansion.canRegister()) { if (!expansion.canRegister()) return false;
return false; if (!expansion.register()) return false;
}
if (!expansion.register()) {
return false;
}
if (expansion instanceof Listener) { if (expansion instanceof Listener) {
Listener l = (Listener) expansion; Bukkit.getPluginManager().registerEvents((Listener) expansion, plugin);
Bukkit.getPluginManager().registerEvents(l, plugin);
} }
plugin.getLogger().info("Successfully registered expansion: " + expansion.getIdentifier()); plugin.getLogger().info("Successfully registered expansion: " + expansion.getIdentifier());
@ -143,29 +133,18 @@ public final class ExpansionManager {
public PlaceholderExpansion registerExpansion(String fileName) { public PlaceholderExpansion registerExpansion(String fileName) {
List<Class<?>> subs = FileUtil.getClasses("expansions", fileName, PlaceholderExpansion.class); List<Class<?>> subs = FileUtil.getClasses("expansions", fileName, PlaceholderExpansion.class);
if (subs == null || subs.isEmpty()) { if (subs == null || subs.isEmpty()) return null;
return null;
}
// only register the first instance found as an expansion jar should only have 1 class // Only register the first instance found as an expansion JAR should only have 1 class
// extending PlaceholderExpansion // extending PlaceholderExpansion
PlaceholderExpansion ex = createInstance(subs.get(0)); PlaceholderExpansion ex = createInstance(subs.get(0));
if (registerExpansion(ex)) { if (registerExpansion(ex)) return ex;
return ex;
}
return null; return null;
} }
public void registerAllExpansions() { public void registerAllExpansions() {
if (plugin == null) {
return;
}
List<Class<?>> subs = FileUtil.getClasses("expansions", null, PlaceholderExpansion.class); List<Class<?>> subs = FileUtil.getClasses("expansions", null, PlaceholderExpansion.class);
if (subs == null || subs.isEmpty()) { if (subs == null || subs.isEmpty()) return;
return;
}
for (Class<?> klass : subs) { for (Class<?> klass : subs) {
PlaceholderExpansion ex = createInstance(klass); PlaceholderExpansion ex = createInstance(klass);
@ -180,34 +159,29 @@ public final class ExpansionManager {
} }
} }
private PlaceholderExpansion createInstance(Class<?> klass) { private PlaceholderExpansion createInstance(Class<?> clazz) {
if (klass == null) { if (clazz == null) return null;
return null; if (!PlaceholderExpansion.class.isAssignableFrom(clazz)) return null;
}
PlaceholderExpansion ex = null;
if (!PlaceholderExpansion.class.isAssignableFrom(klass)) {
return null;
}
PlaceholderExpansion expansion = null;
try { try {
Constructor<?>[] c = klass.getConstructors(); Constructor<?>[] constructors = clazz.getConstructors();
if (c.length == 0) { if (constructors.length == 0) {
ex = (PlaceholderExpansion) klass.newInstance(); expansion = (PlaceholderExpansion) clazz.newInstance();
} else { } else {
for (Constructor<?> con : c) { for (Constructor<?> ctor : constructors) {
if (con.getParameterTypes().length == 0) { if (ctor.getParameterTypes().length == 0) {
ex = (PlaceholderExpansion) klass.newInstance(); expansion = (PlaceholderExpansion) ctor.newInstance();
break; break;
} }
} }
} }
} catch (Throwable t) { } catch (Throwable t) {
plugin.getLogger() plugin.getLogger()
.severe("Failed to init placeholder expansion from class: " + klass.getName()); .severe("Failed to init placeholder expansion from class: " + clazz.getName());
plugin.getLogger().severe(t.getMessage()); plugin.getLogger().severe(t.getMessage());
} }
return ex; return expansion;
} }
} }

@ -20,26 +20,28 @@
*/ */
package me.clip.placeholderapi.expansion; package me.clip.placeholderapi.expansion;
public enum NMSVersion { import com.google.common.base.Enums;
UNKNOWN("unknown"), import java.util.Optional;
SPIGOT_1_7_R1("v1_7_R1"),
SPIGOT_1_7_R2("v1_7_R2"), public enum NMSVersion {
SPIGOT_1_7_R3("v1_7_R3"), UNKNOWN("unknown"),
SPIGOT_1_7_R4("v1_7_R4"), SPIGOT_1_7_R1("v1_7_R1"),
SPIGOT_1_8_R1("v1_8_R1"), SPIGOT_1_7_R2("v1_7_R2"),
SPIGOT_1_8_R2("v1_8_R2"), SPIGOT_1_7_R3("v1_7_R3"),
SPIGOT_1_8_R3("v1_8_R3"), SPIGOT_1_7_R4("v1_7_R4"),
SPIGOT_1_9_R1("v1_9_R1"), SPIGOT_1_8_R1("v1_8_R1"),
SPIGOT_1_9_R2("v1_9_R2"), SPIGOT_1_8_R2("v1_8_R2"),
SPIGOT_1_10_R1("v1_10_R1"), SPIGOT_1_8_R3("v1_8_R3"),
SPIGOT_1_11_R1("v1_11_R1"), SPIGOT_1_9_R1("v1_9_R1"),
SPIGOT_1_12_R1("v1_12_R1"), SPIGOT_1_9_R2("v1_9_R2"),
SPIGOT_1_13_R1("v1_13_R1"), SPIGOT_1_10_R1("v1_10_R1"),
SPIGOT_1_13_R2("v1_13_R2"), SPIGOT_1_11_R1("v1_11_R1"),
SPIGOT_1_14_R1("v1_14_R1"), SPIGOT_1_12_R1("v1_12_R1"),
SPIGOT_1_15_R1("v1_15_R1"), SPIGOT_1_13_R1("v1_13_R1"),
SPIGOT_1_16_R1("v1_16_R1"); SPIGOT_1_13_R2("v1_13_R2"),
SPIGOT_1_14_R1("v1_14_R1"),
SPIGOT_1_15_R1("v1_15_R1");
private final String version; private final String version;
@ -48,17 +50,12 @@ public enum NMSVersion {
} }
public static NMSVersion getVersion(String version) { public static NMSVersion getVersion(String version) {
for (NMSVersion v : values()) { // Guava caches values() as well.
if (v.getVersion().equalsIgnoreCase(version)) { Optional<NMSVersion> opt = Enums.getIfPresent(NMSVersion.class, version).toJavaUtil();
return v; return opt.orElse(NMSVersion.UNKNOWN);
}
}
return NMSVersion.UNKNOWN;
} }
public String getVersion() { public String getVersion() {
return version; return version;
} }
} }

@ -26,11 +26,11 @@ import me.clip.placeholderapi.PlaceholderHook;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.List; import java.util.List;
public abstract class PlaceholderExpansion extends PlaceholderHook { public abstract class PlaceholderExpansion extends PlaceholderHook {
/** /**
* The name of this expansion * The name of this expansion
* *
@ -123,60 +123,58 @@ public abstract class PlaceholderExpansion extends PlaceholderHook {
} }
/** /**
* Quick getter for the {@link PlaceholderAPIPlugin} instance * Quick getter for the {@link PlaceholderAPIPlugin} config.
* *
* @return {@link PlaceholderAPIPlugin} instance * @return {@link PlaceholderAPIPlugin} config instance.
*/ */
public PlaceholderAPIPlugin getPlaceholderAPI() { public FileConfiguration getConfig() {
return PlaceholderAPIPlugin.getInstance(); return PlaceholderAPIPlugin.getInstance().getConfig();
} }
public String getString(String path, String def) { public String getString(String path, String def) {
return getPlaceholderAPI().getConfig() return getConfig().getString(getPathStarter() + path, def);
.getString("expansions." + getIdentifier() + "." + path, def);
} }
public int getInt(String path, int def) { public int getInt(String path, int def) {
return getPlaceholderAPI().getConfig() return getConfig().getInt(getPathStarter() + path, def);
.getInt("expansions." + getIdentifier() + "." + path, def);
} }
public long getLong(String path, long def) { public long getLong(String path, long def) {
return getPlaceholderAPI().getConfig() return getConfig().getLong(getPathStarter() + path, def);
.getLong("expansions." + getIdentifier() + "." + path, def);
} }
public double getDouble(String path, double def) { public double getDouble(String path, double def) {
return getPlaceholderAPI().getConfig() return getConfig().getDouble(getPathStarter() + path, def);
.getDouble("expansions." + getIdentifier() + "." + path, def);
} }
public List<String> getStringList(String path) { public List<String> getStringList(String path) {
return getPlaceholderAPI().getConfig() return getConfig().getStringList(getPathStarter() + path);
.getStringList("expansions." + getIdentifier() + "." + path);
} }
public Object get(String path, Object def) { public Object get(String path, Object def) {
return getPlaceholderAPI().getConfig().get("expansions." + getIdentifier() + "." + path, def); return getConfig().get(getPathStarter() + path, def);
} }
public ConfigurationSection getConfigSection(String path) { public ConfigurationSection getConfigSection(String path) {
return getPlaceholderAPI().getConfig() return getConfig().getConfigurationSection(getPathStarter() + path);
.getConfigurationSection("expansions." + getIdentifier() + "." + path);
} }
public ConfigurationSection getConfigSection() { public ConfigurationSection getConfigSection() {
return getPlaceholderAPI().getConfig().getConfigurationSection("expansions." + getIdentifier()); return getConfig().getConfigurationSection("expansions." + getIdentifier());
} }
public boolean configurationContains(String path) { public boolean configurationContains(String path) {
return getPlaceholderAPI().getConfig().contains("expansions." + getIdentifier() + "." + path); return getConfig().contains(getPathStarter() + path);
} }
protected String getPathStarter() {
return "expansions." + getIdentifier() + '.';
}
/** /**
* @deprecated As of versions greater than 2.8.7, use {@link #getRequiredPlugin()} * @deprecated As of versions greater than 2.8.7, use {@link #getRequiredPlugin()}
*/ */
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated @Deprecated
public String getPlugin() { public String getPlugin() {
return null; return null;

@ -23,6 +23,5 @@ package me.clip.placeholderapi.expansion;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
public interface Relational { public interface Relational {
String onPlaceholderRequest(Player one, Player two, String identifier); String onPlaceholderRequest(Player one, Player two, String identifier);
} }

@ -20,9 +20,7 @@
*/ */
package me.clip.placeholderapi.expansion; package me.clip.placeholderapi.expansion;
public interface Taskable { public interface Taskable {
/** /**
* Called when the implementing class has successfully been registered to the placeholder map * Called when the implementing class has successfully been registered to the placeholder map
* Tasks that need to be performed when this expansion is registered should go here * Tasks that need to be performed when this expansion is registered should go here

@ -21,7 +21,6 @@
package me.clip.placeholderapi.expansion; package me.clip.placeholderapi.expansion;
public final class Version { public final class Version {
private final boolean isSpigot; private final boolean isSpigot;
private final String version; private final String version;
@ -41,5 +40,4 @@ public final class Version {
public boolean compareTo(String version) { public boolean compareTo(String version) {
return getVersion().equalsIgnoreCase(version); return getVersion().equalsIgnoreCase(version);
} }
} }

@ -36,5 +36,5 @@ public interface VersionSpecific {
* *
* @return true if your expansion is compatible with the version the server is running. * @return true if your expansion is compatible with the version the server is running.
*/ */
boolean isCompatibleWith(Version v); boolean isCompatibleWith(Version version);
} }

@ -26,11 +26,8 @@ import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class CloudExpansion { public class CloudExpansion {
private String name, author,
private String name,
author,
latest_version, latest_version,
description, description,
source_url, source_url,
@ -74,14 +71,12 @@ public class CloudExpansion {
} }
public Version getVersion() { public Version getVersion() {
return getLatestVersion() == null ? null : getVersion(getLatestVersion()); return latest_version == null ? null : getVersion(latest_version);
} }
public Version getVersion(String version) { public Version getVersion(String version) {
return versions == null ? null : versions.stream() return versions == null ? null : versions.stream()
.filter(v -> v.getVersion().equals(version)) .filter(v -> v.getVersion().equals(version)).findFirst().orElse(null);
.findFirst()
.orElse(null);
} }
public List<String> getAvailableVersions() { public List<String> getAvailableVersions() {
@ -140,6 +135,10 @@ public class CloudExpansion {
return verified; return verified;
} }
public void setVerified(boolean verified) {
this.verified = verified;
}
public long getLastUpdate() { public long getLastUpdate() {
return last_update; return last_update;
} }
@ -156,6 +155,10 @@ public class CloudExpansion {
return average_rating; return average_rating;
} }
public void setAverage_rating(double average_rating) {
this.average_rating = average_rating;
}
public List<String> getPlaceholders() { public List<String> getPlaceholders() {
return placeholders; return placeholders;
} }
@ -172,7 +175,11 @@ public class CloudExpansion {
this.versions = versions; this.versions = versions;
} }
public class Version { public void setRatings_count(long ratings_count) {
this.ratings_count = ratings_count;
}
public static class Version {
private String url, version, release_notes; private String url, version, release_notes;
public String getUrl() { public String getUrl() {

@ -37,37 +37,29 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
public class ExpansionCloudManager { public class ExpansionCloudManager {
private static final String API_URL = "http://api.extendedclip.com/v2/"; private static final String API_URL = "http://api.extendedclip.com/v2/";
private static final Gson GSON = new Gson(); private static final Gson GSON = new Gson();
private final PlaceholderAPIPlugin plugin; private final PlaceholderAPIPlugin plugin;
private final File expansionsDir; private final File expansionsDir;
private final List<String> downloading = new ArrayList<>(); private final Set<String> downloading = new HashSet<>();
private final Map<Integer, CloudExpansion> remote = new TreeMap<>(); private final Map<Integer, CloudExpansion> remote = new TreeMap<>();
public ExpansionCloudManager(PlaceholderAPIPlugin plugin) { public ExpansionCloudManager(PlaceholderAPIPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
expansionsDir = new File(plugin.getDataFolder(), "expansions"); expansionsDir = new File(plugin.getDataFolder(), "expansions");
final boolean result = expansionsDir.mkdirs(); if (expansionsDir.mkdirs()) {
if (result) {
plugin.getLogger().info("Created Expansions Directory"); plugin.getLogger().info("Created Expansions Directory");
} }
} }
public void clean() { public void clean() {
remote.clear(); remote.clear();
downloading.clear(); downloading.clear();
} }
public Map<Integer, CloudExpansion> getCloudExpansions() { public Map<Integer, CloudExpansion> getCloudExpansions() {
return remote; return remote;
} }
@ -80,7 +72,6 @@ public class ExpansionCloudManager {
.orElse(null); .orElse(null);
} }
public int getCloudAuthorCount() { public int getCloudAuthorCount() {
return remote.values() return remote.values()
.stream() .stream()
@ -126,14 +117,10 @@ public class ExpansionCloudManager {
public int getPagesAvailable(Map<Integer, CloudExpansion> map, int amount) { public int getPagesAvailable(Map<Integer, CloudExpansion> map, int amount) {
if (map == null) { if (map == null) return 0;
return 0;
}
int pages = map.size() > 0 ? 1 : 0; int pages = map.size() > 0 ? 1 : 0;
if (pages == 0) { if (pages == 0) return 0;
return pages;
}
if (map.size() > amount) { if (map.size() > amount) {
pages = map.size() / amount; pages = map.size() / amount;
@ -159,12 +146,11 @@ public class ExpansionCloudManager {
return ex; return ex;
} }
public void fetch(boolean allowUnverified) { public void fetch(boolean allowUnverified) {
plugin.getLogger().info("Fetching available expansion information..."); plugin.getLogger().info("Fetching available expansion information...");
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> { plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
final Map<String, CloudExpansion> data = new HashMap<>(); Map<String, CloudExpansion> data = new HashMap<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(API_URL).openStream()))) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(API_URL).openStream()))) {
data.putAll(GSON.fromJson(reader, new TypeToken<Map<String, CloudExpansion>>() { data.putAll(GSON.fromJson(reader, new TypeToken<Map<String, CloudExpansion>>() {
@ -173,11 +159,12 @@ public class ExpansionCloudManager {
if (plugin.getPlaceholderAPIConfig().isDebugMode()) { if (plugin.getPlaceholderAPIConfig().isDebugMode()) {
ex.printStackTrace(); ex.printStackTrace();
} else { } else {
plugin.getLogger().warning("Unable to fetch expansions!\nThere was an error with the server host connecting to the PlaceholderAPI eCloud (https://api.extendedclip.com/v2/)"); plugin.getLogger().warning("Unable to fetch expansions!\nThere was an error with the server host connecting to the PlaceholderAPI eCloud (https://api" +
".extendedclip.com/v2/)");
} }
} }
final List<CloudExpansion> unsorted = new ArrayList<>(); List<CloudExpansion> unsorted = new ArrayList<>();
data.forEach((name, cexp) -> { data.forEach((name, cexp) -> {
if ((allowUnverified || cexp.isVerified()) && cexp.getLatestVersion() != null && cexp.getVersion(cexp.getLatestVersion()) != null) { if ((allowUnverified || cexp.isVerified()) && cexp.getLatestVersion() != null && cexp.getVersion(cexp.getLatestVersion()) != null) {
@ -204,7 +191,6 @@ public class ExpansionCloudManager {
} }
plugin.getLogger().info(count + " placeholder expansions are available on the cloud."); plugin.getLogger().info(count + " placeholder expansions are available on the cloud.");
long updates = getToUpdateCount(); long updates = getToUpdateCount();
if (updates > 0) { if (updates > 0) {
@ -220,19 +206,15 @@ public class ExpansionCloudManager {
private void download(URL url, String name) throws IOException { private void download(URL url, String name) throws IOException {
InputStream is = null; InputStream is = null;
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
URLConnection urlConn = url.openConnection(); URLConnection urlConn = url.openConnection();
is = urlConn.getInputStream(); is = urlConn.getInputStream();
fos = new FileOutputStream( fos = new FileOutputStream(
expansionsDir.getAbsolutePath() + File.separator + "Expansion-" + name + ".jar"); expansionsDir.getAbsolutePath() + File.separator + "Expansion-" + name + ".jar");
byte[] buffer = new byte[is.available()]; byte[] buffer = new byte[is.available()];
int l; int l;
while ((l = is.read(buffer)) > 0) { while ((l = is.read(buffer)) > 0) {
@ -252,42 +234,35 @@ public class ExpansionCloudManager {
} }
public void downloadExpansion(final String player, final CloudExpansion ex) { public void downloadExpansion(String player, CloudExpansion ex) {
downloadExpansion(player, ex, ex.getLatestVersion()); downloadExpansion(player, ex, ex.getLatestVersion());
} }
public void downloadExpansion(final String player, final CloudExpansion ex, final String version) { public void downloadExpansion(String player, CloudExpansion ex, String version) {
if (downloading.contains(ex.getName())) { if (downloading.contains(ex.getName())) {
return; return;
} }
final CloudExpansion.Version ver = ex.getVersions() CloudExpansion.Version ver = ex.getVersions()
.stream() .stream()
.filter(v -> v.getVersion().equals(version)) .filter(v -> v.getVersion().equals(version))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
if (ver == null) { if (ver == null) return;
return;
}
downloading.add(ex.getName()); downloading.add(ex.getName());
plugin.getLogger().info("Attempting download of expansion: " + ex.getName() + (player != null ? " by user: " + player : "") + " from url: " + ver.getUrl()); plugin.getLogger().info("Attempting download of expansion: " + ex.getName() + (player != null ? " by user: " + player : "") + " from url: " + ver.getUrl());
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
try { try {
download(new URL(ver.getUrl()), ex.getName()); download(new URL(ver.getUrl()), ex.getName());
plugin.getLogger().info("Download of expansion: " + ex.getName() + " complete!"); plugin.getLogger().info("Download of expansion: " + ex.getName() + " complete!");
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger() plugin.getLogger()
.warning("Failed to download expansion: " + ex.getName() + " from: " + ver.getUrl()); .warning("Failed to download expansion: " + ex.getName() + " from: " + ver.getUrl());
Bukkit.getScheduler().runTask(plugin, () -> { Bukkit.getScheduler().runTask(plugin, () -> {
downloading.remove(ex.getName()); downloading.remove(ex.getName());
if (player != null) { if (player != null) {
@ -314,7 +289,6 @@ public class ExpansionCloudManager {
} }
} }
}); });
}); });
} }
} }

@ -25,9 +25,7 @@ import me.clip.placeholderapi.PlaceholderHook;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
/** @SuppressWarnings("DeprecatedIsStillUsed")
* Use {@link me.clip.placeholderapi.expansion.PlaceholderExpansion} instead
*/
@Deprecated @Deprecated
public abstract class EZPlaceholderHook extends PlaceholderHook { public abstract class EZPlaceholderHook extends PlaceholderHook {
@ -35,8 +33,8 @@ public abstract class EZPlaceholderHook extends PlaceholderHook {
private final String plugin; private final String plugin;
public EZPlaceholderHook(Plugin plugin, String identifier) { public EZPlaceholderHook(Plugin plugin, String identifier) {
Validate.notNull(plugin, "Plugin can not be null!"); Validate.notNull(plugin, "Plugin cannot be null");
Validate.notNull(identifier, "Placeholder name can not be null!"); Validate.notNull(identifier, "Placeholder name cannot be null");
this.identifier = identifier; this.identifier = identifier;
this.plugin = plugin.getName(); this.plugin = plugin.getName();
} }

@ -0,0 +1,39 @@
package me.clip.placeholderapi.listeners;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.bukkit.Bukkit;
/**
* The purpose of this class is to filter the console warning messages when the plugin
* tries to load placeholder expansions from other jars in the plugins folder.
*/
public class ApacheListener extends AbstractFilter {
private boolean cancelled = false;
public ApacheListener(PlaceholderAPIPlugin plugin) {
org.apache.logging.log4j.core.Logger logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger();
logger.addFilter(this);
// 3 second should be more than enough. I have no idea how to unregister a filter.
Bukkit.getScheduler().runTaskLater(plugin, () -> cancelled = true, 3 * 20L);
}
@Override
public Result filter(LogEvent event) {
if (cancelled) return Result.NEUTRAL;
if (event.getLevel() != Level.WARN) return Result.NEUTRAL;
if (!event.getLoggerName().equals("PlaceholderAPI")) return Result.NEUTRAL;
// Format:
// Loaded class {CLASS} from {PLUGIN} {VERSION} which is not a depend, softdepend or loadbefore of this plugin.
// E.g.
// Loaded class com.earth2me.essentials.Essentials from PlaceholderAPI v2.10.5-DEV-84 which is not a depend, softdepend or loadbefore of this plugin.
String message = event.getMessage().getFormattedMessage();
if (message.startsWith("Loaded class") && message.endsWith("which is not a depend, softdepend or loadbefore of this plugin.")) return Result.DENY;
return Result.NEUTRAL;
}
}

@ -37,13 +37,8 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.event.server.PluginDisableEvent;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class PlaceholderListener implements Listener { public class PlaceholderListener implements Listener {
private final PlaceholderAPIPlugin plugin; private final PlaceholderAPIPlugin plugin;
public PlaceholderListener(PlaceholderAPIPlugin instance) { public PlaceholderListener(PlaceholderAPIPlugin instance) {
@ -53,22 +48,23 @@ public class PlaceholderListener implements Listener {
@EventHandler @EventHandler
public void onExpansionUnregister(ExpansionUnregisterEvent event) { public void onExpansionUnregister(ExpansionUnregisterEvent event) {
if (event.getExpansion() instanceof Listener) { PlaceholderExpansion expansion = event.getExpansion();
HandlerList.unregisterAll((Listener) event.getExpansion()); if (expansion instanceof Listener) {
HandlerList.unregisterAll((Listener) expansion);
} }
if (event.getExpansion() instanceof Taskable) { if (expansion instanceof Taskable) {
((Taskable) event.getExpansion()).stop(); ((Taskable) expansion).stop();
} }
if (event.getExpansion() instanceof Cacheable) { if (expansion instanceof Cacheable) {
((Cacheable) event.getExpansion()).clear(); ((Cacheable) expansion).clear();
} }
if (plugin.getExpansionCloud() != null) { if (plugin.getExpansionCloud() != null) {
CloudExpansion ex = plugin.getExpansionCloud() CloudExpansion ex = plugin.getExpansionCloud()
.getCloudExpansion(event.getExpansion().getName()); .getCloudExpansion(expansion.getName());
if (ex != null) { if (ex != null) {
ex.setHasExpansion(false); ex.setHasExpansion(false);
@ -78,28 +74,20 @@ public class PlaceholderListener implements Listener {
} }
@EventHandler(priority = EventPriority.HIGH) @EventHandler(priority = EventPriority.HIGH)
public void onPluginUnload(PluginDisableEvent e) { public void onPluginUnload(PluginDisableEvent event) {
String n = e.getPlugin().getName(); // A plugin name cannot be null.
String name = event.getPlugin().getName();
if (name.equals(plugin.getName())) return;
if (n.equals(plugin.getName())) { for (PlaceholderHook hook : PlaceholderAPI.getPlaceholders().values()) {
return; if (hook.isExpansion()) {
} PlaceholderExpansion ex = (PlaceholderExpansion) hook;
Map<String, PlaceholderHook> hooks = PlaceholderAPI.getPlaceholders(); if (ex.getRequiredPlugin() == null) continue;
for (Entry<String, PlaceholderHook> entry : hooks.entrySet()) { if (ex.getRequiredPlugin().equalsIgnoreCase(name)) {
PlaceholderHook hook = entry.getValue(); if (PlaceholderAPI.unregisterExpansion(ex)) {
plugin.getLogger().info("Unregistered placeholder expansion: " + ex.getIdentifier());
if (hook instanceof PlaceholderExpansion) {
PlaceholderExpansion expansion = (PlaceholderExpansion) hook;
if (expansion.getRequiredPlugin() == null) {
continue;
}
if (expansion.getRequiredPlugin().equalsIgnoreCase(n)) {
if (PlaceholderAPI.unregisterExpansion(expansion)) {
plugin.getLogger().info("Unregistered placeholder expansion: " + expansion.getIdentifier());
} }
} }
} }
@ -107,16 +95,10 @@ public class PlaceholderListener implements Listener {
} }
@EventHandler @EventHandler
public void onQuit(PlayerQuitEvent e) { public void onQuit(PlayerQuitEvent event) {
Set<PlaceholderExpansion> expansions = PlaceholderAPI.getExpansions(); for (PlaceholderHook hook : PlaceholderAPI.getPlaceholders().values()) {
if (hook instanceof Cleanable) {
if (expansions.isEmpty()) { ((Cleanable) hook).cleanup(event.getPlayer());
return;
}
for (PlaceholderExpansion ex : expansions) {
if (ex instanceof Cleanable) {
((Cleanable) ex).cleanup(e.getPlayer());
} }
} }
} }

@ -31,7 +31,6 @@ import org.bukkit.event.server.ServerLoadEvent;
import java.util.Map; import java.util.Map;
public class ServerLoadEventListener implements Listener { public class ServerLoadEventListener implements Listener {
private final PlaceholderAPIPlugin plugin; private final PlaceholderAPIPlugin plugin;
public ServerLoadEventListener(PlaceholderAPIPlugin instance) { public ServerLoadEventListener(PlaceholderAPIPlugin instance) {
@ -40,19 +39,19 @@ public class ServerLoadEventListener implements Listener {
} }
/** /**
* This method will be called when the server is first loaded * This method will be called when the server is first loaded.
* <p> * <p>
* The goal of the method is to register all the expansions as soon as possible * The goal of the method is to register all the expansions as soon as possible
* especially before players can join * especially before players can join.
* <p> * <p>
* This will ensure no issues with expanions and hooks. * This will ensure no issues with expanions and hooks.
* *
* @param e the server load event * @param event the server load event.
*/ */
@EventHandler @EventHandler
public void onServerLoad(ServerLoadEvent e) { public void onServerLoad(ServerLoadEvent event) {
plugin.getLogger().info("Placeholder expansion registration initializing..."); plugin.getLogger().info("Placeholder expansion registration initializing...");
final Map<String, PlaceholderHook> alreadyRegistered = PlaceholderAPI.getPlaceholders(); Map<String, PlaceholderHook> alreadyRegistered = PlaceholderAPI.getPlaceholders();
plugin.getExpansionManager().registerAllExpansions(); plugin.getExpansionManager().registerAllExpansions();
if (alreadyRegistered != null && !alreadyRegistered.isEmpty()) { if (alreadyRegistered != null && !alreadyRegistered.isEmpty()) {

@ -1,9 +0,0 @@
package me.clip.placeholderapi.util;
public class Constants {
public static final String ADMIN_PERMISSION = "placeholderapi.admin";
public static final String ECLOUD_PERMISSION = "placeholderapi.ecloud";
public static final String INFO_PERMISSION = "placeholderapi.info";
public static final String LIST_PERMISSION = "placeholderapi.list";
public static final String RELOAD_PERMISSION = "placeholderapi.reload";
}

@ -42,65 +42,50 @@ public class FileUtil {
try { try {
File f = new File(PlaceholderAPIPlugin.getInstance().getDataFolder(), folder); File f = new File(PlaceholderAPIPlugin.getInstance().getDataFolder(), folder);
if (!f.exists()) { if (!f.exists()) return list;
return list;
}
FilenameFilter fileNameFilter = (dir, name) -> { FilenameFilter fileNameFilter = (dir, name) -> {
boolean isJar = name.endsWith(".jar");
if (fileName != null) { if (fileName != null) {
return name.endsWith(".jar") && name.replace(".jar", "") return isJar && name.substring(0, name.length() - 4)
.equalsIgnoreCase(fileName.replace(".jar", "")); .equalsIgnoreCase(fileName.substring(0, fileName.length() - 4));
} }
return name.endsWith(".jar"); return isJar;
}; };
File[] jars = f.listFiles(fileNameFilter); File[] jars = f.listFiles(fileNameFilter);
if (jars == null) { if (jars == null) return list;
return list;
}
for (File file : jars) { for (File file : jars) {
list = gather(file.toURI().toURL(), list, type); list = gather(file.toURI().toURL(), list, type);
} }
return list; return list;
} catch (Throwable t) { } catch (Throwable ignored) {
} }
return null; return null;
} }
private static List<Class<?>> gather(URL jar, List<Class<?>> list, Class<?> clazz) { private static List<Class<?>> gather(URL jar, List<Class<?>> list, Class<?> clazz) {
if (list == null) { // list cannot be null.
list = new ArrayList<>();
}
try (URLClassLoader cl = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader()); try (URLClassLoader cl = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader());
JarInputStream jis = new JarInputStream(jar.openStream())) { JarInputStream jis = new JarInputStream(jar.openStream())) {
while (true) { JarEntry entry;
JarEntry j = jis.getNextJarEntry(); while ((entry = jis.getNextJarEntry()) != null) {
if (j == null) { String name = entry.getName();
break; if (name == null || name.isEmpty()) continue;
}
String name = j.getName();
if (name == null || name.isEmpty()) {
continue;
}
if (name.endsWith(".class")) { if (name.endsWith(".class")) {
name = name.replace("/", "."); name = name.substring(0, name.length() - 6).replace('/', '.');
String cname = name.substring(0, name.lastIndexOf(".class"));
Class<?> c = cl.loadClass(cname); Class<?> loaded = cl.loadClass(name);
if (clazz.isAssignableFrom(c)) { if (clazz.isAssignableFrom(loaded)) list.add(loaded);
list.add(c);
}
} }
} }
} catch (Throwable t) { } catch (Throwable ignored) {
} }
return list; return list;

@ -24,17 +24,20 @@ import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.Arrays; public class Msg {
import java.util.Objects; public static void msg(CommandSender sender, String... messages) {
import java.util.stream.Collectors; for (String message : messages) {
String msg = color(message);
public final class Msg { sender.sendMessage(msg);
public static void msg(CommandSender s, String... msg) { }
s.sendMessage(Arrays.stream(msg).filter(Objects::nonNull).map(Msg::color).collect(Collectors.joining("\n")));
} }
public static void broadcast(String... msg) { public static void broadcast(String... messages) {
Arrays.stream(msg).filter(Objects::nonNull).map(Msg::color).forEach(Bukkit::broadcastMessage); CommandSender sender = Bukkit.getConsoleSender();
for (String message : messages) {
String msg = color(message);
sender.sendMessage(msg);
}
} }
public static String color(String text) { public static String color(String text) {

@ -1,28 +0,0 @@
/*
*
* PlaceholderAPI
* Copyright (C) 2019 Ryan McCarthy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package me.clip.placeholderapi.util;
public enum TimeFormat {
DAYS,
HOURS,
MINUTES,
SECONDS
}

@ -22,10 +22,10 @@ package me.clip.placeholderapi.util;
import java.time.Duration; import java.time.Duration;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
public class TimeUtil { public class TimeUtil {
public static String getRemaining(int seconds, TimeUnit type) {
public static String getRemaining(int seconds, TimeFormat type) {
if (seconds < 60) { if (seconds < 60) {
switch (type) { switch (type) {
case DAYS: case DAYS:
@ -124,8 +124,8 @@ public class TimeUtil {
* @param duration {@link Duration} (eg, Duration.of(20, {@link ChronoUnit#SECONDS}) for 20 seconds) * @param duration {@link Duration} (eg, Duration.of(20, {@link ChronoUnit#SECONDS}) for 20 seconds)
* @return formatted time * @return formatted time
*/ */
public static String getTime(final Duration duration) { public static String getTime(Duration duration) {
final StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
long seconds = duration.getSeconds(); long seconds = duration.getSeconds();
long minutes = seconds / 60; long minutes = seconds / 60;

@ -18,13 +18,13 @@
* *
* *
*/ */
package me.clip.placeholderapi.updatechecker; package me.clip.placeholderapi.util;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.util.Msg; import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
@ -32,10 +32,11 @@ import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
public class UpdateChecker implements Listener { public class UpdateChecker implements Listener {
private static final int RESOURCE_ID = 6245;
private final int RESOURCE_ID = 6245; private static final String SPIGOT_API = "https://api.spigotmc.org/legacy/update.php?resource=" + RESOURCE_ID;
private final PlaceholderAPIPlugin plugin; private final PlaceholderAPIPlugin plugin;
private final String pluginVersion; private final String pluginVersion;
private String spigotVersion; private String spigotVersion;
@ -57,39 +58,35 @@ public class UpdateChecker implements Listener {
public void fetch() { public void fetch() {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
try { try {
HttpsURLConnection con = (HttpsURLConnection) new URL( HttpsURLConnection con = (HttpsURLConnection) new URL(SPIGOT_API).openConnection();
"https://api.spigotmc.org/legacy/update.php?resource=" + RESOURCE_ID).openConnection();
// Prevents the server from freezing with bad internet connection.
con.setRequestMethod("GET"); con.setRequestMethod("GET");
spigotVersion = new BufferedReader(new InputStreamReader(con.getInputStream())).readLine(); con.setConnectTimeout(2000);
con.setReadTimeout(2000);
spigotVersion = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8)).readLine();
} catch (Exception ex) { } catch (Exception ex) {
plugin.getLogger().info("Failed to check for updates on spigot."); plugin.getLogger().warning("Failed to check for updates on spigot.");
return;
}
if (spigotVersion == null || spigotVersion.isEmpty()) {
return; return;
} }
if (spigotVersion == null || spigotVersion.isEmpty()) return;
updateAvailable = spigotIsNewer(); updateAvailable = spigotIsNewer();
if (!updateAvailable) return;
if (!updateAvailable) {
return;
}
Bukkit.getScheduler().runTask(plugin, () -> { Bukkit.getScheduler().runTask(plugin, () -> {
plugin.getLogger() plugin.getLogger()
.info("An update for PlaceholderAPI (v" + getSpigotVersion() + ") is available at:"); .info("An update for PlaceholderAPI (v" + spigotVersion + ") is available at:");
plugin.getLogger() plugin.getLogger()
.info("https://www.spigotmc.org/resources/placeholderapi." + RESOURCE_ID + "/"); .info("https://www.spigotmc.org/resources/" + RESOURCE_ID + '/');
Bukkit.getPluginManager().registerEvents(this, plugin); Bukkit.getPluginManager().registerEvents(this, plugin);
}); });
}); });
} }
private boolean spigotIsNewer() { private boolean spigotIsNewer() {
if (spigotVersion == null || spigotVersion.isEmpty()) { if (spigotVersion == null || spigotVersion.isEmpty()) return false;
return false;
}
String plV = toReadable(pluginVersion); String plV = toReadable(pluginVersion);
String spV = toReadable(spigotVersion); String spV = toReadable(spigotVersion);
@ -97,21 +94,17 @@ public class UpdateChecker implements Listener {
} }
private String toReadable(String version) { private String toReadable(String version) {
if (version.contains("-DEV-")) { if (version.contains("-DEV-")) version = StringUtils.split(version, "-DEV-")[0];
version = version.split("-DEV-")[0]; return StringUtils.remove(version, '.');
}
return version.replaceAll("\\.", "");
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler
public void onJoin(PlayerJoinEvent e) { public void onJoin(PlayerJoinEvent event) {
if (e.getPlayer().hasPermission("placeholderapi.updatenotify")) { Player player = event.getPlayer();
Msg.msg(e.getPlayer(), if (player.hasPermission("placeholderapi.updatenotify")) {
"&bAn update for &fPlaceholder&7API &e(&fPlaceholder&7API &fv" + getSpigotVersion() Msg.msg(player,
+ "&e)" "&bAn update for &fPlaceholder&7API &e(&fPlaceholder&7API &fv" + getSpigotVersion() + "&e)",
, "&bis available at &ehttps://www.spigotmc.org/resources/placeholderapi." + RESOURCE_ID "&bis available at &ehttps://www.spigotmc.org/resources/placeholderapi." + RESOURCE_ID + '/');
+ "/");
} }
} }
} }

@ -4,39 +4,51 @@ version: ${project.version}
api-version: '1.13' api-version: '1.13'
authors: [extended_clip, Glare] authors: [extended_clip, Glare]
description: ${project.description} description: ${project.description}
permissions:
placeholderapi.*:
description: ability to use all commands
children:
placeholderapi.admin: true
placeholderapi.admin:
description: ability to use all commands
children:
placeholderapi.list: true
placeholderapi.reload: true
placeholderapi.ecloud: true
placeholderapi.parse: true
placeholderapi.register: true
placeholderapi.updatenotify: true
placeholderapi.list:
description: ability to use the list command
default: op
placeholderapi.reload:
description: ability to use the reload command
default: op
placeholderapi.parse:
description: ability to use parse command
default: op
placeholderapi.register:
description: ability to register or unregister placeholder expansions
default: op
placeholderapi.ecloud:
description: allows the usage of ecloud commands
default: op
placeholderapi.updatenotify:
description: notifies you when there is a PAPI update
default: op
commands: commands:
placeholderapi: placeholderapi:
description: PlaceholderAPI command description: PlaceholderAPI command
aliases: [papi] aliases: [papi]
permissions:
placeholderapi.*:
description: ability to use all commands
children:
placeholderapi.admin: true
placeholderapi.admin:
description: ability to use all commands
children:
placeholderapi.list: true
placeholderapi.reload: true
placeholderapi.ecloud: true
placeholderapi.parse: true
placeholderapi.register: true
placeholderapi.updatenotify: true
placeholderapi.list:
description: ability to use the list command
default: op
placeholderapi.reload:
description: ability to use the reload command
default: op
placeholderapi.parse:
description: ability to use parse command
default: op
placeholderapi.register:
description: ability to register or unregister placeholder expansions
default: op
placeholderapi.ecloud:
description: allows the usage of ecloud commands
default: op
children:
placeholderapi.ecloud.enable: true
placeholderapi.ecloud.disable: true
placeholderapi.ecloud.list: true
placeholderapi.ecloud.info: true
placeholderapi.ecloud.clear: true
placeholderapi.ecloud.status: true
placeholderapi.ecloud.refresh: true
placeholderapi.ecloud.download: true
placeholderapi.ecloud.versioninfo: true
placeholderapi.updatenotify:
description: notifies you when there is a PAPI update
default: op