we're really getting somewhere!@1

who the fuck decided to use 2 space instead of 4
This commit is contained in:
PiggyPiglet
2025-11-17 14:11:21 +08:00
parent 2a8bb3695c
commit 968df21ef0
46 changed files with 3711 additions and 3616 deletions

View File

@@ -28,6 +28,7 @@ dependencies {
implementation("net.kyori:adventure-platform-bukkit:4.4.1") implementation("net.kyori:adventure-platform-bukkit:4.4.1")
//compileOnly("org.spigotmc:spigot-api:1.21-R0.1-SNAPSHOT") //compileOnly("org.spigotmc:spigot-api:1.21-R0.1-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT") compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT")
compileOnlyApi("org.jetbrains:annotations:23.0.0") compileOnlyApi("org.jetbrains:annotations:23.0.0")
@@ -92,7 +93,12 @@ tasks {
archiveClassifier.set("") archiveClassifier.set("")
relocate("org.bstats", "me.clip.placeholderapi.metrics") relocate("org.bstats", "me.clip.placeholderapi.metrics")
relocate("net.kyori", "me.clip.placeholderapi.libs.kyori") // relocate("net.kyori", "me.clip.placeholderapi.libs.kyori") {
// exclude("me/clip/placeholderapi/PAPIComponents.java")
// exclude("me/clip/placeholderapi/commands/TestCommand.java")
// }
destinationDirectory = file("server/1.21/plugins/")
exclude("META-INF/versions/**") exclude("META-INF/versions/**")
} }

View File

@@ -0,0 +1,121 @@
package me.clip.placeholderapi;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.expansion.Relational;
import me.clip.placeholderapi.replacer.ExactReplacer;
import me.clip.placeholderapi.replacer.Replacer;
import net.kyori.adventure.text.Component;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.checkerframework.checker.units.qual.N;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public final class PAPIComponents {
private static final Replacer EXACT_REPLACER = new ExactReplacer();
@NotNull
public static Component setPlaceholders(final OfflinePlayer player, @NotNull final Component component) {
// TODO: explore a custom TextReplacementRenderer which doesn't use regex for performance benefits i.e. merge CharsReplacer with kyori TextReplacementRenderer
return component.replaceText(config -> config.match(PlaceholderAPI.PLACEHOLDER_PATTERN).replacement((result, builder) ->
builder.content(EXACT_REPLACER.apply(result.group(), player, PlaceholderAPIPlugin.getInstance().getLocalExpansionManager()::getExpansion))));
}
@NotNull
public static List<Component> setPlaceholders(final OfflinePlayer player, @NotNull final List<Component> components) {
return components.stream().map(component -> setPlaceholders(player, component)).collect(Collectors.toList());
}
@NotNull
public static Component setPlaceholders(final Player player, @NotNull final Component component) {
return setPlaceholders((OfflinePlayer) player, component);
}
@NotNull
public static List<Component> setPlaceholders(final Player player, @NotNull final List<Component> components) {
return setPlaceholders((OfflinePlayer) player, components);
}
@NotNull
public static Component setBracketPlaceholders(final OfflinePlayer player, @NotNull final Component component) {
return component.replaceText(config -> config.match(PlaceholderAPI.BRACKET_PLACEHOLDER_PATTERN).replacement((result, builder) ->
builder.content(EXACT_REPLACER.apply(result.group(), player, PlaceholderAPIPlugin.getInstance().getLocalExpansionManager()::getExpansion))));
}
@NotNull
public static List<Component> setBracketPlaceholders(final OfflinePlayer player, @NotNull final List<Component> components) {
return components.stream().map(component -> setBracketPlaceholders(player, component)).collect(Collectors.toList());
}
@NotNull
public static Component setBracketPlaceholders(final Player player, @NotNull final Component component) {
return setBracketPlaceholders((OfflinePlayer) player, component);
}
@NotNull
public static List<Component> setBracketPlaceholders(final Player player, @NotNull final List<Component> components) {
return setBracketPlaceholders((OfflinePlayer) player, components);
}
// public static Component setRelationalPlaceholders(Player one, Player two, Component component) {
// return component.replaceText(config -> config.match(PlaceholderAPI.RELATIONAL_PLACEHOLDER_PATTERN).replacement((result, builder) -> {
//
// final String format = result.group(2);
// final int index = format.indexOf("_");
//
// if (index <= 0 || index >= format.length()) {
// continue;
// }
//
// String identifier = format.substring(0, index).toLowerCase(Locale.ROOT);
// String params = format.substring(index + 1);
// final PlaceholderExpansion expansion = PlaceholderAPIPlugin.getInstance()
// .getLocalExpansionManager().getExpansion(identifier);
//
// if (!(expansion instanceof Relational)) {
// continue;
// }
//
// final String value = ((Relational) expansion).onPlaceholderRequest(one, two, params);
//
// if (value != null) {
// text = text.replaceAll(Pattern.quote(matcher.group()), Matcher.quoteReplacement(value));
// }
//
//
// }));
//
// final Matcher matcher = PlaceholderAPI.RELATIONAL_PLACEHOLDER_PATTERN.matcher(text);
//
// while (matcher.find()) {
// final String format = matcher.group(2);
// final int index = format.indexOf("_");
//
// if (index <= 0 || index >= format.length()) {
// continue;
// }
//
// String identifier = format.substring(0, index).toLowerCase(Locale.ROOT);
// String params = format.substring(index + 1);
// final PlaceholderExpansion expansion = PlaceholderAPIPlugin.getInstance()
// .getLocalExpansionManager().getExpansion(identifier);
//
// if (!(expansion instanceof Relational)) {
// continue;
// }
//
// final String value = ((Relational) expansion).onPlaceholderRequest(one, two, params);
//
// if (value != null) {
// text = text.replaceAll(Pattern.quote(matcher.group()), Matcher.quoteReplacement(value));
// }
// }
//
// return text;
// }
}

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,7 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import me.clip.placeholderapi.commands.PlaceholderCommandRouter; import me.clip.placeholderapi.commands.PlaceholderCommandRouter;
import me.clip.placeholderapi.commands.TestCommand;
import me.clip.placeholderapi.configuration.PlaceholderAPIConfig; import me.clip.placeholderapi.configuration.PlaceholderAPIConfig;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.expansion.Version; import me.clip.placeholderapi.expansion.Version;
@@ -53,228 +54,229 @@ import org.jetbrains.annotations.NotNull;
*/ */
public final class PlaceholderAPIPlugin extends JavaPlugin { public final class PlaceholderAPIPlugin extends JavaPlugin {
@NotNull @NotNull
private static final Version VERSION; private static final Version VERSION;
private static PlaceholderAPIPlugin instance; private static PlaceholderAPIPlugin instance;
static { static {
String version = Bukkit.getServer().getBukkitVersion().split("-")[0]; String version = Bukkit.getServer().getBukkitVersion().split("-")[0];
String suffix; String suffix;
if (version.chars() if (version.chars()
.filter(c -> c == '.') .filter(c -> c == '.')
.count() == 1) { .count() == 1) {
suffix = "R1"; suffix = "R1";
version = 'v' + version.replace('.', '_') + '_' + suffix; version = 'v' + version.replace('.', '_') + '_' + suffix;
} else { } else {
int minor = Integer.parseInt(version.split("\\.")[2].charAt(0) + ""); int minor = Integer.parseInt(version.split("\\.")[2].charAt(0) + "");
version = 'v' + version.replace('.', '_').replace("_" + minor, "") + '_' + "R" + (minor - 1); version = 'v' + version.replace('.', '_').replace("_" + minor, "") + '_' + "R" + (minor - 1);
}
boolean isSpigot;
try {
Class.forName("org.spigotmc.SpigotConfig");
isSpigot = true;
} catch (final ExceptionInInitializerError | ClassNotFoundException ignored) {
isSpigot = false;
}
VERSION = new Version(version, isSpigot);
} }
boolean isSpigot; @NotNull
try { private final PlaceholderAPIConfig config = new PlaceholderAPIConfig(this);
Class.forName("org.spigotmc.SpigotConfig");
isSpigot = true; @NotNull
} catch (final ExceptionInInitializerError | ClassNotFoundException ignored) { private final LocalExpansionManager localExpansionManager = new LocalExpansionManager(this);
isSpigot = false; @NotNull
private final CloudExpansionManager cloudExpansionManager = new CloudExpansionManager(this);
@NotNull
private final TaskScheduler scheduler = UniversalScheduler.getScheduler(this);
private BukkitAudiences adventure;
/**
* 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
* available from the class: {@link PlaceholderAPI}
*
* @return PlaceholderAPIPlugin instance
*/
@NotNull
public static PlaceholderAPIPlugin getInstance() {
return instance;
} }
VERSION = new Version(version, isSpigot); /**
} * Get the configurable {@linkplain String} value that should be returned when a boolean is true
*
@NotNull * @return string value of true
private final PlaceholderAPIConfig config = new PlaceholderAPIConfig(this); */
@NotNull
@NotNull public static String booleanTrue() {
private final LocalExpansionManager localExpansionManager = new LocalExpansionManager(this); return getInstance().getPlaceholderAPIConfig().booleanTrue();
@NotNull
private final CloudExpansionManager cloudExpansionManager = new CloudExpansionManager(this);
@NotNull
private final TaskScheduler scheduler = UniversalScheduler.getScheduler(this);
private BukkitAudiences adventure;
/**
* 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
* available from the class: {@link PlaceholderAPI}
*
* @return PlaceholderAPIPlugin instance
*/
@NotNull
public static PlaceholderAPIPlugin getInstance() {
return instance;
}
/**
* Get the configurable {@linkplain String} value that should be returned when a boolean is true
*
* @return string value of true
*/
@NotNull
public static String booleanTrue() {
return getInstance().getPlaceholderAPIConfig().booleanTrue();
}
/**
* Get the configurable {@linkplain String} value that should be returned when a boolean is false
*
* @return string value of false
*/
@NotNull
public static String booleanFalse() {
return getInstance().getPlaceholderAPIConfig().booleanFalse();
}
/**
* Get the configurable {@linkplain SimpleDateFormat} object that is used to parse time for
* generic time based placeholders
*
* @return date format
*/
@NotNull
public static SimpleDateFormat getDateFormat() {
try {
return new SimpleDateFormat(getInstance().getPlaceholderAPIConfig().dateFormat());
} catch (final IllegalArgumentException ex) {
Msg.warn("Configured date format ('%s') is invalid! Defaulting to 'MM/dd/yy HH:mm:ss'",
ex, getInstance().getPlaceholderAPIConfig().dateFormat());
return new SimpleDateFormat("MM/dd/yy HH:mm:ss");
}
}
@Deprecated
public static Version getServerVersion() {
return VERSION;
}
@Override
public void onLoad() {
instance = this;
saveDefaultConfig();
}
@Override
public void onEnable() {
setupCommand();
setupMetrics();
setupExpansions();
adventure = BukkitAudiences.create(this);
if (config.isCloudEnabled()) {
getCloudExpansionManager().load();
} }
if (config.checkUpdates()) { /**
new UpdateChecker(this).fetch(); * Get the configurable {@linkplain String} value that should be returned when a boolean is false
} *
} * @return string value of false
*/
@Override @NotNull
public void onDisable() { public static String booleanFalse() {
getCloudExpansionManager().kill(); return getInstance().getPlaceholderAPIConfig().booleanFalse();
getLocalExpansionManager().kill();
HandlerList.unregisterAll(this);
scheduler.cancelTasks(this);
adventure.close();
adventure = null;
instance = null;
}
public void reloadConf(@NotNull final CommandSender sender) {
getLocalExpansionManager().kill();
reloadConfig();
getLocalExpansionManager().load(sender);
if (config.isCloudEnabled()) {
getCloudExpansionManager().load();
} else {
getCloudExpansionManager().kill();
}
}
@NotNull
public LocalExpansionManager getLocalExpansionManager() {
return localExpansionManager;
}
@NotNull
public CloudExpansionManager getCloudExpansionManager() {
return cloudExpansionManager;
}
@NotNull
public BukkitAudiences getAdventure() {
if(adventure == null) {
throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!");
} }
return adventure; /**
} * Get the configurable {@linkplain SimpleDateFormat} object that is used to parse time for
* generic time based placeholders
@NotNull *
public TaskScheduler getScheduler() { * @return date format
return scheduler; */
} @NotNull
public static SimpleDateFormat getDateFormat() {
/** try {
* Obtain the configuration class for PlaceholderAPI. return new SimpleDateFormat(getInstance().getPlaceholderAPIConfig().dateFormat());
* } catch (final IllegalArgumentException ex) {
* @return PlaceholderAPIConfig instance Msg.warn("Configured date format ('%s') is invalid! Defaulting to 'MM/dd/yy HH:mm:ss'",
*/ ex, getInstance().getPlaceholderAPIConfig().dateFormat());
@NotNull return new SimpleDateFormat("MM/dd/yy HH:mm:ss");
public PlaceholderAPIConfig getPlaceholderAPIConfig() { }
return config;
}
private void setupCommand() {
final PluginCommand pluginCommand = getCommand("placeholderapi");
if (pluginCommand == null) {
return;
} }
final PlaceholderCommandRouter router = new PlaceholderCommandRouter(this); @Deprecated
pluginCommand.setExecutor(router); public static Version getServerVersion() {
pluginCommand.setTabCompleter(router); return VERSION;
} }
private void setupMetrics() { @Override
final Metrics metrics = new Metrics(this, 438); public void onLoad() {
metrics.addCustomChart(new SimplePie("using_expansion_cloud", instance = this;
() -> getPlaceholderAPIConfig().isCloudEnabled() ? "yes" : "no"));
saveDefaultConfig();
metrics.addCustomChart(new SimplePie("using_spigot", () -> getServerVersion().isSpigot() ? "yes" : "no")); }
metrics.addCustomChart(new AdvancedPie("expansions_used", () -> { @Override
final Map<String, Integer> values = new HashMap<>(); public void onEnable() {
registerCommand("test", new TestCommand());
for (final PlaceholderExpansion expansion : getLocalExpansionManager().getExpansions()) { setupCommand();
values.put(expansion.getRequiredPlugin() == null ? expansion.getIdentifier() setupMetrics();
: expansion.getRequiredPlugin(), 1); setupExpansions();
}
adventure = BukkitAudiences.create(this);
return values;
})); if (config.isCloudEnabled()) {
} getCloudExpansionManager().load();
}
private void setupExpansions() {
Bukkit.getPluginManager().registerEvents(getLocalExpansionManager(), this); if (config.checkUpdates()) {
new UpdateChecker(this).fetch();
try { }
Class.forName("org.bukkit.event.server.ServerLoadEvent"); }
new ServerLoadEventListener(this);
} catch (final ClassNotFoundException ignored) { @Override
scheduler public void onDisable() {
.runTaskLater(() -> getLocalExpansionManager().load(Bukkit.getConsoleSender()), 1); getCloudExpansionManager().kill();
getLocalExpansionManager().kill();
HandlerList.unregisterAll(this);
scheduler.cancelTasks(this);
adventure.close();
adventure = null;
instance = null;
}
public void reloadConf(@NotNull final CommandSender sender) {
getLocalExpansionManager().kill();
reloadConfig();
getLocalExpansionManager().load(sender);
if (config.isCloudEnabled()) {
getCloudExpansionManager().load();
} else {
getCloudExpansionManager().kill();
}
}
@NotNull
public LocalExpansionManager getLocalExpansionManager() {
return localExpansionManager;
}
@NotNull
public CloudExpansionManager getCloudExpansionManager() {
return cloudExpansionManager;
}
@NotNull
public BukkitAudiences getAdventure() {
if(adventure == null) {
throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!");
}
return adventure;
}
@NotNull
public TaskScheduler getScheduler() {
return scheduler;
}
/**
* Obtain the configuration class for PlaceholderAPI.
*
* @return PlaceholderAPIConfig instance
*/
@NotNull
public PlaceholderAPIConfig getPlaceholderAPIConfig() {
return config;
}
private void setupCommand() {
final PluginCommand pluginCommand = getCommand("placeholderapi");
if (pluginCommand == null) {
return;
}
final PlaceholderCommandRouter router = new PlaceholderCommandRouter(this);
pluginCommand.setExecutor(router);
pluginCommand.setTabCompleter(router);
}
private void setupMetrics() {
final Metrics metrics = new Metrics(this, 438);
metrics.addCustomChart(new SimplePie("using_expansion_cloud",
() -> getPlaceholderAPIConfig().isCloudEnabled() ? "yes" : "no"));
metrics.addCustomChart(new SimplePie("using_spigot", () -> getServerVersion().isSpigot() ? "yes" : "no"));
metrics.addCustomChart(new AdvancedPie("expansions_used", () -> {
final Map<String, Integer> values = new HashMap<>();
for (final PlaceholderExpansion expansion : getLocalExpansionManager().getExpansions()) {
values.put(expansion.getRequiredPlugin() == null ? expansion.getIdentifier()
: expansion.getRequiredPlugin(), 1);
}
return values;
}));
}
private void setupExpansions() {
Bukkit.getPluginManager().registerEvents(getLocalExpansionManager(), this);
try {
Class.forName("org.bukkit.event.server.ServerLoadEvent");
new ServerLoadEventListener(this);
} catch (final ClassNotFoundException ignored) {
scheduler
.runTaskLater(() -> getLocalExpansionManager().load(Bukkit.getConsoleSender()), 1);
}
} }
}
} }

View File

@@ -29,7 +29,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.Optional; import java.util.Optional;
public abstract class PlaceholderHook { public abstract class PlaceholderHook {
@Deprecated
@Nullable @Nullable
public String onRequest(final OfflinePlayer player, @NotNull final String params) { public String onRequest(final OfflinePlayer player, @NotNull final String params) {
if (player != null && player.isOnline()) { if (player != null && player.isOnline()) {
@@ -39,16 +38,8 @@ public abstract class PlaceholderHook {
return onPlaceholderRequest(null, params); return onPlaceholderRequest(null, params);
} }
@Deprecated
@Nullable @Nullable
public String onPlaceholderRequest(final Player player, @NotNull final String params) { public String onPlaceholderRequest(final Player player, @NotNull final String params) {
return null; return null;
} }
@Nullable
public Component onPlaceholderComponentRequest(final OfflinePlayer player, @NotNull final String params) {
final String result = onRequest(player, params);
return result == null ? null : Component.text(result);
}
} }

View File

@@ -22,10 +22,12 @@ package me.clip.placeholderapi.commands;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -34,75 +36,75 @@ import org.jetbrains.annotations.Unmodifiable;
public abstract class PlaceholderCommand { public abstract class PlaceholderCommand {
@NotNull @NotNull
private final String label; private final String label;
@NotNull @NotNull
private final Set<String> alias; private final Set<String> alias;
@Nullable @Nullable
private String permission; private String permission;
protected PlaceholderCommand(@NotNull final String label, @NotNull final String... alias) { protected PlaceholderCommand(@NotNull final String label, @NotNull final String... alias) {
this.label = label; this.label = label;
this.alias = Sets.newHashSet(alias); this.alias = Sets.newHashSet(alias);
setPermission("placeholderapi." + label); setPermission("placeholderapi." + label);
}
@NotNull
public static Stream<PlaceholderCommand> filterByPermission(@NotNull final CommandSender sender,
@NotNull final Stream<PlaceholderCommand> commands) {
return commands.filter(
target -> target.getPermission() == null || sender.hasPermission(target.getPermission()));
}
public static void suggestByParameter(@NotNull final Stream<String> possible,
@NotNull final List<String> suggestions, @Nullable final String parameter) {
if (parameter == null) {
possible.forEach(suggestions::add);
} else {
possible.filter(suggestion -> suggestion.toLowerCase(Locale.ROOT).startsWith(parameter.toLowerCase(Locale.ROOT)))
.forEach(suggestions::add);
} }
}
@NotNull @NotNull
public final String getLabel() { public static Stream<PlaceholderCommand> filterByPermission(@NotNull final CommandSender sender,
return label; @NotNull final Stream<PlaceholderCommand> commands) {
} return commands.filter(
target -> target.getPermission() == null || sender.hasPermission(target.getPermission()));
}
@NotNull public static void suggestByParameter(@NotNull final Stream<String> possible,
@Unmodifiable @NotNull final List<String> suggestions, @Nullable final String parameter) {
public final Set<String> getAlias() { if (parameter == null) {
return ImmutableSet.copyOf(alias); possible.forEach(suggestions::add);
} } else {
possible.filter(suggestion -> suggestion.toLowerCase(Locale.ROOT).startsWith(parameter.toLowerCase(Locale.ROOT)))
.forEach(suggestions::add);
}
}
@NotNull @NotNull
@Unmodifiable public final String getLabel() {
public final Set<String> getLabels() { return label;
return ImmutableSet.<String>builder().add(label).addAll(alias).build(); }
}
@Nullable @NotNull
public final String getPermission() { @Unmodifiable
return permission; public final Set<String> getAlias() {
} return ImmutableSet.copyOf(alias);
}
public void setPermission(@NotNull final String permission) { @NotNull
this.permission = permission; @Unmodifiable
} public final Set<String> getLabels() {
return ImmutableSet.<String>builder().add(label).addAll(alias).build();
}
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, @Nullable
@NotNull final CommandSender sender, @NotNull final String alias, public final String getPermission() {
@NotNull @Unmodifiable final List<String> params) { return permission;
}
} public void setPermission(@NotNull final String permission) {
this.permission = permission;
}
public void complete(@NotNull final PlaceholderAPIPlugin plugin, public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias, @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) { @NotNull @Unmodifiable final List<String> params) {
} }
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
}
} }

View File

@@ -22,6 +22,7 @@ package me.clip.placeholderapi.commands;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@@ -30,6 +31,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.impl.cloud.CommandECloud; import me.clip.placeholderapi.commands.impl.cloud.CommandECloud;
import me.clip.placeholderapi.commands.impl.local.CommandDump; import me.clip.placeholderapi.commands.impl.local.CommandDump;
@@ -51,93 +53,93 @@ import org.jetbrains.annotations.Unmodifiable;
public final class PlaceholderCommandRouter implements CommandExecutor, TabCompleter { public final class PlaceholderCommandRouter implements CommandExecutor, TabCompleter {
@Unmodifiable @Unmodifiable
private static final List<PlaceholderCommand> COMMANDS = ImmutableList.of(new CommandHelp(), private static final List<PlaceholderCommand> COMMANDS = ImmutableList.of(new CommandHelp(),
new CommandInfo(), new CommandInfo(),
new CommandList(), new CommandList(),
new CommandDump(), new CommandDump(),
new CommandECloud(), new CommandECloud(),
new CommandParse(), new CommandParse(),
new CommandReload(), new CommandReload(),
new CommandVersion(), new CommandVersion(),
new CommandExpansionRegister(), new CommandExpansionRegister(),
new CommandExpansionUnregister()); new CommandExpansionUnregister());
@NotNull @NotNull
private final PlaceholderAPIPlugin plugin; private final PlaceholderAPIPlugin plugin;
@NotNull @NotNull
@Unmodifiable @Unmodifiable
private final Map<String, PlaceholderCommand> commands; private final Map<String, PlaceholderCommand> commands;
public PlaceholderCommandRouter(@NotNull final PlaceholderAPIPlugin plugin) { public PlaceholderCommandRouter(@NotNull final PlaceholderAPIPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
final ImmutableMap.Builder<String, PlaceholderCommand> commands = ImmutableMap.builder(); final ImmutableMap.Builder<String, PlaceholderCommand> commands = ImmutableMap.builder();
for (final PlaceholderCommand command : COMMANDS) { for (final PlaceholderCommand command : COMMANDS) {
command.getLabels().forEach(label -> commands.put(label, command)); command.getLabels().forEach(label -> commands.put(label, command));
}
this.commands = commands.build();
} }
this.commands = commands.build();
}
@Override
public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command,
@NotNull final String alias, @NotNull final String[] args) {
if (args.length == 0) {
final PlaceholderCommand fallback = commands.get("version");
if (fallback != null) {
fallback.evaluate(plugin, sender, "", Collections.emptyList());
}
@Override return true;
public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command, }
@NotNull final String alias, @NotNull final String[] args) {
if (args.length == 0) {
final PlaceholderCommand fallback = commands.get("version");
if (fallback != null) {
fallback.evaluate(plugin, sender, "", Collections.emptyList());
}
return true; final String search = args[0].toLowerCase(Locale.ROOT);
final PlaceholderCommand target = commands.get(search);
if (target == null) {
Msg.msg(sender, "&cUnknown command &7" + search);
return true;
}
final String permission = target.getPermission();
if (permission != null && !permission.isEmpty() && !sender.hasPermission(permission)) {
Msg.msg(sender, "&cYou do not have permission to do this!");
return true;
}
target
.evaluate(plugin, sender, search, Arrays.asList(Arrays.copyOfRange(args, 1, args.length)));
return true;
} }
final String search = args[0].toLowerCase(Locale.ROOT); @Override
final PlaceholderCommand target = commands.get(search); public List<String> onTabComplete(@NotNull final CommandSender sender,
@NotNull final Command command, @NotNull final String alias, @NotNull final String[] args) {
final List<String> suggestions = new ArrayList<>();
if (target == null) { if (args.length > 1) {
Msg.msg(sender, "&cUnknown command &7" + search); final PlaceholderCommand target = this.commands.get(args[0].toLowerCase(Locale.ROOT));
return true;
if (target != null) {
target.complete(plugin, sender, args[0].toLowerCase(Locale.ROOT),
Arrays.asList(Arrays.copyOfRange(args, 1, args.length)), suggestions);
}
return suggestions;
}
final Stream<String> targets = PlaceholderCommand
.filterByPermission(sender, commands.values().stream()).map(PlaceholderCommand::getLabels)
.flatMap(Collection::stream);
PlaceholderCommand.suggestByParameter(targets, suggestions, args.length == 0 ? null : args[0]);
return suggestions;
} }
final String permission = target.getPermission();
if (permission != null && !permission.isEmpty() && !sender.hasPermission(permission)) {
Msg.msg(sender, "&cYou do not have permission to do this!");
return true;
}
target
.evaluate(plugin, sender, search, Arrays.asList(Arrays.copyOfRange(args, 1, args.length)));
return true;
}
@Override
public List<String> onTabComplete(@NotNull final CommandSender sender,
@NotNull final Command command, @NotNull final String alias, @NotNull final String[] args) {
final List<String> suggestions = new ArrayList<>();
if (args.length > 1) {
final PlaceholderCommand target = this.commands.get(args[0].toLowerCase(Locale.ROOT));
if (target != null) {
target.complete(plugin, sender, args[0].toLowerCase(Locale.ROOT),
Arrays.asList(Arrays.copyOfRange(args, 1, args.length)), suggestions);
}
return suggestions;
}
final Stream<String> targets = PlaceholderCommand
.filterByPermission(sender, commands.values().stream()).map(PlaceholderCommand::getLabels)
.flatMap(Collection::stream);
PlaceholderCommand.suggestByParameter(targets, suggestions, args.length == 0 ? null : args[0]);
return suggestions;
}
} }

View File

@@ -0,0 +1,51 @@
package me.clip.placeholderapi.commands;
import io.papermc.paper.command.brigadier.BasicCommand;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import me.clip.placeholderapi.PAPIComponents;
import me.clip.placeholderapi.PlaceholderAPI;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEventSource;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.OfflinePlayer;
public class TestCommand implements BasicCommand {
private static final MiniMessage MINI = MiniMessage.miniMessage();
@Override
public void execute(final CommandSourceStack commandSourceStack, final String[] strings) {
// final Component component = Component.text("Woo! Test: %player_name%").color(TextColor.color(50, 168, 82)).hoverEvent(HoverEvent.showText(Component.text("OMG %player_gamemode%")));
final Component component = Component.text("Woo! Test: %player_name%");
String ser = MINI.serialize(component);
System.out.println(ser);
commandSourceStack.getSender().sendMessage(
PAPIComponents.setPlaceholders((OfflinePlayer) commandSourceStack.getSender(), component)
);
long tmp = System.currentTimeMillis();
for (int i = 0; i < 100000; ++i) {
PAPIComponents.setPlaceholders((OfflinePlayer) commandSourceStack.getSender(), component);
}
commandSourceStack.getSender().sendMessage(String.valueOf(System.currentTimeMillis() - tmp));
tmp = System.currentTimeMillis();
for (int i = 0; i < 100000; ++i) {
PlaceholderAPI.setPlaceholders((OfflinePlayer) commandSourceStack.getSender(), "Woo! Test: %player_name%");
}
commandSourceStack.getSender().sendMessage(String.valueOf(System.currentTimeMillis() - tmp));
tmp = System.currentTimeMillis();
for (int i = 0; i < 100000; ++i) {
final String serr = MINI.serialize(component);
final String repl = PlaceholderAPI.setPlaceholders((OfflinePlayer) commandSourceStack.getSender(), serr);
MINI.deserialize(repl);
}
commandSourceStack.getSender().sendMessage(String.valueOf(System.currentTimeMillis() - tmp));
}
}

View File

@@ -37,110 +37,110 @@ import java.util.stream.Stream;
public final class CommandECloud extends PlaceholderCommand { public final class CommandECloud extends PlaceholderCommand {
@Unmodifiable @Unmodifiable
private static final List<PlaceholderCommand> COMMANDS = ImmutableList private static final List<PlaceholderCommand> COMMANDS = ImmutableList
.of(new CommandECloudClear(), .of(new CommandECloudClear(),
new CommandECloudStatus(), new CommandECloudStatus(),
new CommandECloudUpdate(), new CommandECloudUpdate(),
new CommandECloudRefresh(), new CommandECloudRefresh(),
new CommandECloudDownload(), new CommandECloudDownload(),
new CommandECloudExpansionInfo(), new CommandECloudExpansionInfo(),
new CommandECloudExpansionList(), new CommandECloudExpansionList(),
new CommandECloudExpansionPlaceholders()); new CommandECloudExpansionPlaceholders());
static { static {
COMMANDS COMMANDS
.forEach(command -> command.setPermission("placeholderapi.ecloud." + command.getLabel())); .forEach(command -> command.setPermission("placeholderapi.ecloud." + command.getLabel()));
}
@NotNull
@Unmodifiable
private final Map<String, PlaceholderCommand> commands;
public CommandECloud() {
super("ecloud");
final ImmutableMap.Builder<String, PlaceholderCommand> commands = ImmutableMap.builder();
for (final PlaceholderCommand command : COMMANDS) {
command.getLabels().forEach(label -> commands.put(label, command));
} }
this.commands = commands.build(); @NotNull
} @Unmodifiable
private final Map<String, PlaceholderCommand> commands;
@Override public CommandECloud() {
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, super("ecloud");
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&b&lPlaceholderAPI &8- &7eCloud Help Menu &8- ",
" ",
"&b/papi &fecloud status",
" &7&oView status of the eCloud",
"&b/papi &fecloud list <all/{author}/installed> {page}",
" &7&oList all/author specific available expansions",
"&b/papi &fecloud info <expansion name> {version}",
" &7&oView information about a specific expansion available on the eCloud",
"&b/papi &fecloud placeholders <expansion name>",
" &7&oView placeholders for an expansion",
"&b/papi &fecloud download <expansion name> {version}",
" &7&oDownload an expansion from the eCloud",
"&b/papi &fecloud update <expansion name/all>",
" &7&oUpdate a specific/all installed expansions",
"&b/papi &fecloud refresh",
" &7&oFetch the most up to date list of expansions available.",
"&b/papi &fecloud clear",
" &7&oClear the expansion cloud cache.");
return; final ImmutableMap.Builder<String, PlaceholderCommand> commands = ImmutableMap.builder();
for (final PlaceholderCommand command : COMMANDS) {
command.getLabels().forEach(label -> commands.put(label, command));
}
this.commands = commands.build();
} }
final String search = params.get(0).toLowerCase(Locale.ROOT);
final PlaceholderCommand target = commands.get(search);
if (target == null) { @Override
Msg.msg(sender, "&cUnknown command &7ecloud " + search); public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
return; @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&b&lPlaceholderAPI &8- &7eCloud Help Menu &8- ",
" ",
"&b/papi &fecloud status",
" &7&oView status of the eCloud",
"&b/papi &fecloud list <all/{author}/installed> {page}",
" &7&oList all/author specific available expansions",
"&b/papi &fecloud info <expansion name> {version}",
" &7&oView information about a specific expansion available on the eCloud",
"&b/papi &fecloud placeholders <expansion name>",
" &7&oView placeholders for an expansion",
"&b/papi &fecloud download <expansion name> {version}",
" &7&oDownload an expansion from the eCloud",
"&b/papi &fecloud update <expansion name/all>",
" &7&oUpdate a specific/all installed expansions",
"&b/papi &fecloud refresh",
" &7&oFetch the most up to date list of expansions available.",
"&b/papi &fecloud clear",
" &7&oClear the expansion cloud cache.");
return;
}
final String search = params.get(0).toLowerCase(Locale.ROOT);
final PlaceholderCommand target = commands.get(search);
if (target == null) {
Msg.msg(sender, "&cUnknown command &7ecloud " + search);
return;
}
final String permission = target.getPermission();
if (permission != null && !permission.isEmpty() && !sender.hasPermission(permission)) {
Msg.msg(sender, "&cYou do not have permission to do this!");
return;
}
if (!plugin.getPlaceholderAPIConfig().isCloudEnabled()) {
Msg.msg(sender, "&cThe eCloud Manager is not enabled! To enable it, set 'cloud_enabled' to true and reload the plugin.");
return;
}
target.evaluate(plugin, sender, search, params.subList(1, params.size()));
} }
final String permission = target.getPermission(); @Override
if (permission != null && !permission.isEmpty() && !sender.hasPermission(permission)) { public void complete(@NotNull final PlaceholderAPIPlugin plugin,
Msg.msg(sender, "&cYou do not have permission to do this!"); @NotNull final CommandSender sender, @NotNull final String alias,
return; @NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() <= 1) {
final Stream<String> targets = filterByPermission(sender, commands.values().stream())
.map(PlaceholderCommand::getLabels).flatMap(Collection::stream);
suggestByParameter(targets, suggestions, params.isEmpty() ? null : params.get(0));
return; // send sub commands
}
final String search = params.get(0).toLowerCase(Locale.ROOT);
final PlaceholderCommand target = commands.get(search);
if (target == null) {
return;
}
target.complete(plugin, sender, search, params.subList(1, params.size()), suggestions);
} }
if (!plugin.getPlaceholderAPIConfig().isCloudEnabled()) {
Msg.msg(sender, "&cThe eCloud Manager is not enabled! To enable it, set 'cloud_enabled' to true and reload the plugin.");
return;
}
target.evaluate(plugin, sender, search, params.subList(1, params.size()));
}
@Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() <= 1) {
final Stream<String> targets = filterByPermission(sender, commands.values().stream())
.map(PlaceholderCommand::getLabels).flatMap(Collection::stream);
suggestByParameter(targets, suggestions, params.isEmpty() ? null : params.get(0));
return; // send sub commands
}
final String search = params.get(0).toLowerCase(Locale.ROOT);
final PlaceholderCommand target = commands.get(search);
if (target == null) {
return;
}
target.complete(plugin, sender, search, params.subList(1, params.size()), suggestions);
}
} }

View File

@@ -21,6 +21,7 @@
package me.clip.placeholderapi.commands.impl.cloud; package me.clip.placeholderapi.commands.impl.cloud;
import java.util.List; import java.util.List;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
@@ -30,17 +31,17 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandECloudClear extends PlaceholderCommand { public final class CommandECloudClear extends PlaceholderCommand {
public CommandECloudClear() { public CommandECloudClear() {
super("clear"); super("clear");
} }
@Override @Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias, @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) { @NotNull @Unmodifiable final List<String> params) {
plugin.getCloudExpansionManager().clean(); plugin.getCloudExpansionManager().clean();
Msg.msg(sender, Msg.msg(sender,
"&aThe eCloud cache has been cleared!"); "&aThe eCloud cache has been cleared!");
} }
} }

View File

@@ -24,6 +24,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.expansion.cloud.CloudExpansion; import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
@@ -34,106 +35,106 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandECloudDownload extends PlaceholderCommand { public final class CommandECloudDownload extends PlaceholderCommand {
public CommandECloudDownload() { public CommandECloudDownload() {
super("download"); super("download");
}
private boolean isBlockedExpansion(String name) {
String env = System.getenv("PAPI_BLOCKED_EXPANSIONS");
if (env == null) {
return false;
} }
return Arrays.stream(env.split(",")) private boolean isBlockedExpansion(String name) {
.anyMatch(s -> s.equalsIgnoreCase(name)); String env = System.getenv("PAPI_BLOCKED_EXPANSIONS");
} if (env == null) {
return false;
}
@Override return Arrays.stream(env.split(","))
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, .anyMatch(s -> s.equalsIgnoreCase(name));
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&cYou must supply the name of an expansion.");
return;
} }
if (isBlockedExpansion(params.get(0))) { @Override
Msg.msg(sender, public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
"&cThis expansion can't be downloaded."); @NotNull final CommandSender sender, @NotNull final String alias,
return; @NotNull @Unmodifiable final List<String> params) {
} if (params.isEmpty()) {
final CloudExpansion expansion = plugin.getCloudExpansionManager()
.findCloudExpansionByName(params.get(0)).orElse(null);
if (expansion == null) {
Msg.msg(sender,
"&cFailed to find an expansion named: &f" + params.get(0));
return;
}
if (!expansion.isVerified()) {
Msg.msg(sender, "&cThe expansion '&f" + params.get(0) + "&c' is not verified and can only be downloaded manually from &fhttps://placeholderapi.com/ecloud");
return;
}
final CloudExpansion.Version version;
if (params.size() < 2) {
version = expansion.getVersion(expansion.getLatestVersion());
if (version == null) {
Msg.msg(sender,
"&cCould not find latest version for expansion.");
return;
}
} else {
version = expansion.getVersion(params.get(1));
if (version == null) {
Msg.msg(sender,
"&cCould not find specified version: &f" + params.get(1),
"&7Available versions: &f" + expansion.getAvailableVersions());
return;
}
}
plugin.getCloudExpansionManager().downloadExpansion(expansion, version)
.whenComplete((file, exception) -> {
if (exception != null) {
Msg.msg(sender, Msg.msg(sender,
"&cFailed to download expansion: &f" + exception.getMessage()); "&cYou must supply the name of an expansion.");
return; return;
} }
Msg.msg(sender, if (isBlockedExpansion(params.get(0))) {
"&aSuccessfully downloaded expansion &f" + expansion.getName() + " [" + version Msg.msg(sender,
.getVersion() + "] &ato file: &f" + file.getName(), "&cThis expansion can't be downloaded.");
"&aMake sure to type &f/papi reload &ato enable your new expansion!"); return;
}
plugin.getCloudExpansionManager().load(); final CloudExpansion expansion = plugin.getCloudExpansionManager()
}); .findCloudExpansionByName(params.get(0)).orElse(null);
} if (expansion == null) {
Msg.msg(sender,
"&cFailed to find an expansion named: &f" + params.get(0));
return;
}
@Override if (!expansion.isVerified()) {
public void complete(@NotNull final PlaceholderAPIPlugin plugin, Msg.msg(sender, "&cThe expansion '&f" + params.get(0) + "&c' is not verified and can only be downloaded manually from &fhttps://placeholderapi.com/ecloud");
@NotNull final CommandSender sender, @NotNull final String alias, return;
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) { }
if (params.size() > 2) {
return; final CloudExpansion.Version version;
if (params.size() < 2) {
version = expansion.getVersion(expansion.getLatestVersion());
if (version == null) {
Msg.msg(sender,
"&cCould not find latest version for expansion.");
return;
}
} else {
version = expansion.getVersion(params.get(1));
if (version == null) {
Msg.msg(sender,
"&cCould not find specified version: &f" + params.get(1),
"&7Available versions: &f" + expansion.getAvailableVersions());
return;
}
}
plugin.getCloudExpansionManager().downloadExpansion(expansion, version)
.whenComplete((file, exception) -> {
if (exception != null) {
Msg.msg(sender,
"&cFailed to download expansion: &f" + exception.getMessage());
return;
}
Msg.msg(sender,
"&aSuccessfully downloaded expansion &f" + expansion.getName() + " [" + version
.getVersion() + "] &ato file: &f" + file.getName(),
"&aMake sure to type &f/papi reload &ato enable your new expansion!");
plugin.getCloudExpansionManager().load();
});
} }
if (params.size() <= 1) { @Override
final Stream<String> names = plugin.getCloudExpansionManager().getCloudExpansions().values() public void complete(@NotNull final PlaceholderAPIPlugin plugin,
.stream().map(CloudExpansion::getName).map(name -> name.replace(' ', '_')); @NotNull final CommandSender sender, @NotNull final String alias,
suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(0)); @NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
return; if (params.size() > 2) {
} return;
}
final Optional<CloudExpansion> expansion = plugin.getCloudExpansionManager() if (params.size() <= 1) {
.findCloudExpansionByName(params.get(0)); final Stream<String> names = plugin.getCloudExpansionManager().getCloudExpansions().values()
if (!expansion.isPresent()) { .stream().map(CloudExpansion::getName).map(name -> name.replace(' ', '_'));
return; suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(0));
} return;
}
suggestByParameter(expansion.get().getAvailableVersions().stream(), suggestions, params.get(1)); final Optional<CloudExpansion> expansion = plugin.getCloudExpansionManager()
} .findCloudExpansionByName(params.get(0));
if (!expansion.isPresent()) {
return;
}
suggestByParameter(expansion.get().getAvailableVersions().stream(), suggestions, params.get(1));
}
} }

View File

@@ -23,6 +23,7 @@ package me.clip.placeholderapi.commands.impl.cloud;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.expansion.cloud.CloudExpansion; import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
@@ -33,97 +34,97 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandECloudExpansionInfo extends PlaceholderCommand { public final class CommandECloudExpansionInfo extends PlaceholderCommand {
public CommandECloudExpansionInfo() { public CommandECloudExpansionInfo() {
super("info"); super("info");
}
@Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&cYou must specify the name of the expansion.");
return;
} }
final CloudExpansion expansion = plugin.getCloudExpansionManager() @Override
.findCloudExpansionByName(params.get(0)).orElse(null); public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
if (expansion == null) { @NotNull final CommandSender sender, @NotNull final String alias,
Msg.msg(sender, @NotNull @Unmodifiable final List<String> params) {
"&cThere is no expansion with the name: &f" + params.get(0)); if (params.isEmpty()) {
return; Msg.msg(sender,
"&cYou must specify the name of the expansion.");
return;
}
final CloudExpansion expansion = plugin.getCloudExpansionManager()
.findCloudExpansionByName(params.get(0)).orElse(null);
if (expansion == null) {
Msg.msg(sender,
"&cThere is no expansion with the name: &f" + params.get(0));
return;
}
final StringBuilder builder = new StringBuilder();
builder.append("&bExpansion: &f")
.append(expansion.shouldUpdate() ? "&e" : "&a")
.append(expansion.getName())
.append('\n')
.append("&bAuthor: &f")
.append(expansion.getAuthor())
.append('\n')
.append("&bVerified: ")
.append(expansion.isVerified() ? "&a&l✔" : "&c&l❌")
.append('\n');
if (params.size() < 2) {
builder.append("&bLatest Version: &f")
.append(expansion.getLatestVersion())
.append('\n')
.append("&bReleased: &f")
.append(expansion.getTimeSinceLastUpdate())
.append(" ago")
.append('\n')
.append("&bRelease Notes: &f")
.append(expansion.getVersion().getReleaseNotes())
.append('\n');
} else {
final CloudExpansion.Version version = expansion.getVersion(params.get(1));
if (version == null) {
Msg.msg(sender,
"&cCould not find specified version: &f" + params.get(1),
"&aVersions: &f" + expansion.getAvailableVersions());
return;
}
builder.append("&bVersion: &f")
.append(version.getVersion())
.append('\n')
.append("&bRelease Notes: &f")
.append(version.getReleaseNotes())
.append('\n')
.append("&bDownload URL: &f")
.append(version.getUrl())
.append('\n');
}
Msg.msg(sender, builder.toString());
} }
final StringBuilder builder = new StringBuilder(); @Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 2) {
return;
}
builder.append("&bExpansion: &f") if (params.size() <= 1) {
.append(expansion.shouldUpdate() ? "&e" : "&a") final Stream<String> names = plugin.getCloudExpansionManager().getCloudExpansions().values()
.append(expansion.getName()) .stream().map(CloudExpansion::getName).map(name -> name.replace(' ', '_'));
.append('\n') suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(0));
.append("&bAuthor: &f") return;
.append(expansion.getAuthor()) }
.append('\n')
.append("&bVerified: ")
.append(expansion.isVerified() ? "&a&l✔" : "&c&l❌")
.append('\n');
if (params.size() < 2) { final Optional<CloudExpansion> expansion = plugin.getCloudExpansionManager()
builder.append("&bLatest Version: &f") .findCloudExpansionByName(params.get(0));
.append(expansion.getLatestVersion()) if (!expansion.isPresent()) {
.append('\n') return;
.append("&bReleased: &f") }
.append(expansion.getTimeSinceLastUpdate())
.append(" ago")
.append('\n')
.append("&bRelease Notes: &f")
.append(expansion.getVersion().getReleaseNotes())
.append('\n');
} else {
final CloudExpansion.Version version = expansion.getVersion(params.get(1));
if (version == null) {
Msg.msg(sender,
"&cCould not find specified version: &f" + params.get(1),
"&aVersions: &f" + expansion.getAvailableVersions());
return;
}
builder.append("&bVersion: &f") suggestByParameter(expansion.get().getAvailableVersions().stream(), suggestions, params.get(1));
.append(version.getVersion())
.append('\n')
.append("&bRelease Notes: &f")
.append(version.getReleaseNotes())
.append('\n')
.append("&bDownload URL: &f")
.append(version.getUrl())
.append('\n');
} }
Msg.msg(sender, builder.toString());
}
@Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 2) {
return;
}
if (params.size() <= 1) {
final Stream<String> names = plugin.getCloudExpansionManager().getCloudExpansions().values()
.stream().map(CloudExpansion::getName).map(name -> name.replace(' ', '_'));
suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(0));
return;
}
final Optional<CloudExpansion> expansion = plugin.getCloudExpansionManager()
.findCloudExpansionByName(params.get(0));
if (!expansion.isPresent()) {
return;
}
suggestByParameter(expansion.get().getAvailableVersions().stream(), suggestions, params.get(1));
}
} }

View File

@@ -25,12 +25,14 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.configuration.ExpansionSort; import me.clip.placeholderapi.configuration.ExpansionSort;
@@ -54,299 +56,299 @@ import static net.kyori.adventure.text.format.NamedTextColor.*;
public final class CommandECloudExpansionList extends PlaceholderCommand { public final class CommandECloudExpansionList extends PlaceholderCommand {
private static final int PAGE_SIZE = 10; private static final int PAGE_SIZE = 10;
@NotNull @NotNull
private static final Function<CloudExpansion, Object> EXPANSION_NAME = private static final Function<CloudExpansion, Object> EXPANSION_NAME =
expansion -> (expansion.shouldUpdate() ? "&6" : expansion.hasExpansion() ? "&a" : "&7") expansion -> (expansion.shouldUpdate() ? "&6" : expansion.hasExpansion() ? "&a" : "&7")
+ expansion.getName(); + expansion.getName();
@NotNull @NotNull
private static final Function<CloudExpansion, Object> EXPANSION_AUTHOR = private static final Function<CloudExpansion, Object> EXPANSION_AUTHOR =
expansion -> "&f" + expansion.getAuthor(); expansion -> "&f" + expansion.getAuthor();
@NotNull @NotNull
private static final Function<CloudExpansion, Object> EXPANSION_VERIFIED = private static final Function<CloudExpansion, Object> EXPANSION_VERIFIED =
expansion -> expansion.isVerified() ? "&aY" : "&cN"; expansion -> expansion.isVerified() ? "&aY" : "&cN";
@NotNull @NotNull
private static final Function<CloudExpansion, Object> EXPANSION_LATEST_VERSION = private static final Function<CloudExpansion, Object> EXPANSION_LATEST_VERSION =
expansion -> "&f" + expansion.getLatestVersion(); expansion -> "&f" + expansion.getLatestVersion();
@NotNull @NotNull
private static final Function<CloudExpansion, Object> EXPANSION_CURRENT_VERSION = private static final Function<CloudExpansion, Object> EXPANSION_CURRENT_VERSION =
expansion -> "&f" + PlaceholderAPIPlugin.getInstance().getLocalExpansionManager() expansion -> "&f" + PlaceholderAPIPlugin.getInstance().getLocalExpansionManager()
.findExpansionByName(expansion.getName()).map(PlaceholderExpansion::getVersion) .findExpansionByName(expansion.getName()).map(PlaceholderExpansion::getVersion)
.orElse("Unknown"); .orElse("Unknown");
@Unmodifiable @Unmodifiable
private static final Set<String> OPTIONS = ImmutableSet.of("all", "installed"); private static final Set<String> OPTIONS = ImmutableSet.of("all", "installed");
public CommandECloudExpansionList() { public CommandECloudExpansionList() {
super("list"); super("list");
}
@NotNull
private static Collection<CloudExpansion> getExpansions(@NotNull final String target,
@NotNull final PlaceholderAPIPlugin plugin) {
switch (target.toLowerCase(Locale.ROOT)) {
case "all":
return plugin.getCloudExpansionManager().getCloudExpansions().values();
case "installed":
return plugin.getCloudExpansionManager().getCloudExpansionsInstalled().values();
default:
return plugin.getCloudExpansionManager().getCloudExpansionsByAuthor(target).values();
}
}
@NotNull
private static List<CloudExpansion> getPage(@NotNull final List<CloudExpansion> expansions,
final int page) {
final int head = (page * PAGE_SIZE);
final int tail = Math.min(expansions.size(), head + PAGE_SIZE);
if (expansions.size() < head) {
return Collections.emptyList();
} }
return expansions.subList(head, tail); @NotNull
} private static Collection<CloudExpansion> getExpansions(@NotNull final String target,
@NotNull final PlaceholderAPIPlugin plugin) {
public static void addExpansionTitle(@NotNull final StringBuilder builder, switch (target.toLowerCase(Locale.ROOT)) {
@NotNull final String target, final int page) { case "all":
switch (target.toLowerCase(Locale.ROOT)) { return plugin.getCloudExpansionManager().getCloudExpansions().values();
case "all": case "installed":
builder.append("&bAll Expansions"); return plugin.getCloudExpansionManager().getCloudExpansionsInstalled().values();
break; default:
case "installed": return plugin.getCloudExpansionManager().getCloudExpansionsByAuthor(target).values();
builder.append("&bInstalled Expansions");
break;
default:
builder.append("&bExpansions by &f")
.append(target);
break;
}
if (page == -1) {
builder.append('\n');
return;
}
builder.append(" &bPage&7: &a")
.append(page)
.append("&r");
}
private static Component getMessage(@NotNull final List<CloudExpansion> expansions,
final int page, final int limit, @NotNull final String target) {
final SimpleDateFormat format = PlaceholderAPIPlugin.getDateFormat();
final TextComponent.Builder message = text();
for (int index = 0; index < expansions.size(); index++) {
final CloudExpansion expansion = expansions.get(index);
final TextComponent.Builder line = text();
final int expansionNumber = index + ((page - 1) * PAGE_SIZE) + 1;
line.append(text(expansionNumber + ". ", DARK_GRAY));
final NamedTextColor expansionColour;
if (expansion.shouldUpdate()) {
expansionColour = GOLD;
} else {
if (expansion.hasExpansion()) {
expansionColour = GREEN;
} else {
expansionColour = GRAY;
} }
}
line.append(text(expansion.getName(), expansionColour));
line.clickEvent(ClickEvent.suggestCommand("/papi ecloud download " + expansion.getName()));
final TextComponent.Builder hoverText = text("Click to download this expansion!", AQUA)
.append(newline()).append(newline())
.append(text("Author: ", AQUA)).append(text(expansion.getAuthor(), WHITE))
.append(newline())
.append(text("Verified: ", AQUA)).append(text(expansion.isVerified() ? "" : "", expansion.isVerified() ? GREEN : RED, TextDecoration.BOLD))
.append(newline())
.append(text("Released: ", AQUA)).append(text(format.format(expansion.getLastUpdate()), WHITE))
.toBuilder();
Optional.ofNullable(expansion.getDescription())
.filter(description -> !description.isEmpty())
.ifPresent(description -> hoverText.append(newline()).append(newline())
.append(text(description.replace("\r", "").trim(), WHITE))
);
line.hoverEvent(HoverEvent.showText(hoverText.build()));
if (index != expansions.size() - 1) {
line.append(newline());
}
message.append(line.build());
} }
if (limit > 1) { @NotNull
message.append(newline()); private static List<CloudExpansion> getPage(@NotNull final List<CloudExpansion> expansions,
final int page) {
final int head = (page * PAGE_SIZE);
final int tail = Math.min(expansions.size(), head + PAGE_SIZE);
final TextComponent.Builder left = text("", page > 1 ? GRAY : DARK_GRAY).toBuilder(); if (expansions.size() < head) {
return Collections.emptyList();
}
if (page > 1) { return expansions.subList(head, tail);
left.clickEvent(ClickEvent.runCommand("/papi ecloud list " + target + " " + (page - 1)));
}
final TextComponent.Builder right = text("", page < limit ? GRAY : DARK_GRAY).toBuilder();
if (page < limit) {
right.clickEvent(ClickEvent.runCommand("/papi ecloud list " + target + " " + (page + 1)));
}
message.append(left, text(" " + page + " ", GREEN), right);
} }
return message.build(); public static void addExpansionTitle(@NotNull final StringBuilder builder,
} @NotNull final String target, final int page) {
switch (target.toLowerCase(Locale.ROOT)) {
case "all":
builder.append("&bAll Expansions");
break;
case "installed":
builder.append("&bInstalled Expansions");
break;
default:
builder.append("&bExpansions by &f")
.append(target);
break;
}
private static void addExpansionTable(@NotNull final List<CloudExpansion> expansions, if (page == -1) {
@NotNull final StringBuilder message, final int startIndex, builder.append('\n');
@NotNull final String versionTitle, return;
@NotNull final Function<CloudExpansion, Object> versionFunction) { }
final Map<String, Function<CloudExpansion, Object>> functions = new LinkedHashMap<>();
final AtomicInteger counter = new AtomicInteger(startIndex); builder.append(" &bPage&7: &a")
functions.put("&f", expansion -> "&8" + counter.getAndIncrement() + "."); .append(page)
.append("&r");
functions.put("&9Name", EXPANSION_NAME);
functions.put("&9Author", EXPANSION_AUTHOR);
functions.put("&9Verified", EXPANSION_VERIFIED);
functions.put(versionTitle, versionFunction);
final List<List<String>> rows = new ArrayList<>();
rows.add(0, new ArrayList<>(functions.keySet()));
for (final CloudExpansion expansion : expansions) {
rows.add(functions.values().stream().map(function -> function.apply(expansion))
.map(Objects::toString).collect(Collectors.toList()));
} }
final List<String> table = Format.tablify(Format.Align.LEFT, rows) private static Component getMessage(@NotNull final List<CloudExpansion> expansions,
.orElse(Collections.emptyList()); final int page, final int limit, @NotNull final String target) {
if (table.isEmpty()) { final SimpleDateFormat format = PlaceholderAPIPlugin.getDateFormat();
return;
final TextComponent.Builder message = text();
for (int index = 0; index < expansions.size(); index++) {
final CloudExpansion expansion = expansions.get(index);
final TextComponent.Builder line = text();
final int expansionNumber = index + ((page - 1) * PAGE_SIZE) + 1;
line.append(text(expansionNumber + ". ", DARK_GRAY));
final NamedTextColor expansionColour;
if (expansion.shouldUpdate()) {
expansionColour = GOLD;
} else {
if (expansion.hasExpansion()) {
expansionColour = GREEN;
} else {
expansionColour = GRAY;
}
}
line.append(text(expansion.getName(), expansionColour));
line.clickEvent(ClickEvent.suggestCommand("/papi ecloud download " + expansion.getName()));
final TextComponent.Builder hoverText = text("Click to download this expansion!", AQUA)
.append(newline()).append(newline())
.append(text("Author: ", AQUA)).append(text(expansion.getAuthor(), WHITE))
.append(newline())
.append(text("Verified: ", AQUA)).append(text(expansion.isVerified() ? "" : "", expansion.isVerified() ? GREEN : RED, TextDecoration.BOLD))
.append(newline())
.append(text("Released: ", AQUA)).append(text(format.format(expansion.getLastUpdate()), WHITE))
.toBuilder();
Optional.ofNullable(expansion.getDescription())
.filter(description -> !description.isEmpty())
.ifPresent(description -> hoverText.append(newline()).append(newline())
.append(text(description.replace("\r", "").trim(), WHITE))
);
line.hoverEvent(HoverEvent.showText(hoverText.build()));
if (index != expansions.size() - 1) {
line.append(newline());
}
message.append(line.build());
}
if (limit > 1) {
message.append(newline());
final TextComponent.Builder left = text("", page > 1 ? GRAY : DARK_GRAY).toBuilder();
if (page > 1) {
left.clickEvent(ClickEvent.runCommand("/papi ecloud list " + target + " " + (page - 1)));
}
final TextComponent.Builder right = text("", page < limit ? GRAY : DARK_GRAY).toBuilder();
if (page < limit) {
right.clickEvent(ClickEvent.runCommand("/papi ecloud list " + target + " " + (page + 1)));
}
message.append(left, text(" " + page + " ", GREEN), right);
}
return message.build();
} }
table.add(1, "&8" + Strings.repeat("-", table.get(0).length() - (rows.get(0).size() * 2))); private static void addExpansionTable(@NotNull final List<CloudExpansion> expansions,
@NotNull final StringBuilder message, final int startIndex,
@NotNull final String versionTitle,
@NotNull final Function<CloudExpansion, Object> versionFunction) {
final Map<String, Function<CloudExpansion, Object>> functions = new LinkedHashMap<>();
message.append(String.join("\n", table)); final AtomicInteger counter = new AtomicInteger(startIndex);
} functions.put("&f", expansion -> "&8" + counter.getAndIncrement() + ".");
@Override functions.put("&9Name", EXPANSION_NAME);
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, functions.put("&9Author", EXPANSION_AUTHOR);
@NotNull final CommandSender sender, @NotNull final String alias, functions.put("&9Verified", EXPANSION_VERIFIED);
@NotNull @Unmodifiable final List<String> params) { functions.put(versionTitle, versionFunction);
if (params.isEmpty()) {
Msg.msg(sender, final List<List<String>> rows = new ArrayList<>();
"&cYou must specify an option. [all, {author}, installed]");
return; rows.add(0, new ArrayList<>(functions.keySet()));
for (final CloudExpansion expansion : expansions) {
rows.add(functions.values().stream().map(function -> function.apply(expansion))
.map(Objects::toString).collect(Collectors.toList()));
}
final List<String> table = Format.tablify(Format.Align.LEFT, rows)
.orElse(Collections.emptyList());
if (table.isEmpty()) {
return;
}
table.add(1, "&8" + Strings.repeat("-", table.get(0).length() - (rows.get(0).size() * 2)));
message.append(String.join("\n", table));
} }
final boolean installed = params.get(0).equalsIgnoreCase("installed"); @Override
final List<CloudExpansion> expansions = Lists public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
.newArrayList(getExpansions(params.get(0), plugin)); @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&cYou must specify an option. [all, {author}, installed]");
return;
}
if (expansions.isEmpty()) { final boolean installed = params.get(0).equalsIgnoreCase("installed");
Msg.msg(sender, final List<CloudExpansion> expansions = Lists
"&cNo expansions available to list."); .newArrayList(getExpansions(params.get(0), plugin));
return;
if (expansions.isEmpty()) {
Msg.msg(sender,
"&cNo expansions available to list.");
return;
}
expansions
.sort(plugin.getPlaceholderAPIConfig().getExpansionSort().orElse(ExpansionSort.LATEST));
if (!(sender instanceof Player) && params.size() < 2) {
final StringBuilder builder = new StringBuilder();
addExpansionTitle(builder, params.get(0), -1);
addExpansionTable(expansions,
builder,
1,
installed ? "&9Version" : "&9Latest Version",
installed ? EXPANSION_CURRENT_VERSION : EXPANSION_LATEST_VERSION);
Msg.msg(sender, builder.toString());
return;
}
final int page;
if (params.size() < 2) {
page = 1;
} else {
//noinspection UnstableApiUsage
final Integer parsed = Ints.tryParse(params.get(1));
if (parsed == null) {
Msg.msg(sender,
"&cPage number must be an integer.");
return;
}
final int limit = (int) Math.ceil((double) expansions.size() / PAGE_SIZE);
if (parsed < 1 || parsed > limit) {
Msg.msg(sender,
"&cPage number must be in the range &8[&a1&7..&a" + limit + "&8]");
return;
}
page = parsed;
}
final StringBuilder builder = new StringBuilder();
final List<CloudExpansion> values = getPage(expansions, page - 1);
addExpansionTitle(builder, params.get(0), page);
if (!(sender instanceof Player)) {
addExpansionTable(values,
builder,
((page - 1) * PAGE_SIZE) + 1,
installed ? "&9Version" : "&9Latest Version",
installed ? EXPANSION_CURRENT_VERSION : EXPANSION_LATEST_VERSION);
Msg.msg(sender, builder.toString());
return;
}
Msg.msg(sender, builder.toString());
final int limit = (int) Math.ceil((double) expansions.size() / PAGE_SIZE);
final Component message = getMessage(values, page, limit, params.get(0));
plugin.getAdventure().player((Player) sender).sendMessage(message);
} }
expansions @Override
.sort(plugin.getPlaceholderAPIConfig().getExpansionSort().orElse(ExpansionSort.LATEST)); public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 2) {
return;
}
if (!(sender instanceof Player) && params.size() < 2) { if (params.size() <= 1) {
final StringBuilder builder = new StringBuilder(); suggestByParameter(
Sets.union(OPTIONS, plugin.getCloudExpansionManager().getCloudExpansionAuthors())
.stream(), suggestions, params.isEmpty() ? null : params.get(0));
return;
}
addExpansionTitle(builder, params.get(0), -1); suggestByParameter(IntStream.rangeClosed(1,
addExpansionTable(expansions, (int) Math.ceil((double) getExpansions(params.get(0), plugin).size() / PAGE_SIZE))
builder, .mapToObj(Objects::toString), suggestions, params.get(1));
1,
installed ? "&9Version" : "&9Latest Version",
installed ? EXPANSION_CURRENT_VERSION : EXPANSION_LATEST_VERSION);
Msg.msg(sender, builder.toString());
return;
} }
final int page;
if (params.size() < 2) {
page = 1;
} else {
//noinspection UnstableApiUsage
final Integer parsed = Ints.tryParse(params.get(1));
if (parsed == null) {
Msg.msg(sender,
"&cPage number must be an integer.");
return;
}
final int limit = (int) Math.ceil((double) expansions.size() / PAGE_SIZE);
if (parsed < 1 || parsed > limit) {
Msg.msg(sender,
"&cPage number must be in the range &8[&a1&7..&a" + limit + "&8]");
return;
}
page = parsed;
}
final StringBuilder builder = new StringBuilder();
final List<CloudExpansion> values = getPage(expansions, page - 1);
addExpansionTitle(builder, params.get(0), page);
if (!(sender instanceof Player)) {
addExpansionTable(values,
builder,
((page - 1) * PAGE_SIZE) + 1,
installed ? "&9Version" : "&9Latest Version",
installed ? EXPANSION_CURRENT_VERSION : EXPANSION_LATEST_VERSION);
Msg.msg(sender, builder.toString());
return;
}
Msg.msg(sender, builder.toString());
final int limit = (int) Math.ceil((double) expansions.size() / PAGE_SIZE);
final Component message = getMessage(values, page, limit, params.get(0));
plugin.getAdventure().player((Player) sender).sendMessage(message);
}
@Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 2) {
return;
}
if (params.size() <= 1) {
suggestByParameter(
Sets.union(OPTIONS, plugin.getCloudExpansionManager().getCloudExpansionAuthors())
.stream(), suggestions, params.isEmpty() ? null : params.get(0));
return;
}
suggestByParameter(IntStream.rangeClosed(1,
(int) Math.ceil((double) getExpansions(params.get(0), plugin).size() / PAGE_SIZE))
.mapToObj(Objects::toString), suggestions, params.get(1));
}
} }

View File

@@ -21,9 +21,11 @@
package me.clip.placeholderapi.commands.impl.cloud; package me.clip.placeholderapi.commands.impl.cloud;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.expansion.cloud.CloudExpansion; import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
@@ -34,61 +36,61 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandECloudExpansionPlaceholders extends PlaceholderCommand { public final class CommandECloudExpansionPlaceholders extends PlaceholderCommand {
public CommandECloudExpansionPlaceholders() { public CommandECloudExpansionPlaceholders() {
super("placeholders"); super("placeholders");
}
@Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&cYou must specify the name of the expansion.");
return;
} }
final CloudExpansion expansion = plugin.getCloudExpansionManager() @Override
.findCloudExpansionByName(params.get(0)).orElse(null); public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
if (expansion == null) { @NotNull final CommandSender sender, @NotNull final String alias,
Msg.msg(sender, @NotNull @Unmodifiable final List<String> params) {
"&cThere is no expansion with the name: &f" + params.get(0)); if (params.isEmpty()) {
return; Msg.msg(sender,
"&cYou must specify the name of the expansion.");
return;
}
final CloudExpansion expansion = plugin.getCloudExpansionManager()
.findCloudExpansionByName(params.get(0)).orElse(null);
if (expansion == null) {
Msg.msg(sender,
"&cThere is no expansion with the name: &f" + params.get(0));
return;
}
final List<String> placeholders = expansion.getPlaceholders();
if (placeholders == null || placeholders.isEmpty()) {
Msg.msg(sender,
"&cThe expansion specified does not have placeholders listed.");
return;
}
final List<List<String>> partitions = Lists
.partition(placeholders.stream().sorted().collect(Collectors.toList()), 10);
Msg.msg(sender,
"&6" + placeholders.size() + "&7 placeholders: &a",
partitions.stream().map(partition -> String.join(", ", partition))
.collect(Collectors.joining("\n")));
} }
final List<String> placeholders = expansion.getPlaceholders(); @Override
if (placeholders == null || placeholders.isEmpty()) { public void complete(@NotNull final PlaceholderAPIPlugin plugin,
Msg.msg(sender, @NotNull final CommandSender sender, @NotNull final String alias,
"&cThe expansion specified does not have placeholders listed."); @NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
return; if (params.size() > 1) {
return;
}
final Stream<String> names = plugin.getCloudExpansionManager()
.getCloudExpansions()
.values()
.stream()
.map(CloudExpansion::getName)
.map(name -> name.replace(' ', '_'));
suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(0));
} }
final List<List<String>> partitions = Lists
.partition(placeholders.stream().sorted().collect(Collectors.toList()), 10);
Msg.msg(sender,
"&6" + placeholders.size() + "&7 placeholders: &a",
partitions.stream().map(partition -> String.join(", ", partition))
.collect(Collectors.joining("\n")));
}
@Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 1) {
return;
}
final Stream<String> names = plugin.getCloudExpansionManager()
.getCloudExpansions()
.values()
.stream()
.map(CloudExpansion::getName)
.map(name -> name.replace(' ', '_'));
suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(0));
}
} }

View File

@@ -21,6 +21,7 @@
package me.clip.placeholderapi.commands.impl.cloud; package me.clip.placeholderapi.commands.impl.cloud;
import java.util.List; import java.util.List;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
@@ -30,18 +31,18 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandECloudRefresh extends PlaceholderCommand { public final class CommandECloudRefresh extends PlaceholderCommand {
public CommandECloudRefresh() { public CommandECloudRefresh() {
super("refresh"); super("refresh");
} }
@Override @Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias, @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) { @NotNull @Unmodifiable final List<String> params) {
plugin.getCloudExpansionManager().load(); plugin.getCloudExpansionManager().load();
Msg.msg(sender, Msg.msg(sender,
"&aThe eCloud manager has been refreshed!"); "&aThe eCloud manager has been refreshed!");
} }
} }

View File

@@ -21,6 +21,7 @@
package me.clip.placeholderapi.commands.impl.cloud; package me.clip.placeholderapi.commands.impl.cloud;
import java.util.List; import java.util.List;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.expansion.manager.CloudExpansionManager; import me.clip.placeholderapi.expansion.manager.CloudExpansionManager;
@@ -31,34 +32,34 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandECloudStatus extends PlaceholderCommand { public final class CommandECloudStatus extends PlaceholderCommand {
public CommandECloudStatus() { public CommandECloudStatus() {
super("status"); super("status");
}
@Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
final CloudExpansionManager manager = plugin.getCloudExpansionManager();
final int updateCount = manager.getCloudUpdateCount();
final int authorCount = manager.getCloudExpansionAuthorCount();
final int expansionCount = manager.getCloudExpansions().size();
final StringBuilder builder = new StringBuilder();
builder.append("&bThere are &a").append(expansionCount)
.append("&b expansions available on the eCloud.").append('\n');
builder.append("&7A total of &f").append(authorCount)
.append("&7 authors have contributed to the eCloud.").append('\n');
if (updateCount > 0) {
builder.append("&eYou have &f").append(updateCount)
.append(updateCount > 1 ? "&e expansions" : "&e expansion").append(" installed that ")
.append(updateCount > 1 ? "have an" : "has an").append(" update available.");
} }
Msg.msg(sender, builder.toString()); @Override
} public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
final CloudExpansionManager manager = plugin.getCloudExpansionManager();
final int updateCount = manager.getCloudUpdateCount();
final int authorCount = manager.getCloudExpansionAuthorCount();
final int expansionCount = manager.getCloudExpansions().size();
final StringBuilder builder = new StringBuilder();
builder.append("&bThere are &a").append(expansionCount)
.append("&b expansions available on the eCloud.").append('\n');
builder.append("&7A total of &f").append(authorCount)
.append("&7 authors have contributed to the eCloud.").append('\n');
if (updateCount > 0) {
builder.append("&eYou have &f").append(updateCount)
.append(updateCount > 1 ? "&e expansions" : "&e expansion").append(" installed that ")
.append(updateCount > 1 ? "have an" : "has an").append(" update available.");
}
Msg.msg(sender, builder.toString());
}
} }

View File

@@ -21,6 +21,7 @@
package me.clip.placeholderapi.commands.impl.cloud; package me.clip.placeholderapi.commands.impl.cloud;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -28,6 +29,7 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
@@ -44,98 +46,98 @@ import org.jetbrains.annotations.Unmodifiable;
*/ */
public final class CommandECloudUpdate extends PlaceholderCommand { public final class CommandECloudUpdate extends PlaceholderCommand {
public CommandECloudUpdate() { public CommandECloudUpdate() {
super("update"); super("update");
}
private static CompletableFuture<List<@Nullable Class<? extends PlaceholderExpansion>>> downloadAndDiscover(
@NotNull final List<CloudExpansion> expansions, @NotNull final PlaceholderAPIPlugin plugin) {
return expansions.stream()
.map(expansion -> plugin.getCloudExpansionManager()
.downloadExpansion(expansion, expansion.getVersion()))
.map(future -> future.thenCompose(plugin.getLocalExpansionManager()::findExpansionInFile))
.collect(Futures.collector());
}
@Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&cYou must define 'all' or the name of an expansion to update.");
return;
} }
final boolean multiple = params.get(0).equalsIgnoreCase("all"); private static CompletableFuture<List<@Nullable Class<? extends PlaceholderExpansion>>> downloadAndDiscover(
final List<CloudExpansion> expansions = new ArrayList<>(); @NotNull final List<CloudExpansion> expansions, @NotNull final PlaceholderAPIPlugin plugin) {
return expansions.stream()
// gather target expansions .map(expansion -> plugin.getCloudExpansionManager()
if (multiple) { .downloadExpansion(expansion, expansion.getVersion()))
expansions.addAll(plugin.getCloudExpansionManager().getCloudExpansionsInstalled().values()); .map(future -> future.thenCompose(plugin.getLocalExpansionManager()::findExpansionInFile))
} else { .collect(Futures.collector());
plugin.getCloudExpansionManager().findCloudExpansionByName(params.get(0))
.ifPresent(expansions::add);
} }
// remove the ones that are the latest version @Override
expansions.removeIf(expansion -> !expansion.shouldUpdate()); public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&cYou must define 'all' or the name of an expansion to update.");
return;
}
if (expansions.isEmpty()) { final boolean multiple = params.get(0).equalsIgnoreCase("all");
Msg.msg(sender, final List<CloudExpansion> expansions = new ArrayList<>();
"&cNo updates available for " + (!multiple ? "this expansion."
: "your active expansions."));
return;
}
Msg.msg(sender, // gather target expansions
"&aUpdating expansions: " + expansions.stream().map(CloudExpansion::getName) if (multiple) {
.collect(Collectors.joining("&7, &6", "&8[&6", "&8]&r"))); expansions.addAll(plugin.getCloudExpansionManager().getCloudExpansionsInstalled().values());
} else {
plugin.getCloudExpansionManager().findCloudExpansionByName(params.get(0))
.ifPresent(expansions::add);
}
// remove the ones that are the latest version
expansions.removeIf(expansion -> !expansion.shouldUpdate());
if (expansions.isEmpty()) {
Msg.msg(sender,
"&cNo updates available for " + (!multiple ? "this expansion."
: "your active expansions."));
return;
}
Futures.onMainThread(plugin, downloadAndDiscover(expansions, plugin), (classes, exception) -> {
if (exception != null) {
Msg.msg(sender, Msg.msg(sender,
"&cFailed to update expansions: &e" + exception.getMessage()); "&aUpdating expansions: " + expansions.stream().map(CloudExpansion::getName)
return; .collect(Collectors.joining("&7, &6", "&8[&6", "&8]&r")));
}
Msg.msg(sender, Futures.onMainThread(plugin, downloadAndDiscover(expansions, plugin), (classes, exception) -> {
"&aSuccessfully downloaded updates, registering new versions."); if (exception != null) {
Msg.msg(sender,
"&cFailed to update expansions: &e" + exception.getMessage());
return;
}
final String message = classes.stream() Msg.msg(sender,
.filter(Objects::nonNull) "&aSuccessfully downloaded updates, registering new versions.");
.map(plugin.getLocalExpansionManager()::register)
.filter(Optional::isPresent)
.map(Optional::get)
.map(expansion -> " &a" + expansion.getName() + " &f" + expansion.getVersion())
.collect(Collectors.joining("\n"));
Msg.msg(sender, final String message = classes.stream()
"&7Registered expansions:", message); .filter(Objects::nonNull)
.map(plugin.getLocalExpansionManager()::register)
.filter(Optional::isPresent)
.map(Optional::get)
.map(expansion -> " &a" + expansion.getName() + " &f" + expansion.getVersion())
.collect(Collectors.joining("\n"));
}); Msg.msg(sender,
} "&7Registered expansions:", message);
@Override });
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 1) {
return;
} }
final List<CloudExpansion> installed = Lists @Override
.newArrayList(plugin.getCloudExpansionManager().getCloudExpansionsInstalled().values()); public void complete(@NotNull final PlaceholderAPIPlugin plugin,
installed.removeIf(expansion -> !expansion.shouldUpdate()); @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 1) {
return;
}
if (!installed.isEmpty() && (params.isEmpty() || "all" final List<CloudExpansion> installed = Lists
.startsWith(params.get(0).toLowerCase(Locale.ROOT)))) { .newArrayList(plugin.getCloudExpansionManager().getCloudExpansionsInstalled().values());
suggestions.add("all"); installed.removeIf(expansion -> !expansion.shouldUpdate());
if (!installed.isEmpty() && (params.isEmpty() || "all"
.startsWith(params.get(0).toLowerCase(Locale.ROOT)))) {
suggestions.add("all");
}
suggestByParameter(
installed.stream().map(CloudExpansion::getName).map(name -> name.replace(" ", "_")),
suggestions, params.isEmpty() ? null : params.get(0));
} }
suggestByParameter(
installed.stream().map(CloudExpansion::getName).map(name -> name.replace(" ", "_")),
suggestions, params.isEmpty() ? null : params.get(0));
}
} }

View File

@@ -54,160 +54,160 @@ import java.util.stream.Collectors;
public final class CommandDump extends PlaceholderCommand { public final class CommandDump extends PlaceholderCommand {
@NotNull @NotNull
private static final String URL = "https://paste.helpch.at/"; private static final String URL = "https://paste.helpch.at/";
@NotNull @NotNull
private static final Gson gson = new Gson(); private static final Gson gson = new Gson();
@NotNull @NotNull
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.LONG) .ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(Locale.US) .withLocale(Locale.US)
.withZone(ZoneId.of("UTC")); .withZone(ZoneId.of("UTC"));
public CommandDump() { public CommandDump() {
super("dump"); super("dump");
} }
@Override @Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias, @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) { @NotNull @Unmodifiable final List<String> params) {
postDump(makeDump(plugin)).whenComplete((key, exception) -> { postDump(makeDump(plugin)).whenComplete((key, exception) -> {
if (exception != null) { if (exception != null) {
plugin.getLogger().log(Level.WARNING, "failed to post dump details", exception); plugin.getLogger().log(Level.WARNING, "failed to post dump details", exception);
Msg.msg(sender, Msg.msg(sender,
"&cFailed to post dump details, check console."); "&cFailed to post dump details, check console.");
return; return;
} }
Msg.msg(sender, Msg.msg(sender,
"&aSuccessfully posted dump: " + URL + key); "&aSuccessfully posted dump: " + URL + key);
}); });
} }
@NotNull @NotNull
private CompletableFuture<String> postDump(@NotNull final String dump) { private CompletableFuture<String> postDump(@NotNull final String dump) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try { try {
final HttpURLConnection connection = ((HttpURLConnection) new URL(URL + "documents") final HttpURLConnection connection = ((HttpURLConnection) new URL(URL + "documents")
.openConnection()); .openConnection());
connection.setRequestMethod("POST"); connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "text/plain; charset=utf-8"); connection.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
connection.setDoOutput(true); connection.setDoOutput(true);
connection.connect(); connection.connect();
try (final OutputStream stream = connection.getOutputStream()) {
stream.write(dump.getBytes(StandardCharsets.UTF_8));
}
try (final InputStream stream = connection.getInputStream()) {
final String json = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8));
return gson.fromJson(json, JsonObject.class).get("key").getAsString();
}
} catch (final IOException ex) {
throw new CompletionException(ex);
}
});
}
@NotNull
private String makeDump(@NotNull final PlaceholderAPIPlugin plugin) {
final StringBuilder builder = new StringBuilder();
builder.append("Generated: ")
.append(DATE_FORMAT.format(Instant.now()))
.append("\n\n");
builder.append("PlaceholderAPI: ")
.append(plugin.getDescription().getVersion())
.append("\n\n");
builder.append("Expansions Registered:")
.append('\n');
final List<PlaceholderExpansion> expansions = plugin.getLocalExpansionManager()
.getExpansions()
.stream()
.sorted(
Comparator.comparing(PlaceholderExpansion::getIdentifier)
.thenComparing(PlaceholderExpansion::getAuthor)
)
.collect(Collectors.toList());
int size = expansions.stream().map(e -> e.getIdentifier().length())
.max(Integer::compareTo)
.orElse(0);
for (final PlaceholderExpansion expansion : expansions) {
builder.append(" ")
.append(String.format("%-" + size + "s", expansion.getIdentifier()))
.append(" [Author: ")
.append(expansion.getAuthor())
.append(", Version: ")
.append(expansion.getVersion())
.append("]\n");
try (final OutputStream stream = connection.getOutputStream()) {
stream.write(dump.getBytes(StandardCharsets.UTF_8));
} }
try (final InputStream stream = connection.getInputStream()) { builder.append('\n');
final String json = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8));
return gson.fromJson(json, JsonObject.class).get("key").getAsString(); builder.append("Expansions Directory:")
.append('\n');
final String[] jars = plugin.getLocalExpansionManager()
.getExpansionsFolder()
.list((dir, name) -> name.toLowerCase(Locale.ROOT).endsWith(".jar"));
if (jars == null) {
builder.append(" ¨[Warning]: Could not load jar files from expansions folder.");
} else {
for (final String jar : jars) {
builder.append(" ")
.append(jar)
.append('\n');
}
} }
} catch (final IOException ex) {
throw new CompletionException(ex);
}
});
}
@NotNull builder.append('\n');
private String makeDump(@NotNull final PlaceholderAPIPlugin plugin) {
final StringBuilder builder = new StringBuilder();
builder.append("Generated: ") builder.append("Server Info: ")
.append(DATE_FORMAT.format(Instant.now())) .append(plugin.getServer().getBukkitVersion())
.append("\n\n"); .append('/')
.append(plugin.getServer().getVersion())
.append("\n");
builder.append("PlaceholderAPI: ") builder.append("Java Version: ")
.append(plugin.getDescription().getVersion()) .append(System.getProperty("java.version"))
.append("\n\n"); .append("\n\n");
builder.append("Expansions Registered:") builder.append("Plugin Info:")
.append('\n'); .append('\n');
final List<PlaceholderExpansion> expansions = plugin.getLocalExpansionManager() List<Plugin> plugins = Arrays.stream(plugin.getServer().getPluginManager().getPlugins())
.getExpansions() .sorted(Comparator.comparing(Plugin::getName))
.stream() .collect(Collectors.toList());
.sorted(
Comparator.comparing(PlaceholderExpansion::getIdentifier)
.thenComparing(PlaceholderExpansion::getAuthor)
)
.collect(Collectors.toList());
int size = expansions.stream().map(e -> e.getIdentifier().length()) size = plugins.stream().map(pl -> pl.getName().length())
.max(Integer::compareTo) .max(Integer::compareTo)
.orElse(0); .orElse(0);
for (final PlaceholderExpansion expansion : expansions) { for (final Plugin other : plugins) {
builder.append(" ") builder.append(" ")
.append(String.format("%-" + size + "s", expansion.getIdentifier())) .append(String.format("%-" + size + "s", other.getName()))
.append(" [Author: ") .append(" [Authors: [")
.append(expansion.getAuthor()) .append(String.join(", ", other.getDescription().getAuthors()))
.append(", Version: ") .append("], Version: ")
.append(expansion.getVersion()) .append(other.getDescription().getVersion())
.append("]\n"); .append("]")
.append("\n");
}
return builder.toString();
} }
builder.append('\n');
builder.append("Expansions Directory:")
.append('\n');
final String[] jars = plugin.getLocalExpansionManager()
.getExpansionsFolder()
.list((dir, name) -> name.toLowerCase(Locale.ROOT).endsWith(".jar"));
if (jars == null) {
builder.append(" ¨[Warning]: Could not load jar files from expansions folder.");
} else {
for (final String jar : jars) {
builder.append(" ")
.append(jar)
.append('\n');
}
}
builder.append('\n');
builder.append("Server Info: ")
.append(plugin.getServer().getBukkitVersion())
.append('/')
.append(plugin.getServer().getVersion())
.append("\n");
builder.append("Java Version: ")
.append(System.getProperty("java.version"))
.append("\n\n");
builder.append("Plugin Info:")
.append('\n');
List<Plugin> plugins = Arrays.stream(plugin.getServer().getPluginManager().getPlugins())
.sorted(Comparator.comparing(Plugin::getName))
.collect(Collectors.toList());
size = plugins.stream().map(pl -> pl.getName().length())
.max(Integer::compareTo)
.orElse(0);
for (final Plugin other : plugins) {
builder.append(" ")
.append(String.format("%-" + size + "s", other.getName()))
.append(" [Authors: [")
.append(String.join(", ", other.getDescription().getAuthors()))
.append("], Version: ")
.append(other.getDescription().getVersion())
.append("]")
.append("\n");
}
return builder.toString();
}
} }

View File

@@ -25,6 +25,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
@@ -37,74 +38,74 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandExpansionRegister extends PlaceholderCommand { public final class CommandExpansionRegister extends PlaceholderCommand {
public CommandExpansionRegister() { public CommandExpansionRegister() {
super("register"); super("register");
}
@Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.size() < 1) {
Msg.msg(sender,
"&cYou must specify the name of an expansion file.");
return;
} }
final LocalExpansionManager manager = plugin.getLocalExpansionManager(); @Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.size() < 1) {
Msg.msg(sender,
"&cYou must specify the name of an expansion file.");
return;
}
final File file = new File(manager.getExpansionsFolder(), params.get(0)); final LocalExpansionManager manager = plugin.getLocalExpansionManager();
if (!file.exists() || !file.getParentFile().equals(manager.getExpansionsFolder())) {
Msg.msg(sender, final File file = new File(manager.getExpansionsFolder(), params.get(0));
"&cThe file &f" + file.getName() + "&c doesn't exist!"); if (!file.exists() || !file.getParentFile().equals(manager.getExpansionsFolder())) {
return; Msg.msg(sender,
"&cThe file &f" + file.getName() + "&c doesn't exist!");
return;
}
Futures.onMainThread(plugin, manager.findExpansionInFile(file), (clazz, exception) -> {
if (exception != null) {
Msg.msg(sender,
"&cFailed to find expansion in file: &f" + file);
plugin.getLogger()
.log(Level.WARNING, "failed to find expansion in file: " + file, exception);
return;
}
if (clazz == null) {
Msg.msg(sender,
"&cNo expansion class found in file: &f" + file);
return;
}
final Optional<PlaceholderExpansion> expansion = manager.register(clazz);
if (!expansion.isPresent()) {
Msg.msg(sender,
"&cFailed to register expansion from &f" + params.get(0));
return;
}
Msg.msg(sender,
"&aSuccessfully registered expansion: &f" + expansion.get().getName());
});
} }
Futures.onMainThread(plugin, manager.findExpansionInFile(file), (clazz, exception) -> { @Override
if (exception != null) { public void complete(@NotNull final PlaceholderAPIPlugin plugin,
Msg.msg(sender, @NotNull final CommandSender sender, @NotNull final String alias,
"&cFailed to find expansion in file: &f" + file); @NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 1) {
return;
}
plugin.getLogger() final String[] fileNames = plugin.getLocalExpansionManager().getExpansionsFolder()
.log(Level.WARNING, "failed to find expansion in file: " + file, exception); .list((dir, name) -> name.endsWith(".jar"));
return; if (fileNames == null || fileNames.length == 0) {
} return;
}
if (clazz == null) { suggestByParameter(Arrays.stream(fileNames), suggestions,
Msg.msg(sender, params.isEmpty() ? null : params.get(0));
"&cNo expansion class found in file: &f" + file);
return;
}
final Optional<PlaceholderExpansion> expansion = manager.register(clazz);
if (!expansion.isPresent()) {
Msg.msg(sender,
"&cFailed to register expansion from &f" + params.get(0));
return;
}
Msg.msg(sender,
"&aSuccessfully registered expansion: &f" + expansion.get().getName());
});
}
@Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 1) {
return;
} }
final String[] fileNames = plugin.getLocalExpansionManager().getExpansionsFolder()
.list((dir, name) -> name.endsWith(".jar"));
if (fileNames == null || fileNames.length == 0) {
return;
}
suggestByParameter(Arrays.stream(fileNames), suggestions,
params.isEmpty() ? null : params.get(0));
}
} }

View File

@@ -22,6 +22,7 @@ package me.clip.placeholderapi.commands.impl.local;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
@@ -33,45 +34,45 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandExpansionUnregister extends PlaceholderCommand { public final class CommandExpansionUnregister extends PlaceholderCommand {
public CommandExpansionUnregister() { public CommandExpansionUnregister() {
super("unregister"); super("unregister");
}
@Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&cYou must specify the name of the expansion.");
return;
} }
final Optional<PlaceholderExpansion> expansion = plugin.getLocalExpansionManager() @Override
.findExpansionByName(params.get(0)); public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
if (!expansion.isPresent()) { @NotNull final CommandSender sender, @NotNull final String alias,
Msg.msg(sender, @NotNull @Unmodifiable final List<String> params) {
"&cThere is no expansion loaded with the identifier: &f" + params.get(0)); if (params.isEmpty()) {
return; Msg.msg(sender,
"&cYou must specify the name of the expansion.");
return;
}
final Optional<PlaceholderExpansion> expansion = plugin.getLocalExpansionManager()
.findExpansionByName(params.get(0));
if (!expansion.isPresent()) {
Msg.msg(sender,
"&cThere is no expansion loaded with the identifier: &f" + params.get(0));
return;
}
final String message = !expansion.get().unregister() ?
"&cFailed to unregister expansion: &f" :
"&aSuccessfully unregistered expansion: &f";
Msg.msg(sender, message + expansion.get().getName());
} }
final String message = !expansion.get().unregister() ? @Override
"&cFailed to unregister expansion: &f" : public void complete(@NotNull final PlaceholderAPIPlugin plugin,
"&aSuccessfully unregistered expansion: &f"; @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 1) {
return;
}
Msg.msg(sender, message + expansion.get().getName()); suggestByParameter(PlaceholderAPI.getRegisteredIdentifiers().stream(), suggestions,
} params.isEmpty() ? null : params.get(0));
@Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 1) {
return;
} }
suggestByParameter(PlaceholderAPI.getRegisteredIdentifiers().stream(), suggestions,
params.isEmpty() ? null : params.get(0));
}
} }

View File

@@ -21,6 +21,7 @@
package me.clip.placeholderapi.commands.impl.local; package me.clip.placeholderapi.commands.impl.local;
import java.util.List; import java.util.List;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
@@ -31,42 +32,42 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandHelp extends PlaceholderCommand { public final class CommandHelp extends PlaceholderCommand {
public CommandHelp() { public CommandHelp() {
super("help"); super("help");
} }
@Override @Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias, @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) { @NotNull @Unmodifiable final List<String> params) {
final PluginDescriptionFile description = plugin.getDescription(); final PluginDescriptionFile description = plugin.getDescription();
Msg.msg(sender, Msg.msg(sender,
"&b&lPlaceholderAPI &8- &7Help Menu &8- &7(&f" + description.getVersion() + "&7)", "&b&lPlaceholderAPI &8- &7Help Menu &8- &7(&f" + description.getVersion() + "&7)",
" ", " ",
"&b/papi &fbcparse &9<me|--null|player name> <message>", "&b/papi &fbcparse &9<me|--null|player name> <message>",
" &7&oParse a message with placeholders and broadcast it", " &7&oParse a message with placeholders and broadcast it",
"&b/papi &fcmdparse &9<me|player> <command with placeholders>", "&b/papi &fcmdparse &9<me|player> <command with placeholders>",
" &7&oParse a message with relational placeholders", " &7&oParse a message with relational placeholders",
"&b/papi &fdump", "&b/papi &fdump",
" &7&oDump all relevant information needed to help debug issues into a paste link.", " &7&oDump all relevant information needed to help debug issues into a paste link.",
"&b/papi &finfo &9<placeholder name>", "&b/papi &finfo &9<placeholder name>",
" &7&oView information for a specific expansion", " &7&oView information for a specific expansion",
"&b/papi &flist", "&b/papi &flist",
" &7&oList active expansions", " &7&oList active expansions",
"&b/papi &fparse &9<me|--null|player name> <message>", "&b/papi &fparse &9<me|--null|player name> <message>",
" &7&oParse a message with placeholders", " &7&oParse a message with placeholders",
"&b/papi &fparserel &9<player one> <player two> <message>", "&b/papi &fparserel &9<player one> <player two> <message>",
" &7&oParse a message with relational placeholders", " &7&oParse a message with relational placeholders",
"&b/papi &fregister &9<file name>", "&b/papi &fregister &9<file name>",
" &7&oRegister an expansion by the name of the file", " &7&oRegister an expansion by the name of the file",
"&b/papi &freload", "&b/papi &freload",
" &7&oReload the config of PAPI", " &7&oReload the config of PAPI",
"&b/papi &funregister &9<expansion name>", "&b/papi &funregister &9<expansion name>",
" &7&oUnregister an expansion by name", " &7&oUnregister an expansion by name",
"&b/papi &fversion", "&b/papi &fversion",
" &7&oView plugin info/version"); " &7&oView plugin info/version");
} }
} }

View File

@@ -21,6 +21,7 @@
package me.clip.placeholderapi.commands.impl.local; package me.clip.placeholderapi.commands.impl.local;
import java.util.List; import java.util.List;
import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
@@ -32,82 +33,82 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandInfo extends PlaceholderCommand { public final class CommandInfo extends PlaceholderCommand {
public CommandInfo() { public CommandInfo() {
super("info"); super("info");
}
@Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
if (params.isEmpty()) {
Msg.msg(sender,
"&cYou must specify the name of the expansion.");
return;
} }
final PlaceholderExpansion expansion = plugin.getLocalExpansionManager() @Override
.findExpansionByIdentifier(params.get(0)).orElse(null); public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
if (expansion == null) { @NotNull final CommandSender sender, @NotNull final String alias,
Msg.msg(sender, @NotNull @Unmodifiable final List<String> params) {
"&cThere is no expansion loaded with the identifier: &f" + params.get(0)); if (params.isEmpty()) {
return; Msg.msg(sender,
"&cYou must specify the name of the expansion.");
return;
}
final PlaceholderExpansion expansion = plugin.getLocalExpansionManager()
.findExpansionByIdentifier(params.get(0)).orElse(null);
if (expansion == null) {
Msg.msg(sender,
"&cThere is no expansion loaded with the identifier: &f" + params.get(0));
return;
}
final StringBuilder builder = new StringBuilder();
builder.append("&7Placeholder expansion info for: &r")
.append(expansion.getName())
.append('\n')
.append("&7Status: &r")
.append(expansion.isRegistered() ? "&aRegistered" : "7cNotRegistered")
.append('\n');
final String author = expansion.getAuthor();
if (author != null) {
builder.append("&7Author: &r")
.append(author)
.append('\n');
}
final String version = expansion.getVersion();
if (version != null) {
builder.append("&7Version: &r")
.append(version)
.append('\n');
}
final String requiredPlugin = expansion.getRequiredPlugin();
if (requiredPlugin != null) {
builder.append("&7Requires plugin: &r")
.append(requiredPlugin)
.append('\n');
}
final List<String> placeholders = expansion.getPlaceholders();
if (placeholders != null && !placeholders.isEmpty()) {
builder.append("&8&m-- &7Placeholders &8&m--&r")
.append('\n');
for (final String placeholder : placeholders) {
builder.append(placeholder)
.append('\n');
}
}
Msg.msg(sender, builder.toString());
} }
final StringBuilder builder = new StringBuilder(); @Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 1) {
return;
}
builder.append("&7Placeholder expansion info for: &r") suggestByParameter(PlaceholderAPI.getRegisteredIdentifiers().stream(), suggestions,
.append(expansion.getName()) params.isEmpty() ? null : params.get(0));
.append('\n')
.append("&7Status: &r")
.append(expansion.isRegistered() ? "&aRegistered" : "7cNotRegistered")
.append('\n');
final String author = expansion.getAuthor();
if (author != null) {
builder.append("&7Author: &r")
.append(author)
.append('\n');
} }
final String version = expansion.getVersion();
if (version != null) {
builder.append("&7Version: &r")
.append(version)
.append('\n');
}
final String requiredPlugin = expansion.getRequiredPlugin();
if (requiredPlugin != null) {
builder.append("&7Requires plugin: &r")
.append(requiredPlugin)
.append('\n');
}
final List<String> placeholders = expansion.getPlaceholders();
if (placeholders != null && !placeholders.isEmpty()) {
builder.append("&8&m-- &7Placeholders &8&m--&r")
.append('\n');
for (final String placeholder : placeholders) {
builder.append(placeholder)
.append('\n');
}
}
Msg.msg(sender, builder.toString());
}
@Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() > 1) {
return;
}
suggestByParameter(PlaceholderAPI.getRegisteredIdentifiers().stream(), suggestions,
params.isEmpty() ? null : params.get(0));
}
} }

View File

@@ -21,9 +21,11 @@
package me.clip.placeholderapi.commands.impl.local; package me.clip.placeholderapi.commands.impl.local;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
@@ -34,28 +36,28 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandList extends PlaceholderCommand { public final class CommandList extends PlaceholderCommand {
public CommandList() { public CommandList() {
super("list"); super("list");
}
@Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
final Set<String> identifiers = PlaceholderAPI.getRegisteredIdentifiers();
if (identifiers.isEmpty()) {
Msg.msg(sender, "&cThere are no placeholder hooks active!");
return;
} }
final List<List<String>> partitions = Lists
.partition(identifiers.stream().sorted().collect(Collectors.toList()), 10);
Msg.msg(sender, @Override
"&7A total of &f" + identifiers.size() + "&7 placeholder hook(s) are active: &a", public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
partitions.stream().map(partition -> String.join("&7, &a", partition)) @NotNull final CommandSender sender, @NotNull final String alias,
.collect(Collectors.joining("\n"))); @NotNull @Unmodifiable final List<String> params) {
} final Set<String> identifiers = PlaceholderAPI.getRegisteredIdentifiers();
if (identifiers.isEmpty()) {
Msg.msg(sender, "&cThere are no placeholder hooks active!");
return;
}
final List<List<String>> partitions = Lists
.partition(identifiers.stream().sorted().collect(Collectors.toList()), 10);
Msg.msg(sender,
"&7A total of &f" + identifiers.size() + "&7 placeholder hook(s) are active: &a",
partitions.stream().map(partition -> String.join("&7, &a", partition))
.collect(Collectors.joining("\n")));
}
} }

View File

@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
@@ -40,217 +41,217 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandParse extends PlaceholderCommand { public final class CommandParse extends PlaceholderCommand {
public CommandParse() { public CommandParse() {
super("parse", "bcparse", "parserel", "cmdparse"); super("parse", "bcparse", "parserel", "cmdparse");
}
@Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) {
switch (alias.toLowerCase(Locale.ROOT)) {
case "parserel":
evaluateParseRelation(sender, params);
break;
case "parse":
evaluateParseSingular(sender, params, false, false);
break;
case "bcparse":
evaluateParseSingular(sender, params, true, false);
break;
case "cmdparse":
evaluateParseSingular(sender, params, false, true);
break;
}
}
@Override
public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
switch (alias.toLowerCase(Locale.ROOT)) {
case "parserel":
completeParseRelation(params, suggestions);
break;
case "parse":
case "bcparse":
case "cmdparse":
completeParseSingular(sender, params, suggestions);
break;
}
}
private void evaluateParseSingular(@NotNull final CommandSender sender,
@NotNull @Unmodifiable final List<String> params, final boolean broadcast,
final boolean command) {
if (params.size() < 2) {
Msg.msg(sender,
"&cYou must provide a target and message: &b/papi "
+ (command ? "cmdparse" : (broadcast ? "bcparse" : "parse"))
+ " &7{target} &a{message}");
return;
} }
OfflinePlayer player;
if ("me".equalsIgnoreCase(params.get(0))) { @Override
if (!(sender instanceof Player)) { public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
Msg.msg(sender, "&cYou must be a player to use &7me&c as a target!"); @NotNull final CommandSender sender, @NotNull final String alias,
return; @NotNull @Unmodifiable final List<String> params) {
} switch (alias.toLowerCase(Locale.ROOT)) {
case "parserel":
player = ((Player) sender); evaluateParseRelation(sender, params);
} else if ("--null".equalsIgnoreCase(params.get(0))) { break;
player = null; case "parse":
} else { evaluateParseSingular(sender, params, false, false);
final OfflinePlayer target = resolvePlayer(params.get(0)); break;
if (target == null) { case "bcparse":
Msg.msg(sender, "&cFailed to find player: &7" + params.get(0)); evaluateParseSingular(sender, params, true, false);
return; break;
} case "cmdparse":
evaluateParseSingular(sender, params, false, true);
player = target; break;
}
} }
final String message = PlaceholderAPI @Override
.setPlaceholders(player, String.join(" ", params.subList(1, params.size()))); public void complete(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias,
if (command) { @NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
Bukkit.dispatchCommand(sender, message); switch (alias.toLowerCase(Locale.ROOT)) {
return; case "parserel":
completeParseRelation(params, suggestions);
break;
case "parse":
case "bcparse":
case "cmdparse":
completeParseSingular(sender, params, suggestions);
break;
}
} }
if (broadcast) {
Bukkit.broadcastMessage(message);
} else {
sender.sendMessage(message);
}
}
private void evaluateParseRelation(@NotNull final CommandSender sender, private void evaluateParseSingular(@NotNull final CommandSender sender,
@NotNull @Unmodifiable final List<String> params) { @NotNull @Unmodifiable final List<String> params, final boolean broadcast,
if (params.size() < 3) { final boolean command) {
Msg.msg(sender, if (params.size() < 2) {
"&cYou must supply two targets, and a message: &b/papi parserel &7{target one} " Msg.msg(sender,
+ "{target two} &a{message}"); "&cYou must provide a target and message: &b/papi "
return; + (command ? "cmdparse" : (broadcast ? "bcparse" : "parse"))
} + " &7{target} &a{message}");
return;
OfflinePlayer playerOne; }
if ("me".equalsIgnoreCase(params.get(0))) { OfflinePlayer player;
if (!(sender instanceof Player)) {
Msg.msg(sender, "&cYou must be a player to use &7me&c as a target!"); if ("me".equalsIgnoreCase(params.get(0))) {
return; if (!(sender instanceof Player)) {
} Msg.msg(sender, "&cYou must be a player to use &7me&c as a target!");
return;
playerOne = ((Player) sender); }
} else {
playerOne = resolvePlayer(params.get(0)); player = ((Player) sender);
} } else if ("--null".equalsIgnoreCase(params.get(0))) {
player = null;
if (playerOne == null || !playerOne.isOnline()) { } else {
Msg.msg(sender, "&cFailed to find player: &f" + params.get(0)); final OfflinePlayer target = resolvePlayer(params.get(0));
return; if (target == null) {
} Msg.msg(sender, "&cFailed to find player: &7" + params.get(0));
return;
OfflinePlayer playerTwo; }
if ("me".equalsIgnoreCase(params.get(1))) { player = target;
if (!(sender instanceof Player)) { }
Msg.msg(sender, "&cYou must be a player to use &7me&c as a target!");
return; final String message = PlaceholderAPI
} .setPlaceholders(player, String.join(" ", params.subList(1, params.size())));
playerTwo = ((Player) sender); if (command) {
} else { Bukkit.dispatchCommand(sender, message);
playerTwo = resolvePlayer(params.get(1)); return;
} }
if (playerTwo == null || !playerTwo.isOnline()) { if (broadcast) {
Msg.msg(sender, "&cFailed to find player: &f" + params.get(1)); Bukkit.broadcastMessage(message);
return; } else {
sender.sendMessage(message);
}
} }
final String message = PlaceholderAPI private void evaluateParseRelation(@NotNull final CommandSender sender,
.setRelationalPlaceholders((Player) playerOne, (Player) playerTwo, @NotNull @Unmodifiable final List<String> params) {
String.join(" ", params.subList(2, params.size()))); if (params.size() < 3) {
Msg.msg(sender,
sender.sendMessage(message); "&cYou must supply two targets, and a message: &b/papi parserel &7{target one} "
} + "{target two} &a{message}");
return;
}
OfflinePlayer playerOne;
private void completeParseSingular(@NotNull final CommandSender sender, if ("me".equalsIgnoreCase(params.get(0))) {
@NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) { if (!(sender instanceof Player)) {
if (params.size() <= 1) { Msg.msg(sender, "&cYou must be a player to use &7me&c as a target!");
if (sender instanceof Player && (params.isEmpty() || "me" return;
.startsWith(params.get(0).toLowerCase(Locale.ROOT)))) { }
suggestions.add("me");
}
if ("--null".startsWith(params.get(0).toLowerCase(Locale.ROOT))) {
suggestions.add("--null");
}
final Stream<String> names = Bukkit.getOnlinePlayers().stream().map(Player::getName);
suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(0));
return; playerOne = ((Player) sender);
} else {
playerOne = resolvePlayer(params.get(0));
}
if (playerOne == null || !playerOne.isOnline()) {
Msg.msg(sender, "&cFailed to find player: &f" + params.get(0));
return;
}
OfflinePlayer playerTwo;
if ("me".equalsIgnoreCase(params.get(1))) {
if (!(sender instanceof Player)) {
Msg.msg(sender, "&cYou must be a player to use &7me&c as a target!");
return;
}
playerTwo = ((Player) sender);
} else {
playerTwo = resolvePlayer(params.get(1));
}
if (playerTwo == null || !playerTwo.isOnline()) {
Msg.msg(sender, "&cFailed to find player: &f" + params.get(1));
return;
}
final String message = PlaceholderAPI
.setRelationalPlaceholders((Player) playerOne, (Player) playerTwo,
String.join(" ", params.subList(2, params.size())));
sender.sendMessage(message);
} }
final String name = params.get(params.size() - 1);
if (!name.startsWith("%") || name.endsWith("%")) { private void completeParseSingular(@NotNull final CommandSender sender,
return; @NotNull @Unmodifiable final List<String> params, @NotNull final List<String> suggestions) {
if (params.size() <= 1) {
if (sender instanceof Player && (params.isEmpty() || "me"
.startsWith(params.get(0).toLowerCase(Locale.ROOT)))) {
suggestions.add("me");
}
if ("--null".startsWith(params.get(0).toLowerCase(Locale.ROOT))) {
suggestions.add("--null");
}
final Stream<String> names = Bukkit.getOnlinePlayers().stream().map(Player::getName);
suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(0));
return;
}
final String name = params.get(params.size() - 1);
if (!name.startsWith("%") || name.endsWith("%")) {
return;
}
final int index = name.indexOf('_');
if (index == -1) {
return; // no arguments supplied yet
}
final PlaceholderExpansion expansion = PlaceholderAPIPlugin.getInstance()
.getLocalExpansionManager().findExpansionByIdentifier(name.substring(1, index))
.orElse(null);
if (expansion == null) {
return;
}
final Set<String> possible = new HashSet<>(expansion.getPlaceholders());
PlaceholderAPIPlugin.getInstance()
.getCloudExpansionManager()
.findCloudExpansionByName(expansion.getName())
.ifPresent(cloud -> possible.addAll(cloud.getPlaceholders()));
suggestByParameter(possible.stream(), suggestions, params.get(params.size() - 1));
} }
final int index = name.indexOf('_'); private void completeParseRelation(@NotNull @Unmodifiable final List<String> params,
if (index == -1) { @NotNull final List<String> suggestions) {
return; // no arguments supplied yet if (params.size() > 2) {
return;
}
final Stream<String> names = Bukkit.getOnlinePlayers().stream().map(Player::getName);
suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(params.size() - 1));
} }
final PlaceholderExpansion expansion = PlaceholderAPIPlugin.getInstance()
.getLocalExpansionManager().findExpansionByIdentifier(name.substring(1, index)) @Nullable
.orElse(null); private OfflinePlayer resolvePlayer(@NotNull final String name) {
if (expansion == null) { OfflinePlayer target = Bukkit.getPlayerExact(name);
return;
if (target == null) {
// Not the best option, but Spigot doesn't offer a good replacement (as usual)
target = Bukkit.getOfflinePlayer(name);
return target.hasPlayedBefore() ? target : null;
}
return target;
} }
final Set<String> possible = new HashSet<>(expansion.getPlaceholders());
PlaceholderAPIPlugin.getInstance()
.getCloudExpansionManager()
.findCloudExpansionByName(expansion.getName())
.ifPresent(cloud -> possible.addAll(cloud.getPlaceholders()));
suggestByParameter(possible.stream(), suggestions, params.get(params.size() - 1));
}
private void completeParseRelation(@NotNull @Unmodifiable final List<String> params,
@NotNull final List<String> suggestions) {
if (params.size() > 2) {
return;
}
final Stream<String> names = Bukkit.getOnlinePlayers().stream().map(Player::getName);
suggestByParameter(names, suggestions, params.isEmpty() ? null : params.get(params.size() - 1));
}
@Nullable
private OfflinePlayer resolvePlayer(@NotNull final String name) {
OfflinePlayer target = Bukkit.getPlayerExact(name);
if (target == null) {
// Not the best option, but Spigot doesn't offer a good replacement (as usual)
target = Bukkit.getOfflinePlayer(name);
return target.hasPlayedBefore() ? target : null;
}
return target;
}
} }

View File

@@ -21,6 +21,7 @@
package me.clip.placeholderapi.commands.impl.local; package me.clip.placeholderapi.commands.impl.local;
import java.util.List; import java.util.List;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@@ -29,15 +30,15 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandReload extends PlaceholderCommand { public final class CommandReload extends PlaceholderCommand {
public CommandReload() { public CommandReload() {
super("reload"); super("reload");
} }
@Override @Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias, @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) { @NotNull @Unmodifiable final List<String> params) {
plugin.reloadConf(sender); plugin.reloadConf(sender);
} }
} }

View File

@@ -21,6 +21,7 @@
package me.clip.placeholderapi.commands.impl.local; package me.clip.placeholderapi.commands.impl.local;
import java.util.List; import java.util.List;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.commands.PlaceholderCommand;
import me.clip.placeholderapi.util.Msg; import me.clip.placeholderapi.util.Msg;
@@ -31,22 +32,22 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CommandVersion extends PlaceholderCommand { public final class CommandVersion extends PlaceholderCommand {
public CommandVersion() { public CommandVersion() {
super("version"); super("version");
} }
@Override @Override
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, public void evaluate(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CommandSender sender, @NotNull final String alias, @NotNull final CommandSender sender, @NotNull final String alias,
@NotNull @Unmodifiable final List<String> params) { @NotNull @Unmodifiable final List<String> params) {
final PluginDescriptionFile description = plugin.getDescription(); final PluginDescriptionFile description = plugin.getDescription();
Msg.msg(sender, Msg.msg(sender,
"&b&lPlaceholderAPI &7(&f" + description.getVersion() + "&7)", "&b&lPlaceholderAPI &7(&f" + description.getVersion() + "&7)",
"&7Author: &f" + description.getAuthors(), "&7Author: &f" + description.getAuthors(),
"&7PAPI Commands: &b/papi &fhelp", "&7PAPI Commands: &b/papi &fhelp",
"&7eCloud Commands&8: &b/papi &fecloud"); "&7eCloud Commands&8: &b/papi &fecloud");
} }
} }

View File

@@ -29,61 +29,61 @@ import org.jetbrains.annotations.NotNull;
/** /**
* This event indicates that a <b>single</b> {@link PlaceholderExpansion PlaceholderExpansion} has * This event indicates that a <b>single</b> {@link PlaceholderExpansion PlaceholderExpansion} has
* been registered in PlaceholderAPI. * been registered in PlaceholderAPI.
* *
* <p>To know when <b>all</b> Expansions have been registered, use the * <p>To know when <b>all</b> Expansions have been registered, use the
* {@link me.clip.placeholderapi.events.ExpansionsLoadedEvent ExpansionsLoadedEvent} instead. * {@link me.clip.placeholderapi.events.ExpansionsLoadedEvent ExpansionsLoadedEvent} instead.
*/ */
public final class ExpansionRegisterEvent extends Event implements Cancellable { public final class ExpansionRegisterEvent extends Event implements Cancellable {
@NotNull @NotNull
private static final HandlerList HANDLERS = new HandlerList(); private static final HandlerList HANDLERS = new HandlerList();
@NotNull @NotNull
private final PlaceholderExpansion expansion; private final PlaceholderExpansion expansion;
private boolean cancelled; private boolean cancelled;
public ExpansionRegisterEvent(@NotNull final PlaceholderExpansion expansion) { public ExpansionRegisterEvent(@NotNull final PlaceholderExpansion expansion) {
this.expansion = expansion; this.expansion = expansion;
} }
@NotNull @NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return HANDLERS; return HANDLERS;
} }
/**
* The {@link PlaceholderExpansion PlaceholderExpansion} that was registered in PlaceholderAPI.
* <br>The PlaceholderExpansion will be available for use when the event
* {@link #isCancelled() was not cancelled}!
*
* @return Current instance of the registered {@link PlaceholderExpansion PlaceholderExpansion}
*/
@NotNull
public PlaceholderExpansion getExpansion() {
return expansion;
}
/** /**
* Indicates if this event was cancelled or not. * The {@link PlaceholderExpansion PlaceholderExpansion} that was registered in PlaceholderAPI.
* <br>A cancelled Event will result in the {@link #getExpansion() PlaceholderExpansion} NOT * <br>The PlaceholderExpansion will be available for use when the event
* being added to PlaceholderAPI's internal list and will therefore be considered not registered * {@link #isCancelled() was not cancelled}!
* anymore. *
* * @return Current instance of the registered {@link PlaceholderExpansion PlaceholderExpansion}
* @return Whether the event has been cancelled or not. */
*/ @NotNull
@Override public PlaceholderExpansion getExpansion() {
public boolean isCancelled() { return expansion;
return cancelled; }
}
@Override /**
public void setCancelled(boolean cancelled) { * Indicates if this event was cancelled or not.
this.cancelled = cancelled; * <br>A cancelled Event will result in the {@link #getExpansion() PlaceholderExpansion} NOT
} * being added to PlaceholderAPI's internal list and will therefore be considered not registered
* anymore.
*
* @return Whether the event has been cancelled or not.
*/
@Override
public boolean isCancelled() {
return cancelled;
}
@NotNull @Override
@Override public void setCancelled(boolean cancelled) {
public HandlerList getHandlers() { this.cancelled = cancelled;
return HANDLERS; }
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
} }

View File

@@ -28,7 +28,7 @@ import org.jetbrains.annotations.NotNull;
/** /**
* This event indicates that a {@link PlaceholderExpansion PlaceholderExpansion} has been * This event indicates that a {@link PlaceholderExpansion PlaceholderExpansion} has been
* unregistered by PlaceholderAPI. * unregistered by PlaceholderAPI.
* *
* <p>Note that this event is triggered <b>before</b> the PlaceholderExpansion is completely * <p>Note that this event is triggered <b>before</b> the PlaceholderExpansion is completely
* removed. * removed.
* <br>This includes removing any Listeners, stopping active tasks and clearing the cache of * <br>This includes removing any Listeners, stopping active tasks and clearing the cache of
@@ -36,35 +36,35 @@ import org.jetbrains.annotations.NotNull;
*/ */
public final class ExpansionUnregisterEvent extends Event { public final class ExpansionUnregisterEvent extends Event {
@NotNull @NotNull
private static final HandlerList HANDLERS = new HandlerList(); private static final HandlerList HANDLERS = new HandlerList();
@NotNull
private final PlaceholderExpansion expansion;
public ExpansionUnregisterEvent(@NotNull final PlaceholderExpansion expansion) { @NotNull
this.expansion = expansion; private final PlaceholderExpansion expansion;
}
@NotNull public ExpansionUnregisterEvent(@NotNull final PlaceholderExpansion expansion) {
public static HandlerList getHandlerList() { this.expansion = expansion;
return HANDLERS; }
}
/**
* The {@link PlaceholderExpansion PlaceholderExpansion} that was unregistered.
*
* @return The {@link PlaceholderExpansion PlaceholderExpansion} instance.
*/
@NotNull
public PlaceholderExpansion getExpansion() {
return expansion;
}
@NotNull @NotNull
@Override public static HandlerList getHandlerList() {
public HandlerList getHandlers() { return HANDLERS;
return HANDLERS; }
}
/**
* The {@link PlaceholderExpansion PlaceholderExpansion} that was unregistered.
*
* @return The {@link PlaceholderExpansion PlaceholderExpansion} instance.
*/
@NotNull
public PlaceholderExpansion getExpansion() {
return expansion;
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
} }

View File

@@ -23,6 +23,7 @@ package me.clip.placeholderapi.events;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@@ -32,14 +33,14 @@ import org.jetbrains.annotations.NotNull;
* This event indicated that <b>all</b> {@link PlaceholderExpansion PlaceholderExpansions} have * This event indicated that <b>all</b> {@link PlaceholderExpansion PlaceholderExpansions} have
* been registered in PlaceholderAPI and can now be used. * been registered in PlaceholderAPI and can now be used.
* <br>This even will also be triggered whenever PlaceholderAPI gets reloaded. * <br>This even will also be triggered whenever PlaceholderAPI gets reloaded.
* *
* <p>All PlaceholderExpansions, except for those loaded by plugins, are loaded * <p>All PlaceholderExpansions, except for those loaded by plugins, are loaded
* after Spigot triggered its ServerLoadEvent (1.13+), or after PlaceholderAPI has been enabled. * after Spigot triggered its ServerLoadEvent (1.13+), or after PlaceholderAPI has been enabled.
*/ */
public class ExpansionsLoadedEvent extends Event { public class ExpansionsLoadedEvent extends Event {
private final List<PlaceholderExpansion> expansions; private final List<PlaceholderExpansion> expansions;
public ExpansionsLoadedEvent(List<PlaceholderExpansion> expansions) { public ExpansionsLoadedEvent(List<PlaceholderExpansion> expansions) {
this.expansions = Collections.unmodifiableList(expansions); this.expansions = Collections.unmodifiableList(expansions);
} }
@@ -47,16 +48,16 @@ public class ExpansionsLoadedEvent extends Event {
/** /**
* Returns a unmodifiable list of {@link PlaceholderExpansion PlaceholderExpansions} that * Returns a unmodifiable list of {@link PlaceholderExpansion PlaceholderExpansions} that
* have been registered by PlaceholderAPI. * have been registered by PlaceholderAPI.
* *
* <p><b>This list does not include manually registered PlaceholderExpansions.</b> * <p><b>This list does not include manually registered PlaceholderExpansions.</b>
* *
* @return List of {@link PlaceholderExpansion registered PlaceholderExpansions}. * @return List of {@link PlaceholderExpansion registered PlaceholderExpansions}.
*/ */
@NotNull @NotNull
public final List<PlaceholderExpansion> getExpansions(){ public final List<PlaceholderExpansion> getExpansions() {
return expansions; return expansions;
} }
@NotNull @NotNull
private static final HandlerList HANDLERS = new HandlerList(); private static final HandlerList HANDLERS = new HandlerList();

View File

@@ -31,40 +31,40 @@ import org.jetbrains.annotations.NotNull;
@Deprecated @Deprecated
public final class PlaceholderHookUnloadEvent extends Event { public final class PlaceholderHookUnloadEvent extends Event {
@NotNull @NotNull
private static final HandlerList HANDLERS = new HandlerList(); private static final HandlerList HANDLERS = new HandlerList();
@NotNull @NotNull
private final String plugin; private final String plugin;
@NotNull @NotNull
private final PlaceholderHook placeholderHook; private final PlaceholderHook placeholderHook;
public PlaceholderHookUnloadEvent(@NotNull final String plugin, public PlaceholderHookUnloadEvent(@NotNull final String plugin,
@NotNull final PlaceholderHook placeholderHook) { @NotNull final PlaceholderHook placeholderHook) {
this.plugin = plugin; this.plugin = plugin;
this.placeholderHook = placeholderHook; this.placeholderHook = placeholderHook;
} }
@NotNull @NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return HANDLERS; return HANDLERS;
} }
@NotNull @NotNull
public String getHookName() { public String getHookName() {
return plugin; return plugin;
} }
@NotNull @NotNull
public PlaceholderHook getHook() { public PlaceholderHook getHook() {
return placeholderHook; return placeholderHook;
} }
@NotNull @NotNull
@Override @Override
public HandlerList getHandlers() { public HandlerList getHandlers() {
return HANDLERS; return HANDLERS;
} }
} }

View File

@@ -23,24 +23,24 @@ package me.clip.placeholderapi.expansion;
@Deprecated @Deprecated
public final class Version { public final class Version {
private final boolean isSpigot; private final boolean isSpigot;
private final String version; private final String version;
public Version(String version, boolean isSpigot) { public Version(String version, boolean isSpigot) {
this.version = version; this.version = version;
this.isSpigot = isSpigot; this.isSpigot = isSpigot;
} }
public String getVersion() { public String getVersion() {
return version == null ? "unknown" : version; return version == null ? "unknown" : version;
} }
public boolean isSpigot() { public boolean isSpigot() {
return isSpigot; return isSpigot;
} }
public boolean compareTo(String version) { public boolean compareTo(String version) {
return getVersion().equalsIgnoreCase(version); return getVersion().equalsIgnoreCase(version);
} }
} }

View File

@@ -23,180 +23,181 @@ package me.clip.placeholderapi.expansion.cloud;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import me.clip.placeholderapi.util.TimeUtil; import me.clip.placeholderapi.util.TimeUtil;
public class CloudExpansion { public class CloudExpansion {
private String name, private String name,
author, author,
latest_version, latest_version,
description, description,
source_url, source_url,
dependency_url; dependency_url;
private boolean hasExpansion, private boolean hasExpansion,
shouldUpdate, shouldUpdate,
verified; verified;
private long last_update, private long last_update,
ratings_count; ratings_count;
private double average_rating; private double average_rating;
private List<String> placeholders; private List<String> placeholders;
private List<Version> versions; private List<Version> versions;
public CloudExpansion() { public CloudExpansion() {
}
public String getTimeSinceLastUpdate() {
int time = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - getLastUpdate());
return TimeUtil.getTime(time);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Version getVersion() {
return getLatestVersion() == null ? null : getVersion(getLatestVersion());
}
public Version getVersion(String version) {
return versions == null ? null : versions.stream()
.filter(v -> v.getVersion().equals(version))
.findFirst()
.orElse(null);
}
public List<String> getAvailableVersions() {
return versions.stream().map(Version::getVersion).collect(Collectors.toList());
}
public String getLatestVersion() {
return latest_version;
}
public void setLatestVersion(String latest_version) {
this.latest_version = latest_version;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getSourceUrl() {
return source_url;
}
public void setSourceUrl(String source_url) {
this.source_url = source_url;
}
public String getDependencyUrl() {
return dependency_url;
}
public void setDependencyUrl(String dependency_url) {
this.dependency_url = dependency_url;
}
public boolean hasExpansion() {
return hasExpansion;
}
public void setHasExpansion(boolean hasExpansion) {
this.hasExpansion = hasExpansion;
}
public boolean shouldUpdate() {
return shouldUpdate;
}
public void setShouldUpdate(boolean shouldUpdate) {
this.shouldUpdate = shouldUpdate;
}
public boolean isVerified() {
return verified;
}
public long getLastUpdate() {
return last_update;
}
public void setLastUpdate(long last_update) {
this.last_update = last_update;
}
public long getRatingsCount() {
return ratings_count;
}
public double getAverage_rating() {
return average_rating;
}
public List<String> getPlaceholders() {
return placeholders;
}
public void setPlaceholders(List<String> placeholders) {
this.placeholders = placeholders;
}
public List<Version> getVersions() {
return versions;
}
public void setVersions(List<Version> versions) {
this.versions = versions;
}
public static class Version {
private String url, version, release_notes;
public String getUrl() {
return url;
} }
public void setUrl(String url) { public String getTimeSinceLastUpdate() {
this.url = url; int time = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - getLastUpdate());
return TimeUtil.getTime(time);
} }
public String getVersion() { public String getName() {
return version; return name;
} }
public void setVersion(String version) { public void setName(String name) {
this.version = version; this.name = name;
} }
public String getReleaseNotes() { public String getAuthor() {
return release_notes; return author;
} }
public void setReleaseNotes(String release_notes) { public void setAuthor(String author) {
this.release_notes = release_notes; this.author = author;
}
public Version getVersion() {
return getLatestVersion() == null ? null : getVersion(getLatestVersion());
}
public Version getVersion(String version) {
return versions == null ? null : versions.stream()
.filter(v -> v.getVersion().equals(version))
.findFirst()
.orElse(null);
}
public List<String> getAvailableVersions() {
return versions.stream().map(Version::getVersion).collect(Collectors.toList());
}
public String getLatestVersion() {
return latest_version;
}
public void setLatestVersion(String latest_version) {
this.latest_version = latest_version;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getSourceUrl() {
return source_url;
}
public void setSourceUrl(String source_url) {
this.source_url = source_url;
}
public String getDependencyUrl() {
return dependency_url;
}
public void setDependencyUrl(String dependency_url) {
this.dependency_url = dependency_url;
}
public boolean hasExpansion() {
return hasExpansion;
}
public void setHasExpansion(boolean hasExpansion) {
this.hasExpansion = hasExpansion;
}
public boolean shouldUpdate() {
return shouldUpdate;
}
public void setShouldUpdate(boolean shouldUpdate) {
this.shouldUpdate = shouldUpdate;
}
public boolean isVerified() {
return verified;
}
public long getLastUpdate() {
return last_update;
}
public void setLastUpdate(long last_update) {
this.last_update = last_update;
}
public long getRatingsCount() {
return ratings_count;
}
public double getAverage_rating() {
return average_rating;
}
public List<String> getPlaceholders() {
return placeholders;
}
public void setPlaceholders(List<String> placeholders) {
this.placeholders = placeholders;
}
public List<Version> getVersions() {
return versions;
}
public void setVersions(List<Version> versions) {
this.versions = versions;
}
public static class Version {
private String url, version, release_notes;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getReleaseNotes() {
return release_notes;
}
public void setReleaseNotes(String release_notes) {
this.release_notes = release_notes;
}
} }
}
} }

View File

@@ -25,6 +25,7 @@ import com.google.common.io.Resources;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@@ -50,6 +51,7 @@ import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.expansion.cloud.CloudExpansion; import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
@@ -59,218 +61,218 @@ import org.jetbrains.annotations.Unmodifiable;
public final class CloudExpansionManager { public final class CloudExpansionManager {
@NotNull @NotNull
private static final String API_URL = "http://api.extendedclip.com/v2/"; private static final String API_URL = "http://api.extendedclip.com/v2/";
@NotNull @NotNull
private static final Gson GSON = new Gson(); private static final Gson GSON = new Gson();
@NotNull @NotNull
private static final Type TYPE = new TypeToken<Map<String, CloudExpansion>>() {}.getType(); private static final Type TYPE = new TypeToken<Map<String, CloudExpansion>>() {}.getType();
@NotNull @NotNull
private final Collector<CloudExpansion, ?, Map<String, CloudExpansion>> INDEXED_NAME_COLLECTOR = Collectors private final Collector<CloudExpansion, ?, Map<String, CloudExpansion>> INDEXED_NAME_COLLECTOR = Collectors
.toMap(CloudExpansionManager::toIndexName, Function.identity()); .toMap(CloudExpansionManager::toIndexName, Function.identity());
@NotNull @NotNull
private final PlaceholderAPIPlugin plugin; private final PlaceholderAPIPlugin plugin;
@NotNull @NotNull
private final Map<String, CloudExpansion> cache = new HashMap<>(); private final Map<String, CloudExpansion> cache = new HashMap<>();
@NotNull @NotNull
private final Map<String, CompletableFuture<File>> await = new ConcurrentHashMap<>(); private final Map<String, CompletableFuture<File>> await = new ConcurrentHashMap<>();
private final ExecutorService ASYNC_EXECUTOR = private final ExecutorService ASYNC_EXECUTOR =
Executors.newCachedThreadPool( Executors.newCachedThreadPool(
new ThreadFactoryBuilder().setNameFormat("placeholderapi-io-#%1$d").build()); new ThreadFactoryBuilder().setNameFormat("placeholderapi-io-#%1$d").build());
public CloudExpansionManager(@NotNull final PlaceholderAPIPlugin plugin) { public CloudExpansionManager(@NotNull final PlaceholderAPIPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
}
@NotNull
private static String toIndexName(@NotNull final String name) {
return name.toLowerCase(Locale.ROOT).replace(' ', '_');
}
@NotNull
private static String toIndexName(@NotNull final CloudExpansion expansion) {
return toIndexName(expansion.getName());
}
public void load() {
clean();
fetch();
}
public void kill() {
clean();
}
@NotNull
@Unmodifiable
public Map<String, CloudExpansion> getCloudExpansions() {
return ImmutableMap.copyOf(cache);
}
@NotNull
@Unmodifiable
public Map<String, CloudExpansion> getCloudExpansionsInstalled() {
if (cache.isEmpty()) {
return Collections.emptyMap();
} }
return cache.values() @NotNull
.stream() private static String toIndexName(@NotNull final String name) {
.filter(CloudExpansion::hasExpansion) return name.toLowerCase(Locale.ROOT).replace(' ', '_');
.collect(INDEXED_NAME_COLLECTOR);
}
@NotNull
@Unmodifiable
public Map<String, CloudExpansion> getCloudExpansionsByAuthor(@NotNull final String author) {
if (cache.isEmpty()) {
return Collections.emptyMap();
} }
return cache.values() @NotNull
.stream() private static String toIndexName(@NotNull final CloudExpansion expansion) {
.filter(expansion -> author.equalsIgnoreCase(expansion.getAuthor())) return toIndexName(expansion.getName());
.collect(INDEXED_NAME_COLLECTOR); }
}
@NotNull public void load() {
@Unmodifiable clean();
public Set<String> getCloudExpansionAuthors() { fetch();
return cache.values().stream().map(CloudExpansion::getAuthor).collect(Collectors.toSet()); }
}
public int getCloudExpansionAuthorCount() { public void kill() {
return getCloudExpansionAuthors().size(); clean();
} }
public int getCloudUpdateCount() { @NotNull
return ((int) plugin.getLocalExpansionManager() @Unmodifiable
.getExpansions() public Map<String, CloudExpansion> getCloudExpansions() {
.stream() return ImmutableMap.copyOf(cache);
.filter(expansion -> findCloudExpansionByName(expansion.getName()) }
.map(CloudExpansion::shouldUpdate).orElse(false))
.count());
}
@NotNull @NotNull
public Optional<CloudExpansion> findCloudExpansionByName(@NotNull final String name) { @Unmodifiable
return Optional.ofNullable(cache.get(toIndexName(name))); public Map<String, CloudExpansion> getCloudExpansionsInstalled() {
} if (cache.isEmpty()) {
return Collections.emptyMap();
}
public void clean() { return cache.values()
cache.clear(); .stream()
.filter(CloudExpansion::hasExpansion)
.collect(INDEXED_NAME_COLLECTOR);
}
await.values().forEach(future -> future.cancel(true)); @NotNull
await.clear(); @Unmodifiable
} public Map<String, CloudExpansion> getCloudExpansionsByAuthor(@NotNull final String author) {
if (cache.isEmpty()) {
return Collections.emptyMap();
}
public void fetch() { return cache.values()
plugin.getLogger().info("Fetching available expansion information..."); .stream()
.filter(expansion -> author.equalsIgnoreCase(expansion.getAuthor()))
.collect(INDEXED_NAME_COLLECTOR);
}
ASYNC_EXECUTOR.submit( @NotNull
() -> { @Unmodifiable
// a defence tactic! use ConcurrentHashMap instead of normal HashMap public Set<String> getCloudExpansionAuthors() {
Map<String, CloudExpansion> values = new ConcurrentHashMap<>(); return cache.values().stream().map(CloudExpansion::getAuthor).collect(Collectors.toSet());
try { }
//noinspection UnstableApiUsage
String json = Resources.toString(new URL(API_URL), StandardCharsets.UTF_8);
values.putAll(GSON.fromJson(json, TYPE));
List<String> toRemove = new ArrayList<>(); public int getCloudExpansionAuthorCount() {
return getCloudExpansionAuthors().size();
}
for (Map.Entry<String, CloudExpansion> entry : values.entrySet()) { public int getCloudUpdateCount() {
CloudExpansion expansion = entry.getValue(); return ((int) plugin.getLocalExpansionManager()
if (expansion.getLatestVersion() == null .getExpansions()
|| expansion.getVersion(expansion.getLatestVersion()) == null) { .stream()
toRemove.add(entry.getKey()); .filter(expansion -> findCloudExpansionByName(expansion.getName())
} .map(CloudExpansion::shouldUpdate).orElse(false))
} .count());
}
for (String name : toRemove) { @NotNull
values.remove(name); public Optional<CloudExpansion> findCloudExpansionByName(@NotNull final String name) {
} return Optional.ofNullable(cache.get(toIndexName(name)));
} catch (Throwable e) { }
// ugly swallowing of every throwable, but we have to be defensive
plugin.getLogger().log(Level.WARNING, "Failed to download expansion information", e);
}
// loop through what's left on the main thread public void clean() {
plugin cache.clear();
.getScheduler()
.runTask( await.values().forEach(future -> future.cancel(true));
() -> { await.clear();
}
public void fetch() {
plugin.getLogger().info("Fetching available expansion information...");
ASYNC_EXECUTOR.submit(
() -> {
// a defence tactic! use ConcurrentHashMap instead of normal HashMap
Map<String, CloudExpansion> values = new ConcurrentHashMap<>();
try { try {
for (Map.Entry<String, CloudExpansion> entry : values.entrySet()) { //noinspection UnstableApiUsage
String name = entry.getKey(); String json = Resources.toString(new URL(API_URL), StandardCharsets.UTF_8);
CloudExpansion expansion = entry.getValue(); values.putAll(GSON.fromJson(json, TYPE));
expansion.setName(name); List<String> toRemove = new ArrayList<>();
Optional<PlaceholderExpansion> localOpt = for (Map.Entry<String, CloudExpansion> entry : values.entrySet()) {
plugin.getLocalExpansionManager().findExpansionByName(name); CloudExpansion expansion = entry.getValue();
if (localOpt.isPresent()) { if (expansion.getLatestVersion() == null
PlaceholderExpansion local = localOpt.get(); || expansion.getVersion(expansion.getLatestVersion()) == null) {
if (local.isRegistered()) { toRemove.add(entry.getKey());
expansion.setHasExpansion(true); }
expansion.setShouldUpdate(
!local.getVersion().equalsIgnoreCase(expansion.getLatestVersion()));
}
} }
cache.put(toIndexName(expansion), expansion); for (String name : toRemove) {
} values.remove(name);
}
} catch (Throwable e) { } catch (Throwable e) {
// ugly swallowing of every throwable, but we have to be defensive // ugly swallowing of every throwable, but we have to be defensive
plugin plugin.getLogger().log(Level.WARNING, "Failed to download expansion information", e);
.getLogger()
.log(Level.WARNING, "Failed to download expansion information", e);
} }
});
});
}
public boolean isDownloading(@NotNull final CloudExpansion expansion) { // loop through what's left on the main thread
return await.containsKey(toIndexName(expansion)); plugin
} .getScheduler()
.runTask(
() -> {
try {
for (Map.Entry<String, CloudExpansion> entry : values.entrySet()) {
String name = entry.getKey();
CloudExpansion expansion = entry.getValue();
@NotNull expansion.setName(name);
public CompletableFuture<File> downloadExpansion(@NotNull final CloudExpansion expansion,
@NotNull final CloudExpansion.Version version) { Optional<PlaceholderExpansion> localOpt =
final CompletableFuture<File> previous = await.get(toIndexName(expansion)); plugin.getLocalExpansionManager().findExpansionByName(name);
if (previous != null) { if (localOpt.isPresent()) {
return previous; PlaceholderExpansion local = localOpt.get();
if (local.isRegistered()) {
expansion.setHasExpansion(true);
expansion.setShouldUpdate(
!local.getVersion().equalsIgnoreCase(expansion.getLatestVersion()));
}
}
cache.put(toIndexName(expansion), expansion);
}
} catch (Throwable e) {
// ugly swallowing of every throwable, but we have to be defensive
plugin
.getLogger()
.log(Level.WARNING, "Failed to download expansion information", e);
}
});
});
} }
final File file = new File(plugin.getLocalExpansionManager().getExpansionsFolder(), public boolean isDownloading(@NotNull final CloudExpansion expansion) {
"Expansion-" + toIndexName(expansion) + ".jar"); return await.containsKey(toIndexName(expansion));
}
final CompletableFuture<File> download = CompletableFuture.supplyAsync(() -> { @NotNull
try (final ReadableByteChannel source = Channels.newChannel(new URL(version.getUrl()) public CompletableFuture<File> downloadExpansion(@NotNull final CloudExpansion expansion,
.openStream()); final FileOutputStream target = new FileOutputStream(file)) { @NotNull final CloudExpansion.Version version) {
target.getChannel().transferFrom(source, 0, Long.MAX_VALUE); final CompletableFuture<File> previous = await.get(toIndexName(expansion));
} catch (final IOException ex) { if (previous != null) {
throw new CompletionException(ex); return previous;
} }
return file;
}, ASYNC_EXECUTOR);
download.whenCompleteAsync((value, exception) -> { final File file = new File(plugin.getLocalExpansionManager().getExpansionsFolder(),
await.remove(toIndexName(expansion)); "Expansion-" + toIndexName(expansion) + ".jar");
if (exception != null) { final CompletableFuture<File> download = CompletableFuture.supplyAsync(() -> {
Msg.severe("Failed to download %s:%s", exception, expansion.getName(), expansion.getVersion()); try (final ReadableByteChannel source = Channels.newChannel(new URL(version.getUrl())
} .openStream()); final FileOutputStream target = new FileOutputStream(file)) {
}, ASYNC_EXECUTOR); target.getChannel().transferFrom(source, 0, Long.MAX_VALUE);
} catch (final IOException ex) {
throw new CompletionException(ex);
}
return file;
}, ASYNC_EXECUTOR);
await.put(toIndexName(expansion), download); download.whenCompleteAsync((value, exception) -> {
await.remove(toIndexName(expansion));
return download; if (exception != null) {
} Msg.severe("Failed to download %s:%s", exception, expansion.getName(), expansion.getVersion());
}
}, ASYNC_EXECUTOR);
await.put(toIndexName(expansion), download);
return download;
}
} }

View File

@@ -22,6 +22,7 @@ package me.clip.placeholderapi.expansion.manager;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import java.io.File; import java.io.File;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Arrays; import java.util.Arrays;
@@ -38,6 +39,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.events.ExpansionRegisterEvent; import me.clip.placeholderapi.events.ExpansionRegisterEvent;
import me.clip.placeholderapi.events.ExpansionUnregisterEvent; import me.clip.placeholderapi.events.ExpansionUnregisterEvent;
@@ -68,428 +70,429 @@ import org.jetbrains.annotations.Unmodifiable;
public final class LocalExpansionManager implements Listener { public final class LocalExpansionManager implements Listener {
@NotNull @NotNull
private static final String EXPANSIONS_FOLDER_NAME = "expansions"; private static final String EXPANSIONS_FOLDER_NAME = "expansions";
@NotNull @NotNull
private static final Set<MethodSignature> ABSTRACT_EXPANSION_METHODS = Arrays.stream(PlaceholderExpansion.class.getDeclaredMethods()) private static final Set<MethodSignature> ABSTRACT_EXPANSION_METHODS = Arrays.stream(PlaceholderExpansion.class.getDeclaredMethods())
.filter(method -> Modifier.isAbstract(method.getModifiers())) .filter(method -> Modifier.isAbstract(method.getModifiers()))
.map(method -> new MethodSignature(method.getName(), method.getParameterTypes())) .map(method -> new MethodSignature(method.getName(), method.getParameterTypes()))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@NotNull @NotNull
private final File folder; private final File folder;
@NotNull @NotNull
private final PlaceholderAPIPlugin plugin; private final PlaceholderAPIPlugin plugin;
@NotNull @NotNull
private final Map<String, PlaceholderExpansion> expansions = new ConcurrentHashMap<>(); private final Map<String, PlaceholderExpansion> expansions = new ConcurrentHashMap<>();
private final ReentrantLock expansionsLock = new ReentrantLock(); private final ReentrantLock expansionsLock = new ReentrantLock();
public LocalExpansionManager(@NotNull final PlaceholderAPIPlugin plugin) { public LocalExpansionManager(@NotNull final PlaceholderAPIPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
this.folder = new File(plugin.getDataFolder(), EXPANSIONS_FOLDER_NAME); this.folder = new File(plugin.getDataFolder(), EXPANSIONS_FOLDER_NAME);
if (!this.folder.exists() && !folder.mkdirs()) { if (!this.folder.exists() && !folder.mkdirs()) {
Msg.warn("Failed to create expansions folder!"); Msg.warn("Failed to create expansions folder!");
}
}
public void load(@NotNull final CommandSender sender) {
registerAll(sender);
}
public void kill() {
unregisterAll();
}
@NotNull
public File getExpansionsFolder() {
return folder;
}
@NotNull
@Unmodifiable
public Collection<String> getIdentifiers() {
expansionsLock.lock();
try {
return ImmutableSet.copyOf(expansions.keySet());
} finally {
expansionsLock.unlock();
}
}
@NotNull
@Unmodifiable
public Collection<PlaceholderExpansion> getExpansions() {
expansionsLock.lock();
try {
return ImmutableSet.copyOf(expansions.values());
} finally {
expansionsLock.unlock();
}
}
@Nullable
public PlaceholderExpansion getExpansion(@NotNull final String identifier) {
expansionsLock.lock();
try {
return expansions.get(identifier.toLowerCase(Locale.ROOT));
} finally {
expansionsLock.unlock();
}
}
@NotNull
public Optional<PlaceholderExpansion> findExpansionByName(@NotNull final String name) {
expansionsLock.lock();
try {
PlaceholderExpansion bestMatch = null;
for (Map.Entry<String, PlaceholderExpansion> entry : expansions.entrySet()) {
PlaceholderExpansion expansion = entry.getValue();
if (expansion.getName().equalsIgnoreCase(name)) {
bestMatch = expansion;
break;
} }
}
return Optional.ofNullable(bestMatch);
} finally {
expansionsLock.unlock();
} }
}
@NotNull public void load(@NotNull final CommandSender sender) {
public Optional<PlaceholderExpansion> findExpansionByIdentifier( registerAll(sender);
@NotNull final String identifier) { }
return Optional.ofNullable(getExpansion(identifier));
} public void kill() {
unregisterAll();
}
public Optional<PlaceholderExpansion> register( @NotNull
@NotNull final Class<? extends PlaceholderExpansion> clazz) { public File getExpansionsFolder() {
try { return folder;
final PlaceholderExpansion expansion = createExpansionInstance(clazz); }
if(expansion == null){ @NotNull
return Optional.empty(); @Unmodifiable
} public Collection<String> getIdentifiers() {
expansionsLock.lock();
Objects.requireNonNull(expansion.getAuthor(), "The expansion author is null!"); try {
Objects.requireNonNull(expansion.getIdentifier(), "The expansion identifier is null!"); return ImmutableSet.copyOf(expansions.keySet());
Objects.requireNonNull(expansion.getVersion(), "The expansion version is null!"); } finally {
expansionsLock.unlock();
if (expansion.getRequiredPlugin() != null && !expansion.getRequiredPlugin().isEmpty()) {
if (!Bukkit.getPluginManager().isPluginEnabled(expansion.getRequiredPlugin())) {
Msg.warn("Cannot load expansion %s due to a missing plugin: %s", expansion.getIdentifier(),
expansion.getRequiredPlugin());
return Optional.empty();
} }
}
expansion.setExpansionType(PlaceholderExpansion.Type.EXTERNAL);
if (!expansion.register()) {
Msg.warn("Cannot load expansion %s due to an unknown issue.", expansion.getIdentifier());
return Optional.empty();
}
return Optional.of(expansion);
} catch (LinkageError | NullPointerException ex) {
final String reason;
if (ex instanceof LinkageError) {
reason = " (Is a dependency missing?)";
} else {
reason = " - One of its properties is null which is not allowed!";
}
Msg.severe("Failed to load expansion class %s%s", ex, clazz.getSimpleName(), reason);
} }
return Optional.empty(); @NotNull
} @Unmodifiable
public Collection<PlaceholderExpansion> getExpansions() {
/** expansionsLock.lock();
* Attempt to register a {@link PlaceholderExpansion} try {
* @param expansion the expansion to register return ImmutableSet.copyOf(expansions.values());
* @return if the expansion was registered } finally {
*/ expansionsLock.unlock();
@ApiStatus.Internal }
public boolean register(@NotNull final PlaceholderExpansion expansion) {
final String identifier = expansion.getIdentifier().toLowerCase(Locale.ROOT);
if (!expansion.canRegister()) {
return false;
} }
// Avoid loading two external expansions with the same identifier @Nullable
if (expansion.getExpansionType() == PlaceholderExpansion.Type.EXTERNAL && expansions.containsKey(identifier)) { public PlaceholderExpansion getExpansion(@NotNull final String identifier) {
Msg.warn("Failed to load external expansion %s. Identifier is already in use.", expansion.getIdentifier()); expansionsLock.lock();
return false; try {
return expansions.get(identifier.toLowerCase(Locale.ROOT));
} finally {
expansionsLock.unlock();
}
} }
if (expansion instanceof Configurable) { @NotNull
Map<String, Object> defaults = ((Configurable) expansion).getDefaults(); public Optional<PlaceholderExpansion> findExpansionByName(@NotNull final String name) {
String pre = "expansions." + identifier + "."; expansionsLock.lock();
FileConfiguration cfg = plugin.getConfig(); try {
boolean save = false; PlaceholderExpansion bestMatch = null;
for (Map.Entry<String, PlaceholderExpansion> entry : expansions.entrySet()) {
if (defaults != null) { PlaceholderExpansion expansion = entry.getValue();
for (Map.Entry<String, Object> entries : defaults.entrySet()) { if (expansion.getName().equalsIgnoreCase(name)) {
if (entries.getKey() == null || entries.getKey().isEmpty()) { bestMatch = expansion;
continue; break;
} }
if (entries.getValue() == null) {
if (cfg.contains(pre + entries.getKey())) {
save = true;
cfg.set(pre + entries.getKey(), null);
} }
} else { return Optional.ofNullable(bestMatch);
if (!cfg.contains(pre + entries.getKey())) { } finally {
save = true; expansionsLock.unlock();
cfg.set(pre + entries.getKey(), entries.getValue()); }
}
@NotNull
public Optional<PlaceholderExpansion> findExpansionByIdentifier(
@NotNull final String identifier) {
return Optional.ofNullable(getExpansion(identifier));
}
public Optional<PlaceholderExpansion> register(
@NotNull final Class<? extends PlaceholderExpansion> clazz) {
try {
final PlaceholderExpansion expansion = createExpansionInstance(clazz);
if (expansion == null) {
return Optional.empty();
} }
}
}
}
if (save) { Objects.requireNonNull(expansion.getAuthor(), "The expansion author is null!");
plugin.saveConfig(); Objects.requireNonNull(expansion.getIdentifier(), "The expansion identifier is null!");
plugin.reloadConfig(); Objects.requireNonNull(expansion.getVersion(), "The expansion version is null!");
}
}
if (expansion instanceof VersionSpecific) { if (expansion.getRequiredPlugin() != null && !expansion.getRequiredPlugin().isEmpty()) {
VersionSpecific nms = (VersionSpecific) expansion; if (!Bukkit.getPluginManager().isPluginEnabled(expansion.getRequiredPlugin())) {
if (!nms.isCompatibleWith(PlaceholderAPIPlugin.getServerVersion())) { Msg.warn("Cannot load expansion %s due to a missing plugin: %s", expansion.getIdentifier(),
Msg.warn("Your server version is incompatible with expansion %s %s", expansion.getRequiredPlugin());
expansion.getIdentifier(), expansion.getVersion()); return Optional.empty();
return false; }
} }
}
final PlaceholderExpansion removed = getExpansion(identifier); expansion.setExpansionType(PlaceholderExpansion.Type.EXTERNAL);
if (removed != null && !removed.unregister()) {
return false;
}
final ExpansionRegisterEvent event = new ExpansionRegisterEvent(expansion); if (!expansion.register()) {
Bukkit.getPluginManager().callEvent(event); Msg.warn("Cannot load expansion %s due to an unknown issue.", expansion.getIdentifier());
return Optional.empty();
}
if (event.isCancelled()) { return Optional.of(expansion);
return false; } catch (LinkageError | NullPointerException ex) {
} final String reason;
expansionsLock.lock(); if (ex instanceof LinkageError) {
try { reason = " (Is a dependency missing?)";
expansions.put(identifier, expansion); } else {
} finally { reason = " - One of its properties is null which is not allowed!";
expansionsLock.unlock(); }
}
if (expansion instanceof Listener) { Msg.severe("Failed to load expansion class %s%s", ex, clazz.getSimpleName(), reason);
Bukkit.getPluginManager().registerEvents(((Listener) expansion), plugin);
}
Msg.info(
"Successfully registered %s expansion: %s [%s]",
expansion.getExpansionType().name().toLowerCase(),
expansion.getIdentifier(),
expansion.getVersion()
);
if (expansion instanceof Taskable) {
((Taskable) expansion).start();
}
// Check eCloud for updates only if the expansion is external
if (plugin.getPlaceholderAPIConfig().isCloudEnabled() && expansion.getExpansionType() == PlaceholderExpansion.Type.EXTERNAL) {
final Optional<CloudExpansion> cloudExpansionOptional = plugin.getCloudExpansionManager().findCloudExpansionByName(identifier);
if (cloudExpansionOptional.isPresent()) {
CloudExpansion cloudExpansion = cloudExpansionOptional.get();
cloudExpansion.setHasExpansion(true);
cloudExpansion.setShouldUpdate(!cloudExpansion.getLatestVersion().equals(expansion.getVersion()));
}
}
return true;
}
@ApiStatus.Internal
public boolean unregister(@NotNull final PlaceholderExpansion expansion) {
if (expansions.remove(expansion.getIdentifier().toLowerCase(Locale.ROOT)) == null) {
return false;
}
Bukkit.getPluginManager().callEvent(new ExpansionUnregisterEvent(expansion));
if (expansion instanceof Listener) {
HandlerList.unregisterAll((Listener) expansion);
}
if (expansion instanceof Taskable) {
((Taskable) expansion).stop();
}
if (expansion instanceof Cacheable) {
((Cacheable) expansion).clear();
}
if (plugin.getPlaceholderAPIConfig().isCloudEnabled()) {
plugin.getCloudExpansionManager().findCloudExpansionByName(expansion.getName())
.ifPresent(cloud -> {
cloud.setHasExpansion(false);
cloud.setShouldUpdate(false);
});
}
return true;
}
private void registerAll(@NotNull final CommandSender sender) {
Msg.info("Placeholder expansion registration initializing...");
Futures.onMainThread(plugin, findExpansionsOnDisk(), (classes, exception) -> {
if (exception != null) {
Msg.severe("Failed to load class files of expansion.", exception);
return;
}
final List<PlaceholderExpansion> registered = classes.stream()
.filter(Objects::nonNull)
.map(this::register)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
final long needsUpdate = registered.stream()
.map(expansion -> plugin.getCloudExpansionManager().findCloudExpansionByName(expansion.getName()).orElse(null))
.filter(Objects::nonNull)
.filter(CloudExpansion::shouldUpdate)
.count();
StringBuilder message = new StringBuilder(registered.size() == 0 ? "&6" : "&a")
.append(registered.size())
.append(' ')
.append("placeholder hook(s) registered!");
if (needsUpdate > 0) {
message.append(' ')
.append("&6")
.append(needsUpdate)
.append(' ')
.append("placeholder hook(s) have an update available.");
}
Msg.msg(sender, message.toString());
Bukkit.getPluginManager().callEvent(new ExpansionsLoadedEvent(registered));
});
}
private void unregisterAll() {
for (final PlaceholderExpansion expansion : Sets.newHashSet(expansions.values())) {
if (expansion.persist()) {
continue;
}
expansion.unregister();
}
}
@NotNull
public CompletableFuture<@NotNull List<@Nullable Class<? extends PlaceholderExpansion>>> findExpansionsOnDisk() {
File[] files = folder.listFiles((dir, name) -> name.endsWith(".jar"));
if (files == null) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
return Arrays.stream(files)
.map(this::findExpansionInFile)
.collect(Futures.collector());
}
@NotNull
public CompletableFuture<@Nullable Class<? extends PlaceholderExpansion>> findExpansionInFile(
@NotNull final File file) {
return CompletableFuture.supplyAsync(() -> {
try {
final Class<? extends PlaceholderExpansion> expansionClass = FileUtil.findClass(file, PlaceholderExpansion.class);
if (expansionClass == null) {
Msg.severe("Failed to load expansion %s, as it does not have a class which"
+ " extends PlaceholderExpansion", file.getName());
return null;
} }
Set<MethodSignature> expansionMethods = Arrays.stream(expansionClass.getDeclaredMethods()) return Optional.empty();
.map(method -> new MethodSignature(method.getName(), method.getParameterTypes())) }
.collect(Collectors.toSet());
if (!expansionMethods.containsAll(ABSTRACT_EXPANSION_METHODS)) { /**
Msg.severe("Failed to load expansion %s, as it does not have the required" * Attempt to register a {@link PlaceholderExpansion}
+ " methods declared for a PlaceholderExpansion.", file.getName()); *
return null; * @param expansion the expansion to register
* @return if the expansion was registered
*/
@ApiStatus.Internal
public boolean register(@NotNull final PlaceholderExpansion expansion) {
final String identifier = expansion.getIdentifier().toLowerCase(Locale.ROOT);
if (!expansion.canRegister()) {
return false;
} }
return expansionClass; // Avoid loading two external expansions with the same identifier
} catch (VerifyError | NoClassDefFoundError e) { if (expansion.getExpansionType() == PlaceholderExpansion.Type.EXTERNAL && expansions.containsKey(identifier)) {
Msg.severe("Failed to load expansion %s (is a dependency missing?)", e, file.getName()); Msg.warn("Failed to load external expansion %s. Identifier is already in use.", expansion.getIdentifier());
return null; return false;
} catch (Exception e) { }
plugin.getLogger().log(Level.SEVERE, "Failed to load expansion file: " + file.getAbsolutePath(), e);
return null;
}
});
}
if (expansion instanceof Configurable) {
Map<String, Object> defaults = ((Configurable) expansion).getDefaults();
String pre = "expansions." + identifier + ".";
FileConfiguration cfg = plugin.getConfig();
boolean save = false;
@Nullable if (defaults != null) {
public PlaceholderExpansion createExpansionInstance( for (Map.Entry<String, Object> entries : defaults.entrySet()) {
@NotNull final Class<? extends PlaceholderExpansion> clazz) throws LinkageError { if (entries.getKey() == null || entries.getKey().isEmpty()) {
try { continue;
return clazz.getDeclaredConstructor().newInstance(); }
} catch (final Exception ex) {
if (ex.getCause() instanceof LinkageError) {
throw ((LinkageError) ex.getCause());
}
Msg.warn("There was an issue with loading an expansion.");
return null;
}
}
if (entries.getValue() == null) {
if (cfg.contains(pre + entries.getKey())) {
save = true;
cfg.set(pre + entries.getKey(), null);
}
} else {
if (!cfg.contains(pre + entries.getKey())) {
save = true;
cfg.set(pre + entries.getKey(), entries.getValue());
}
}
}
}
@EventHandler if (save) {
public void onQuit(@NotNull final PlayerQuitEvent event) { plugin.saveConfig();
for (final PlaceholderExpansion expansion : getExpansions()) { plugin.reloadConfig();
if (!(expansion instanceof Cleanable)) { }
continue; }
}
((Cleanable) expansion).cleanup(event.getPlayer()); if (expansion instanceof VersionSpecific) {
} VersionSpecific nms = (VersionSpecific) expansion;
} if (!nms.isCompatibleWith(PlaceholderAPIPlugin.getServerVersion())) {
Msg.warn("Your server version is incompatible with expansion %s %s",
expansion.getIdentifier(), expansion.getVersion());
return false;
}
}
@EventHandler(priority = EventPriority.HIGH) final PlaceholderExpansion removed = getExpansion(identifier);
public void onPluginDisable(@NotNull final PluginDisableEvent event) { if (removed != null && !removed.unregister()) {
final String name = event.getPlugin().getName(); return false;
if (name.equals(plugin.getName())) { }
return;
final ExpansionRegisterEvent event = new ExpansionRegisterEvent(expansion);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
expansionsLock.lock();
try {
expansions.put(identifier, expansion);
} finally {
expansionsLock.unlock();
}
if (expansion instanceof Listener) {
Bukkit.getPluginManager().registerEvents(((Listener) expansion), plugin);
}
Msg.info(
"Successfully registered %s expansion: %s [%s]",
expansion.getExpansionType().name().toLowerCase(),
expansion.getIdentifier(),
expansion.getVersion()
);
if (expansion instanceof Taskable) {
((Taskable) expansion).start();
}
// Check eCloud for updates only if the expansion is external
if (plugin.getPlaceholderAPIConfig().isCloudEnabled() && expansion.getExpansionType() == PlaceholderExpansion.Type.EXTERNAL) {
final Optional<CloudExpansion> cloudExpansionOptional = plugin.getCloudExpansionManager().findCloudExpansionByName(identifier);
if (cloudExpansionOptional.isPresent()) {
CloudExpansion cloudExpansion = cloudExpansionOptional.get();
cloudExpansion.setHasExpansion(true);
cloudExpansion.setShouldUpdate(!cloudExpansion.getLatestVersion().equals(expansion.getVersion()));
}
}
return true;
} }
for (final PlaceholderExpansion expansion : getExpansions()) { @ApiStatus.Internal
if (!name.equalsIgnoreCase(expansion.getRequiredPlugin())) { public boolean unregister(@NotNull final PlaceholderExpansion expansion) {
continue; if (expansions.remove(expansion.getIdentifier().toLowerCase(Locale.ROOT)) == null) {
} return false;
}
expansion.unregister(); Bukkit.getPluginManager().callEvent(new ExpansionUnregisterEvent(expansion));
Msg.info("Unregistered placeholder expansion %s", expansion.getIdentifier());
Msg.info("Reason: required plugin %s was disabled.", name); if (expansion instanceof Listener) {
HandlerList.unregisterAll((Listener) expansion);
}
if (expansion instanceof Taskable) {
((Taskable) expansion).stop();
}
if (expansion instanceof Cacheable) {
((Cacheable) expansion).clear();
}
if (plugin.getPlaceholderAPIConfig().isCloudEnabled()) {
plugin.getCloudExpansionManager().findCloudExpansionByName(expansion.getName())
.ifPresent(cloud -> {
cloud.setHasExpansion(false);
cloud.setShouldUpdate(false);
});
}
return true;
}
private void registerAll(@NotNull final CommandSender sender) {
Msg.info("Placeholder expansion registration initializing...");
Futures.onMainThread(plugin, findExpansionsOnDisk(), (classes, exception) -> {
if (exception != null) {
Msg.severe("Failed to load class files of expansion.", exception);
return;
}
final List<PlaceholderExpansion> registered = classes.stream()
.filter(Objects::nonNull)
.map(this::register)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
final long needsUpdate = registered.stream()
.map(expansion -> plugin.getCloudExpansionManager().findCloudExpansionByName(expansion.getName()).orElse(null))
.filter(Objects::nonNull)
.filter(CloudExpansion::shouldUpdate)
.count();
StringBuilder message = new StringBuilder(registered.size() == 0 ? "&6" : "&a")
.append(registered.size())
.append(' ')
.append("placeholder hook(s) registered!");
if (needsUpdate > 0) {
message.append(' ')
.append("&6")
.append(needsUpdate)
.append(' ')
.append("placeholder hook(s) have an update available.");
}
Msg.msg(sender, message.toString());
Bukkit.getPluginManager().callEvent(new ExpansionsLoadedEvent(registered));
});
}
private void unregisterAll() {
for (final PlaceholderExpansion expansion : Sets.newHashSet(expansions.values())) {
if (expansion.persist()) {
continue;
}
expansion.unregister();
}
}
@NotNull
public CompletableFuture<@NotNull List<@Nullable Class<? extends PlaceholderExpansion>>> findExpansionsOnDisk() {
File[] files = folder.listFiles((dir, name) -> name.endsWith(".jar"));
if (files == null) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
return Arrays.stream(files)
.map(this::findExpansionInFile)
.collect(Futures.collector());
}
@NotNull
public CompletableFuture<@Nullable Class<? extends PlaceholderExpansion>> findExpansionInFile(
@NotNull final File file) {
return CompletableFuture.supplyAsync(() -> {
try {
final Class<? extends PlaceholderExpansion> expansionClass = FileUtil.findClass(file, PlaceholderExpansion.class);
if (expansionClass == null) {
Msg.severe("Failed to load expansion %s, as it does not have a class which"
+ " extends PlaceholderExpansion", file.getName());
return null;
}
Set<MethodSignature> expansionMethods = Arrays.stream(expansionClass.getDeclaredMethods())
.map(method -> new MethodSignature(method.getName(), method.getParameterTypes()))
.collect(Collectors.toSet());
if (!expansionMethods.containsAll(ABSTRACT_EXPANSION_METHODS)) {
Msg.severe("Failed to load expansion %s, as it does not have the required"
+ " methods declared for a PlaceholderExpansion.", file.getName());
return null;
}
return expansionClass;
} catch (VerifyError | NoClassDefFoundError e) {
Msg.severe("Failed to load expansion %s (is a dependency missing?)", e, file.getName());
return null;
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Failed to load expansion file: " + file.getAbsolutePath(), e);
return null;
}
});
}
@Nullable
public PlaceholderExpansion createExpansionInstance(
@NotNull final Class<? extends PlaceholderExpansion> clazz) throws LinkageError {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (final Exception ex) {
if (ex.getCause() instanceof LinkageError) {
throw ((LinkageError) ex.getCause());
}
Msg.warn("There was an issue with loading an expansion.");
return null;
}
}
@EventHandler
public void onQuit(@NotNull final PlayerQuitEvent event) {
for (final PlaceholderExpansion expansion : getExpansions()) {
if (!(expansion instanceof Cleanable)) {
continue;
}
((Cleanable) expansion).cleanup(event.getPlayer());
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onPluginDisable(@NotNull final PluginDisableEvent event) {
final String name = event.getPlugin().getName();
if (name.equals(plugin.getName())) {
return;
}
for (final PlaceholderExpansion expansion : getExpansions()) {
if (!name.equalsIgnoreCase(expansion.getRequiredPlugin())) {
continue;
}
expansion.unregister();
Msg.info("Unregistered placeholder expansion %s", expansion.getIdentifier());
Msg.info("Reason: required plugin %s was disabled.", name);
}
} }
}
} }

View File

@@ -22,6 +22,7 @@ package me.clip.placeholderapi.replacer;
import java.util.Locale; import java.util.Locale;
import java.util.function.Function; import java.util.function.Function;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
@@ -30,108 +31,110 @@ import org.jetbrains.annotations.Nullable;
public final class CharsReplacer implements Replacer { public final class CharsReplacer implements Replacer {
@NotNull @NotNull
private final Closure closure; private final Closure closure;
public CharsReplacer(@NotNull final Closure closure) { public CharsReplacer(@NotNull final Closure closure) {
this.closure = closure; this.closure = closure;
}
@NotNull
@Override
public String apply(@NotNull final String text, @Nullable final OfflinePlayer player,
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) {
final char[] chars = text.toCharArray();
final StringBuilder builder = new StringBuilder(text.length());
final StringBuilder identifier = new StringBuilder();
final StringBuilder parameters = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
final char l = chars[i];
if (l != closure.head || i + 1 >= chars.length) {
builder.append(l);
continue;
}
boolean identified = false;
boolean invalid = true;
boolean hadSpace = false;
while (++i < chars.length) {
final char p = chars[i];
if (p == ' ' && !identified) {
hadSpace = true;
break;
}
if (p == closure.tail) {
invalid = false;
break;
}
if (p == '_' && !identified) {
identified = true;
continue;
}
if (identified) {
parameters.append(p);
} else {
identifier.append(p);
}
}
final String identifierString = identifier.toString();
final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT);
final String parametersString = parameters.toString();
identifier.setLength(0);
parameters.setLength(0);
if (invalid) {
builder.append(closure.head).append(identifierString);
if (identified) {
builder.append('_').append(parametersString);
}
if (hadSpace) {
builder.append(' ');
}
continue;
}
final PlaceholderExpansion placeholder = lookup.apply(lowercaseIdentifierString);
if (placeholder == null) {
builder.append(closure.head).append(identifierString);
if (identified) {
builder.append('_');
}
builder.append(parametersString).append(closure.tail);
continue;
}
final String replacement = placeholder.onRequest(player, parametersString);
if (replacement == null) {
builder.append(closure.head).append(identifierString);
if (identified) {
builder.append('_');
}
builder.append(parametersString).append(closure.tail);
continue;
}
builder.append(replacement);
} }
return builder.toString();
} @NotNull
@Override
public String apply(@NotNull final String text, @Nullable final OfflinePlayer player,
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) {
final char[] chars = text.toCharArray();
// Woo! Hlello %player_name%
// Woo! GHsda PiggyPiglet
final StringBuilder builder = new StringBuilder(text.length());
final StringBuilder identifier = new StringBuilder();
final StringBuilder parameters = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
final char l = chars[i];
if (l != closure.head || i + 1 >= chars.length) {
builder.append(l);
continue;
}
boolean identified = false;
boolean invalid = true;
boolean hadSpace = false;
while (++i < chars.length) {
final char p = chars[i];
if (p == ' ' && !identified) {
hadSpace = true;
break;
}
if (p == closure.tail) {
invalid = false;
break;
}
if (p == '_' && !identified) {
identified = true;
continue;
}
if (identified) {
parameters.append(p);
} else {
identifier.append(p);
}
}
final String identifierString = identifier.toString();
final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT);
final String parametersString = parameters.toString();
identifier.setLength(0);
parameters.setLength(0);
if (invalid) {
builder.append(closure.head).append(identifierString);
if (identified) {
builder.append('_').append(parametersString);
}
if (hadSpace) {
builder.append(' ');
}
continue;
}
final PlaceholderExpansion placeholder = lookup.apply(lowercaseIdentifierString);
if (placeholder == null) {
builder.append(closure.head).append(identifierString);
if (identified) {
builder.append('_');
}
builder.append(parametersString).append(closure.tail);
continue;
}
final String replacement = placeholder.onRequest(player, parametersString);
if (replacement == null) {
builder.append(closure.head).append(identifierString);
if (identified) {
builder.append('_');
}
builder.append(parametersString).append(closure.tail);
continue;
}
builder.append(replacement);
}
return builder.toString();
}
} }

View File

@@ -1,303 +0,0 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2025 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.clip.placeholderapi.replacer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.collect.Lists;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.kyori.adventure.text.*;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.renderer.ComponentRenderer;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A renderer performing a replacement on every {@link TextComponent} element of a component tree.
*/
final class ComponentCharsReplacer implements ComponentRenderer<ComponentCharsReplacer.State> {
//static final TextReplacementRenderer INSTANCE = new TextReplacementRenderer();
private final OfflinePlayer player;
private final Function<String, @Nullable PlaceholderExpansion> lookup;
private static final Closure CLOSURE = Closure.PERCENT;
enum Closure {
BRACKET('{', '}'),
PERCENT('%', '%');
public final char head, tail;
Closure(final char head, final char tail) {
this.head = head;
this.tail = tail;
}
}
public ComponentCharsReplacer(@Nullable final OfflinePlayer player,
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) {
this.player = player;
this.lookup = lookup;
}
@Override
public Component render(final Component component, final State state) {
if (!state.running) return component;
final boolean prevFirstMatch = state.firstMatch;
state.firstMatch = true;
final List<Component> oldChildren = component.children();
final int oldChildrenSize = oldChildren.size();
Style oldStyle = component.style();
List<Component> children = null;
Component modified = component;
// replace the component itself
if (component instanceof TextComponent) {
TextComponent tc = (TextComponent) component;
final String content = tc.content();
final char[] chars = content.toCharArray();
final StringBuilder identifier = new StringBuilder();
final StringBuilder parameters = new StringBuilder();
final Map<List<Integer>, Component> replacements = new HashMap<>();
for (int i = 0; i < chars.length; i++) {
final char l = chars[i];
if (l != CLOSURE.head || i + 1 >= chars.length) {
continue;
}
boolean identified = false;
boolean invalid = true;
boolean hadSpace = false;
while (++i < chars.length) {
final char p = chars[i];
if (p == ' ' && !identified) {
hadSpace = true;
break;
}
if (p == CLOSURE.tail) {
invalid = false;
break;
}
if (p == '_' && !identified) {
identified = true;
break;
}
if (identified) {
parameters.append(p);
} else {
identifier.append(p);
}
}
final String identifierString = identifier.toString();
final String lowerIdentifiedString = identifierString.toLowerCase();
final String parametersString = parameters.toString();
identifier.setLength(0);
parameters.setLength(0);
if (invalid) {
continue;
}
replacements.put(Lists.newArrayList(i, i + identifierString.length() + parametersString.length()), lookup.apply(lowerIdentifiedString).onPlaceholderComponentRequest(player, parametersString));
}
// do something with our replacements
final Matcher matcher = state.pattern.matcher(content);
int replacedUntil = 0; // last index handled
while (matcher.find()) {
final PatternReplacementResult result = state.continuer.shouldReplace(matcher, ++state.matchCount, state.replaceCount);
if (matcher.start() == 0) {
// if we're a full match, modify the component directly
if (matcher.end() == content.length()) {
final ComponentLike replacement = state.replacement.apply(matcher, Component.text().content(matcher.group())
.style(component.style()));
modified = replacement == null ? Component.empty() : replacement.asComponent();
if (modified.style().hoverEvent() != null) {
oldStyle = oldStyle.hoverEvent(null); // Remove original hover if it has been replaced completely
}
// merge style of the match into this component to prevent unexpected loss of style
modified = modified.style(modified.style().merge(component.style(), Style.Merge.Strategy.IF_ABSENT_ON_TARGET));
if (children == null) { // Prepare children
children = new ArrayList<>(oldChildrenSize + modified.children().size());
children.addAll(modified.children());
}
} else {
// otherwise, work on a child of the root node
modified = Component.text("", component.style());
final ComponentLike child = state.replacement.apply(matcher, Component.text().content(matcher.group()));
if (child != null) {
if (children == null) {
children = new ArrayList<>(oldChildrenSize + 1);
}
children.add(child.asComponent());
}
}
} else {
if (children == null) {
children = new ArrayList<>(oldChildrenSize + 2);
}
if (state.firstMatch) {
// truncate parent to content before match
modified = ((TextComponent) component).content(content.substring(0, matcher.start()));
} else if (replacedUntil < matcher.start()) {
children.add(Component.text(content.substring(replacedUntil, matcher.start())));
}
final ComponentLike builder = state.replacement.apply(matcher, Component.text().content(matcher.group()));
if (builder != null) {
children.add(builder.asComponent());
}
}
state.replaceCount++;
state.firstMatch = false;
replacedUntil = matcher.end();
}
if (replacedUntil < content.length()) {
// append trailing content
if (replacedUntil > 0) {
if (children == null) {
children = new ArrayList<>(oldChildrenSize);
}
children.add(Component.text(content.substring(replacedUntil)));
}
// otherwise, we haven't modified the component, so nothing to change
}
} else if (modified instanceof TranslatableComponent) { // get TranslatableComponent with() args
final List<TranslationArgument> args = ((TranslatableComponent) modified).arguments();
List<TranslationArgument> newArgs = null;
for (int i = 0, size = args.size(); i < size; i++) {
final TranslationArgument original = args.get(i);
final TranslationArgument replaced = original.value() instanceof Component ? TranslationArgument.component(this.render((Component) original.value(), state)) : original;
if (replaced != original) {
if (newArgs == null) {
newArgs = new ArrayList<>(size);
if (i > 0) {
newArgs.addAll(args.subList(0, i));
}
}
}
if (newArgs != null) {
newArgs.add(replaced);
}
}
if (newArgs != null) {
modified = ((TranslatableComponent) modified).arguments(newArgs);
}
}
// Only visit children if we're running
if (state.running) {
// hover event
if (state.replaceInsideHoverEvents) {
final HoverEvent<?> event = oldStyle.hoverEvent();
if (event != null) {
final HoverEvent<?> rendered = event.withRenderedValue(this, state);
if (event != rendered) {
modified = modified.style(s -> s.hoverEvent(rendered));
}
}
}
// Children
boolean first = true;
for (int i = 0; i < oldChildrenSize; i++) {
final Component child = oldChildren.get(i);
final Component replaced = this.render(child, state);
if (replaced != child) {
if (children == null) {
children = new ArrayList<>(oldChildrenSize);
}
if (first) {
children.addAll(oldChildren.subList(0, i));
}
first = false;
}
if (children != null) {
children.add(replaced);
first = false;
}
}
} else {
// we're not visiting children, re-add original children if necessary
if (children != null) {
children.addAll(oldChildren);
}
}
state.firstMatch = prevFirstMatch;
// Update the modified component with new children
if (children != null) {
return modified.children(children);
}
return modified;
}
static final class State {
final Pattern pattern;
final BiFunction<MatchResult, TextComponent.Builder, @Nullable ComponentLike> replacement;
final TextReplacementConfig.Condition continuer;
final boolean replaceInsideHoverEvents;
boolean running = true;
int matchCount = 0;
int replaceCount = 0;
boolean firstMatch = true;
State(final Pattern pattern, final BiFunction<MatchResult, TextComponent.Builder, @Nullable ComponentLike> replacement, final TextReplacementConfig.Condition continuer, final boolean replaceInsideHoverEvents) {
this.pattern = pattern;
this.replacement = replacement;
this.continuer = continuer;
this.replaceInsideHoverEvents = replaceInsideHoverEvents;
}
}
}

View File

@@ -0,0 +1,146 @@
package me.clip.placeholderapi.replacer;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.kyori.adventure.text.*;
import net.kyori.adventure.text.format.Style;
import org.bukkit.OfflinePlayer;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;
public final class ComponentReplacer {
public Component replace(Component component, OfflinePlayer player, Function<String, PlaceholderExpansion> function) {
Component modified = component;
final List<Component> oldChildren = component.children();
final int oldChildrenSize = oldChildren.size();
List<Component> children = null;
if (component instanceof TextComponent) {
TextComponent tc = (TextComponent) component;
final String content = tc.content();
final char[] chars = content.toCharArray();
final StringBuilder identifier = new StringBuilder();
final StringBuilder parameters = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
final char l = chars[i];
if (l != '%' || i + 1 >= chars.length) {
continue;
}
final int start = i;
boolean identified = false;
boolean invalid = true;
while (++i < chars.length) {
final char p = chars[i];
if (p == ' ' && !identified) {
break;
}
if (p == '%') {
invalid = false;
break;
}
if (p == '_' && !identified) {
identified = true;
continue;
}
if (identified) {
parameters.append(p);
} else {
identifier.append(p);
}
}
final String identifierString = identifier.toString();
final String lowercaseIdentifierString = identifierString.toLowerCase(Locale.ROOT);
final String parametersString = parameters.toString();
identifier.setLength(0);
parameters.setLength(0);
if (invalid) {
continue;
}
final PlaceholderExpansion expansion = function.apply(lowercaseIdentifierString);
if (expansion == null) {
continue;
}
final String placeholderValue = expansion.onRequest(player, parametersString);
if (placeholderValue == null) {
continue;
}
if (start == 0) {
// if we're a full match, modify the component directly
if (i == content.length() - 1) {
final ComponentLike replacement = Component.text(placeholderValue).style(component.style());
modified = replacement.asComponent();
Style modStyle = modified.style();
if (modStyle.hoverEvent() != null) {
Object hoverValue = modStyle.hoverEvent().value();
if (hoverValue instanceof Component) {
final Component replacedHoverComponent = replace((Component) hoverValue, player, function);
if (replacedHoverComponent != hoverValue) {
modified.style();
}
}
}
if (modStyle.clickEvent() != null) {
String clickValue =
}
}
}
}
} else if (component instanceof TranslatableComponent) {
TranslatableComponent tc = (TranslatableComponent) component;
final List<TranslationArgument> args = tc.arguments();
List<TranslationArgument> newArgs = null;
for (int i = 0, size = args.size(); i < size; i++) {
final TranslationArgument original = args.get(i);
TranslationArgument replacement = original instanceof Component ? TranslationArgument.component(replace((Component) original, player, function)) : original;
if (original != replacement) {
if (newArgs == null) {
newArgs = new ArrayList<>(size);
if (i > 0) {
newArgs.addAll(args.subList(0, i));
}
}
}
if (newArgs != null) {
newArgs.add(replacement);
}
}
if (newArgs != null) {
modified = ((TranslatableComponent) modified).arguments(newArgs);
}
}
return modified;
}
}

View File

@@ -0,0 +1,48 @@
package me.clip.placeholderapi.replacer;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
import java.util.regex.Pattern;
public class ExactReplacer implements Replacer {
private static final Pattern DELIMITER = Pattern.compile("_");
@NotNull
@Override
public String apply(@NotNull String text, @Nullable final OfflinePlayer player,
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup) {
text = text.substring(1, text.length() - 1);
final String[] parts = DELIMITER.split(text);
final PlaceholderExpansion expansion;
if (parts.length == 0) {
expansion = lookup.apply(text);
} else {
expansion = lookup.apply(parts[0]);
}
if (expansion == null) {
return text;
}
final String params;
if (text.endsWith("_")) {
params = "";
} else {
params = text.substring(text.indexOf('_') + 1);
}
final String result = expansion.onRequest(player, params);
if (result == null) {
return text;
}
return result;
}
}

View File

@@ -21,6 +21,7 @@
package me.clip.placeholderapi.replacer; package me.clip.placeholderapi.replacer;
import java.util.function.Function; import java.util.function.Function;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -28,22 +29,22 @@ import org.jetbrains.annotations.Nullable;
public interface Replacer { public interface Replacer {
@NotNull @NotNull
String apply(@NotNull final String text, @Nullable final OfflinePlayer player, String apply(@NotNull final String text, @Nullable final OfflinePlayer player,
@NotNull final Function<String, @Nullable PlaceholderExpansion> lookup); @NotNull final Function<String, @Nullable PlaceholderExpansion> lookup);
enum Closure { enum Closure {
BRACKET('{', '}'), BRACKET('{', '}'),
PERCENT('%', '%'); PERCENT('%', '%');
public final char head, tail; public final char head, tail;
Closure(final char head, final char tail) { Closure(final char head, final char tail) {
this.head = head; this.head = head;
this.tail = tail; this.tail = tail;
}
} }
}
} }

View File

@@ -34,44 +34,44 @@ import java.util.jar.JarInputStream;
public class FileUtil { public class FileUtil {
@Nullable @Nullable
public static <T> Class<? extends T> findClass(@NotNull final File file, public static <T> Class<? extends T> findClass(@NotNull final File file,
@NotNull final Class<T> clazz) throws IOException, ClassNotFoundException { @NotNull final Class<T> clazz) throws IOException, ClassNotFoundException {
if (!file.exists()) { if (!file.exists()) {
return null; return null;
}
final URL jar = file.toURI().toURL();
final URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader());
final List<String> matches = new ArrayList<>();
final List<Class<? extends T>> classes = new ArrayList<>();
try (final JarInputStream stream = new JarInputStream(jar.openStream())) {
JarEntry entry;
while ((entry = stream.getNextJarEntry()) != null) {
final String name = entry.getName();
if (name.isEmpty() || !name.endsWith(".class")) {
continue;
} }
matches.add(name.substring(0, name.lastIndexOf('.')).replace('/', '.')); final URL jar = file.toURI().toURL();
} final URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader());
final List<String> matches = new ArrayList<>();
final List<Class<? extends T>> classes = new ArrayList<>();
for (final String match : matches) { try (final JarInputStream stream = new JarInputStream(jar.openStream())) {
try { JarEntry entry;
final Class<?> loaded = loader.loadClass(match); while ((entry = stream.getNextJarEntry()) != null) {
if (clazz.isAssignableFrom(loaded)) { final String name = entry.getName();
classes.add(loaded.asSubclass(clazz)); if (name.isEmpty() || !name.endsWith(".class")) {
} continue;
} catch (final NoClassDefFoundError ignored) { }
matches.add(name.substring(0, name.lastIndexOf('.')).replace('/', '.'));
}
for (final String match : matches) {
try {
final Class<?> loaded = loader.loadClass(match);
if (clazz.isAssignableFrom(loaded)) {
classes.add(loaded.asSubclass(clazz));
}
} catch (final NoClassDefFoundError ignored) {
}
}
} }
} if (classes.isEmpty()) {
loader.close();
return null;
}
return classes.get(0);
} }
if (classes.isEmpty()) {
loader.close();
return null;
}
return classes.get(0);
}
} }

View File

@@ -29,6 +29,7 @@ import static java.util.stream.IntStream.range;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@@ -36,35 +37,35 @@ import org.jetbrains.annotations.NotNull;
*/ */
public final class Format { public final class Format {
private Format() {} private Format() {}
@NotNull @NotNull
public static Optional<List<String>> tablify(@NotNull final Align align, public static Optional<List<String>> tablify(@NotNull final Align align,
@NotNull final List<List<String>> rows) { @NotNull final List<List<String>> rows) {
return findSpacing(rows) return findSpacing(rows)
.map(spacing -> buildFormat(align, spacing)) .map(spacing -> buildFormat(align, spacing))
.map(format -> rows.stream() .map(format -> rows.stream()
.map( .map(
row -> String.format(format, row.toArray()).substring(align == Align.RIGHT ? 2 : 0)) row -> String.format(format, row.toArray()).substring(align == Align.RIGHT ? 2 : 0))
.collect(toList())); .collect(toList()));
} }
@NotNull @NotNull
private static String buildFormat(@NotNull final Align align, final int[] spacing) { private static String buildFormat(@NotNull final Align align, final int[] spacing) {
return stream(spacing) return stream(spacing)
.mapToObj(space -> "%" + (align == Align.LEFT ? "-" : "") + (space + 2) + "s") .mapToObj(space -> "%" + (align == Align.LEFT ? "-" : "") + (space + 2) + "s")
.collect(joining()); .collect(joining());
} }
@NotNull @NotNull
private static Optional<int[]> findSpacing(@NotNull final List<List<String>> rows) { private static Optional<int[]> findSpacing(@NotNull final List<List<String>> rows) {
return rows.stream() return rows.stream()
.map(row -> row.stream().mapToInt(String::length).toArray()) .map(row -> row.stream().mapToInt(String::length).toArray())
.reduce((l, r) -> range(0, min(l.length, r.length)).map(i -> max(l[i], r[i])).toArray()); .reduce((l, r) -> range(0, min(l.length, r.length)).map(i -> max(l[i], r[i])).toArray());
} }
public enum Align { public enum Align {
LEFT, RIGHT LEFT, RIGHT
} }
} }

View File

@@ -35,45 +35,45 @@ import org.jetbrains.annotations.NotNull;
public final class Futures { public final class Futures {
private Futures() {} private Futures() {}
public static <T> void onMainThread(@NotNull final PlaceholderAPIPlugin plugin, public static <T> void onMainThread(@NotNull final PlaceholderAPIPlugin plugin,
@NotNull final CompletableFuture<T> future, @NotNull final CompletableFuture<T> future,
@NotNull final BiConsumer<T, Throwable> consumer) { @NotNull final BiConsumer<T, Throwable> consumer) {
future.whenComplete((value, exception) -> { future.whenComplete((value, exception) -> {
if (Bukkit.isPrimaryThread()) { if (Bukkit.isPrimaryThread()) {
consumer.accept(value, exception); consumer.accept(value, exception);
} else { } else {
plugin.getScheduler().runTask(() -> consumer.accept(value, exception)); plugin.getScheduler().runTask(() -> consumer.accept(value, exception));
} }
}); });
} }
@NotNull @NotNull
public static <T> Collector<CompletableFuture<T>, ?, CompletableFuture<List<T>>> collector() { public static <T> Collector<CompletableFuture<T>, ?, CompletableFuture<List<T>>> collector() {
return Collectors.collectingAndThen(Collectors.toList(), Futures::of); return Collectors.collectingAndThen(Collectors.toList(), Futures::of);
} }
@NotNull @NotNull
public static <T> CompletableFuture<List<T>> of( public static <T> CompletableFuture<List<T>> of(
@NotNull final Stream<CompletableFuture<T>> futures) { @NotNull final Stream<CompletableFuture<T>> futures) {
return of(futures.collect(Collectors.toList())); return of(futures.collect(Collectors.toList()));
} }
@NotNull @NotNull
public static <T> CompletableFuture<List<T>> of( public static <T> CompletableFuture<List<T>> of(
@NotNull final Collection<CompletableFuture<T>> futures) { @NotNull final Collection<CompletableFuture<T>> futures) {
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApplyAsync($ -> awaitCompletion(futures)); .thenApplyAsync($ -> awaitCompletion(futures));
} }
@NotNull @NotNull
private static <T> List<T> awaitCompletion( private static <T> List<T> awaitCompletion(
@NotNull final Collection<CompletableFuture<T>> futures) { @NotNull final Collection<CompletableFuture<T>> futures) {
return futures.stream().map(CompletableFuture::join).collect(Collectors.toList()); return futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
} }
} }

View File

@@ -23,6 +23,7 @@ package me.clip.placeholderapi.util;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@@ -30,50 +31,50 @@ import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public final class Msg { public final class Msg {
public static void log(Level level, String msg, Object... args) { public static void log(Level level, String msg, Object... args) {
PlaceholderAPIPlugin.getInstance().getLogger().log(level, String.format(msg, args)); PlaceholderAPIPlugin.getInstance().getLogger().log(level, String.format(msg, args));
}
public static void info(String msg, Object... args) {
log(Level.INFO, msg, args);
}
public static void warn(String msg, Object... args) {
log(Level.WARNING, msg, args);
}
public static void warn(String msg, Throwable throwable, Object... args){
PlaceholderAPIPlugin.getInstance().getLogger().log(Level.WARNING, String.format(msg, args), throwable);
}
public static void severe(String msg, Object... args) {
log(Level.SEVERE, msg, args);
}
public static void severe(String msg, Throwable throwable, Object... args) {
PlaceholderAPIPlugin.getInstance().getLogger().log(Level.SEVERE, String.format(msg, args), throwable);
}
public static void msg(@NotNull final CommandSender sender, @NotNull final String... messages) {
if (messages.length == 0) {
return;
} }
sender.sendMessage(Arrays.stream(messages).map(Msg::color).collect(Collectors.joining("\n"))); public static void info(String msg, Object... args) {
} log(Level.INFO, msg, args);
public static void broadcast(@NotNull final String... messages) {
if (messages.length == 0) {
return;
} }
Bukkit.broadcastMessage( public static void warn(String msg, Object... args) {
Arrays.stream(messages).map(Msg::color).collect(Collectors.joining("\n"))); log(Level.WARNING, msg, args);
} }
public static String color(@NotNull final String text) { public static void warn(String msg, Throwable throwable, Object... args) {
return ChatColor.translateAlternateColorCodes('&', text); PlaceholderAPIPlugin.getInstance().getLogger().log(Level.WARNING, String.format(msg, args), throwable);
} }
public static void severe(String msg, Object... args) {
log(Level.SEVERE, msg, args);
}
public static void severe(String msg, Throwable throwable, Object... args) {
PlaceholderAPIPlugin.getInstance().getLogger().log(Level.SEVERE, String.format(msg, args), throwable);
}
public static void msg(@NotNull final CommandSender sender, @NotNull final String... messages) {
if (messages.length == 0) {
return;
}
sender.sendMessage(Arrays.stream(messages).map(Msg::color).collect(Collectors.joining("\n")));
}
public static void broadcast(@NotNull final String... messages) {
if (messages.length == 0) {
return;
}
Bukkit.broadcastMessage(
Arrays.stream(messages).map(Msg::color).collect(Collectors.joining("\n")));
}
public static String color(@NotNull final String text) {
return ChatColor.translateAlternateColorCodes('&', text);
}
} }

View File

@@ -21,8 +21,8 @@
package me.clip.placeholderapi.util; package me.clip.placeholderapi.util;
public enum TimeFormat { public enum TimeFormat {
DAYS, DAYS,
HOURS, HOURS,
MINUTES, MINUTES,
SECONDS SECONDS
} }

View File

@@ -26,71 +26,71 @@ import java.util.StringJoiner;
public class TimeUtil { public class TimeUtil {
public static String getRemaining(final int seconds, final TimeFormat type) { public static String getRemaining(final int seconds, final TimeFormat type) {
return getRemaining((long) seconds, type); return getRemaining((long) seconds, type);
}
public static String getRemaining(final long seconds, final TimeFormat type) {
switch (type) {
default:
return String.valueOf(seconds);
case SECONDS:
return String.valueOf(seconds % 60);
case MINUTES:
return String.valueOf((seconds / 60) % 60);
case HOURS:
return String.valueOf((seconds / 3600) % 24);
case DAYS:
return String.valueOf(seconds / 86400);
}
}
/**
* Format the given value with s, m, h and d (seconds, minutes, hours and days)
*
* @param duration {@link Duration} (eg, Duration.of(20, {@link ChronoUnit#SECONDS}) for 20
* seconds)
* @return formatted time
*/
public static String getTime(final Duration duration) {
return getTime(duration.getSeconds());
}
public static String getTime(final int seconds) {
return getTime((long) seconds);
}
public static String getTime(long seconds) {
final StringJoiner joiner = new StringJoiner(" ");
long minutes = seconds / 60;
long hours = minutes / 60;
final long days = hours / 24;
seconds %= 60;
minutes %= 60;
hours %= 24;
if (days > 0) {
joiner.add(days + "d");
} }
if (hours > 0) { public static String getRemaining(final long seconds, final TimeFormat type) {
joiner.add(hours + "h"); switch (type) {
default:
return String.valueOf(seconds);
case SECONDS:
return String.valueOf(seconds % 60);
case MINUTES:
return String.valueOf((seconds / 60) % 60);
case HOURS:
return String.valueOf((seconds / 3600) % 24);
case DAYS:
return String.valueOf(seconds / 86400);
}
} }
if (minutes > 0) { /**
joiner.add(minutes + "m"); * Format the given value with s, m, h and d (seconds, minutes, hours and days)
*
* @param duration {@link Duration} (eg, Duration.of(20, {@link ChronoUnit#SECONDS}) for 20
* seconds)
* @return formatted time
*/
public static String getTime(final Duration duration) {
return getTime(duration.getSeconds());
} }
if (seconds > 0) { public static String getTime(final int seconds) {
joiner.add(seconds + "s"); return getTime((long) seconds);
} }
return joiner.toString(); public static String getTime(long seconds) {
} final StringJoiner joiner = new StringJoiner(" ");
long minutes = seconds / 60;
long hours = minutes / 60;
final long days = hours / 24;
seconds %= 60;
minutes %= 60;
hours %= 24;
if (days > 0) {
joiner.add(days + "d");
}
if (hours > 0) {
joiner.add(hours + "h");
}
if (minutes > 0) {
joiner.add(minutes + "m");
}
if (seconds > 0) {
joiner.add(seconds + "s");
}
return joiner.toString();
}
} }

View File

@@ -12,6 +12,8 @@ commands:
placeholderapi: placeholderapi:
description: "PlaceholderAPI Command" description: "PlaceholderAPI Command"
aliases: ["papi"] aliases: ["papi"]
test:
description: "yes"
permissions: permissions:
placeholderapi.*: placeholderapi.*: