mirror of
https://github.com/PlaceholderAPI/PlaceholderAPI
synced 2024-11-18 00:46:55 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
9291184534
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -38,3 +38,7 @@ Please also make sure that you use the [latest release][Spigot] or the latest [d
|
||||
> What steps did you made, to get this bug?
|
||||
<!-- Please write below this line to prevent formatting issues -->
|
||||
1.
|
||||
|
||||
### Installed expansions
|
||||
> Please list all expansions that are displayed when running `/papi list`
|
||||
<!-- Please write below this line to prevent formatting issues -->
|
||||
|
@ -18,9 +18,9 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.google.code.gson:gson:2.8.5"
|
||||
implementation "org.bstats:bstats-bukkit:1.5"
|
||||
implementation "me.rayzr522:jsonmessage:1.2.1"
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'org.bstats:bstats-bukkit:1.5'
|
||||
implementation 'me.rayzr522:jsonmessage:1.2.1'
|
||||
|
||||
compileOnly "org.spigotmc:spigot-api:1.16.1-R0.1-SNAPSHOT"
|
||||
compileOnly "org.jetbrains:annotations:19.0.0"
|
||||
|
@ -181,40 +181,20 @@ public final class PlaceholderAPIPlugin extends JavaPlugin
|
||||
private void setupMetrics()
|
||||
{
|
||||
final Metrics metrics = new Metrics(this);
|
||||
metrics.addCustomChart(
|
||||
new Metrics.SimplePie(
|
||||
"using_expansion_cloud",
|
||||
() -> getPlaceholderAPIConfig().isCloudEnabled() ? "yes" : "no"));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("using_expansion_cloud", () -> getPlaceholderAPIConfig().isCloudEnabled() ? "yes" : "no"));
|
||||
|
||||
metrics.addCustomChart(
|
||||
new Metrics.SimplePie("using_spigot", () -> getServerVersion().isSpigot() ? "yes" : "no"));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("using_spigot", () -> getServerVersion().isSpigot() ? "yes" : "no"));
|
||||
|
||||
metrics.addCustomChart(
|
||||
new Metrics.AdvancedPie(
|
||||
"expansions_used",
|
||||
() -> {
|
||||
Map<String, Integer> map = new HashMap<>();
|
||||
Map<String, PlaceholderHook> hooks = PlaceholderAPI.getPlaceholders();
|
||||
metrics.addCustomChart(new Metrics.AdvancedPie("expansions_used", () -> {
|
||||
final Map<String, Integer> values = new HashMap<>();
|
||||
|
||||
if (!hooks.isEmpty())
|
||||
{
|
||||
for (final PlaceholderExpansion expansion : getLocalExpansionManager().getExpansions())
|
||||
{
|
||||
values.put(expansion.getRequiredPlugin() == null ? expansion.getIdentifier() : expansion.getRequiredPlugin(), 1);
|
||||
}
|
||||
|
||||
for (PlaceholderHook hook : hooks.values())
|
||||
{
|
||||
if (hook instanceof PlaceholderExpansion)
|
||||
{
|
||||
PlaceholderExpansion expansion = (PlaceholderExpansion) hook;
|
||||
map.put(
|
||||
expansion.getRequiredPlugin() == null
|
||||
? expansion.getIdentifier()
|
||||
: expansion.getRequiredPlugin(),
|
||||
1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}));
|
||||
return values;
|
||||
}));
|
||||
}
|
||||
|
||||
private void setupExpansions()
|
||||
|
@ -25,6 +25,10 @@ import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @deprecated This class will be completely removed in the next release, please use {@link me.clip.placeholderapi.expansion.PlaceholderExpansion}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class PlaceholderHook
|
||||
{
|
||||
|
||||
@ -35,8 +39,10 @@ public abstract class PlaceholderHook
|
||||
* player
|
||||
* @param params String passed to the hook to determine what value to return
|
||||
* @return value for the requested player and params
|
||||
* @deprecated This method will be completely removed, please use {@link me.clip.placeholderapi.expansion.PlaceholderExpansion#onRequest(OfflinePlayer, String)}
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
public String onRequest(@Nullable final OfflinePlayer player, @NotNull final String params)
|
||||
{
|
||||
if (player != null && player.isOnline())
|
||||
@ -53,6 +59,8 @@ public abstract class PlaceholderHook
|
||||
* @param player {@link Player} to request the placeholder value for, null if not needed for a player
|
||||
* @param params String passed to the hook to determine what value to return
|
||||
* @return value for the requested player and params
|
||||
*
|
||||
* @deprecated This method will be completely removed, please use {@link me.clip.placeholderapi.expansion.PlaceholderExpansion#onRequest(OfflinePlayer, String)}
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
|
@ -24,7 +24,15 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.commands.impl.cloud.CommandECloud;
|
||||
import me.clip.placeholderapi.commands.impl.local.*;
|
||||
import me.clip.placeholderapi.commands.impl.local.CommandDump;
|
||||
import me.clip.placeholderapi.commands.impl.local.CommandExpansionRegister;
|
||||
import me.clip.placeholderapi.commands.impl.local.CommandExpansionUnregister;
|
||||
import me.clip.placeholderapi.commands.impl.local.CommandHelp;
|
||||
import me.clip.placeholderapi.commands.impl.local.CommandInfo;
|
||||
import me.clip.placeholderapi.commands.impl.local.CommandList;
|
||||
import me.clip.placeholderapi.commands.impl.local.CommandParse;
|
||||
import me.clip.placeholderapi.commands.impl.local.CommandReload;
|
||||
import me.clip.placeholderapi.commands.impl.local.CommandVersion;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@ -43,6 +51,7 @@ public final class PlaceholderCommandRouter implements CommandExecutor, TabCompl
|
||||
private static final List<PlaceholderCommand> COMMANDS = ImmutableList.of(new CommandHelp(),
|
||||
new CommandInfo(),
|
||||
new CommandList(),
|
||||
new CommandDump(),
|
||||
new CommandECloud(),
|
||||
new CommandParse(),
|
||||
new CommandReload(),
|
||||
|
@ -27,6 +27,7 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.primitives.Ints;
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.commands.PlaceholderCommand;
|
||||
import me.clip.placeholderapi.configuration.ExpansionSort;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
||||
import me.clip.placeholderapi.util.Format;
|
||||
@ -39,7 +40,14 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@ -97,7 +105,7 @@ public final class CommandECloudExpansionList extends PlaceholderCommand
|
||||
return;
|
||||
}
|
||||
|
||||
expansions.sort(Comparator.comparing(CloudExpansion::getLastUpdate).reversed());
|
||||
expansions.sort(plugin.getPlaceholderAPIConfig().getExpansionSort().orElse(ExpansionSort.LATEST));
|
||||
|
||||
if (!(sender instanceof Player) && params.size() < 2)
|
||||
{
|
||||
|
@ -25,16 +25,16 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.commands.PlaceholderCommand;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
||||
import me.clip.placeholderapi.util.Futures;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
@ -87,31 +87,31 @@ public final class CommandECloudUpdate extends PlaceholderCommand
|
||||
Msg.msg(sender,
|
||||
"&aUpdating expansions: " + expansions.stream().map(CloudExpansion::getName).collect(Collectors.joining("&7, &6", "&8[&6", "&8]&r")));
|
||||
|
||||
downloadExpansions(plugin, expansions)
|
||||
.thenCompose(files -> discoverExpansions(plugin, files))
|
||||
.whenComplete((classes, exception) -> {
|
||||
if (exception != null)
|
||||
{
|
||||
Msg.msg(sender,
|
||||
"&cFailed to update expansions: &e" + exception.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
Msg.msg(sender,
|
||||
"&aSuccessfully downloaded updates, registering new versions.");
|
||||
Futures.onMainThread(plugin, downloadAndDiscover(expansions, plugin), (classes, exception) -> {
|
||||
if (exception != null)
|
||||
{
|
||||
Msg.msg(sender,
|
||||
"&cFailed to update expansions: &e" + exception.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
final String message = classes.stream()
|
||||
.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);
|
||||
});
|
||||
});
|
||||
Msg.msg(sender,
|
||||
"&aSuccessfully downloaded updates, registering new versions.");
|
||||
|
||||
|
||||
final String message = classes.stream()
|
||||
.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
|
||||
@ -134,22 +134,12 @@ public final class CommandECloudUpdate extends PlaceholderCommand
|
||||
}
|
||||
|
||||
|
||||
public static CompletableFuture<List<File>> downloadExpansions(@NotNull final PlaceholderAPIPlugin plugin, @NotNull final List<CloudExpansion> expansions)
|
||||
private static CompletableFuture<List<@Nullable Class<? extends PlaceholderExpansion>>> downloadAndDiscover(@NotNull final List<CloudExpansion> expansions, @NotNull final PlaceholderAPIPlugin plugin)
|
||||
{
|
||||
final List<CompletableFuture<File>> futures = expansions.stream()
|
||||
.map(expansion -> plugin.getCloudExpansionManager().downloadExpansion(expansion, expansion.getVersion()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApplyAsync(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public static CompletableFuture<List<Class<? extends PlaceholderExpansion>>> discoverExpansions(@NotNull final PlaceholderAPIPlugin plugin, @NotNull final List<File> files)
|
||||
{
|
||||
final List<CompletableFuture<List<Class<? extends PlaceholderExpansion>>>> futures = files.stream()
|
||||
.map(file -> plugin.getLocalExpansionManager().findExpansionsInFile(file))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApplyAsync(v -> futures.stream().map(CompletableFuture::join).flatMap(Collection::stream).collect(Collectors.toList()));
|
||||
return expansions.stream()
|
||||
.map(expansion -> plugin.getCloudExpansionManager().downloadExpansion(expansion, expansion.getVersion()))
|
||||
.map(future -> future.thenCompose(plugin.getLocalExpansionManager()::findExpansionInFile))
|
||||
.collect(Futures.collector());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,179 @@
|
||||
package me.clip.placeholderapi.commands.impl.local;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.gson.JsonParser;
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.commands.PlaceholderCommand;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class CommandDump extends PlaceholderCommand
|
||||
{
|
||||
|
||||
@NotNull
|
||||
private static final String URL = "https://paste.helpch.at/";
|
||||
|
||||
@NotNull
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
|
||||
.withLocale(Locale.US)
|
||||
.withZone(ZoneId.of("UTC"));
|
||||
|
||||
|
||||
public CommandDump()
|
||||
{
|
||||
super("dump");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, @NotNull final CommandSender sender, @NotNull final String alias, @NotNull @Unmodifiable final List<String> params)
|
||||
{
|
||||
postDump(makeDump(plugin)).whenComplete((key, exception) -> {
|
||||
if (exception != null)
|
||||
{
|
||||
plugin.getLogger().log(Level.WARNING, "failed to post dump details", exception);
|
||||
|
||||
Msg.msg(sender,
|
||||
"&cFailed to post dump details, check console.");
|
||||
return;
|
||||
}
|
||||
|
||||
Msg.msg(sender,
|
||||
"&aSuccessfully posted dump: " + URL + key);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private CompletableFuture<String> postDump(@NotNull final String dump)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try
|
||||
{
|
||||
final HttpURLConnection connection = ((HttpURLConnection) new URL(URL + "documents").openConnection());
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
|
||||
connection.setDoOutput(true);
|
||||
|
||||
connection.connect();
|
||||
|
||||
try (final OutputStream stream = connection.getOutputStream())
|
||||
{
|
||||
stream.write(dump.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
try (final InputStream stream = connection.getInputStream())
|
||||
{
|
||||
//noinspection UnstableApiUsage
|
||||
final String json = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8));
|
||||
return JsonParser.parseString(json).getAsJsonObject().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 Map<String, List<PlaceholderExpansion>> expansions = plugin.getLocalExpansionManager()
|
||||
.getExpansions()
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(PlaceholderExpansion::getAuthor));
|
||||
|
||||
for (final Map.Entry<String, List<PlaceholderExpansion>> expansionsByAuthor : expansions.entrySet())
|
||||
{
|
||||
builder.append(" ")
|
||||
.append(expansionsByAuthor.getKey())
|
||||
.append(": ")
|
||||
.append('\n');
|
||||
|
||||
for (final PlaceholderExpansion expansion : expansionsByAuthor.getValue())
|
||||
{
|
||||
builder.append(" ")
|
||||
.append(expansion.getName())
|
||||
.append(':')
|
||||
.append(expansion.getVersion())
|
||||
.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
builder.append('\n');
|
||||
|
||||
builder.append("Expansions Directory:")
|
||||
.append('\n');
|
||||
|
||||
final String[] jars = plugin.getLocalExpansionManager()
|
||||
.getExpansionsFolder()
|
||||
.list((dir, name) -> name.toLowerCase().endsWith(".jar"));
|
||||
|
||||
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\n");
|
||||
|
||||
builder.append("Plugin Info:")
|
||||
.append('\n');
|
||||
|
||||
for (final Plugin other : plugin.getServer().getPluginManager().getPlugins())
|
||||
{
|
||||
builder.append(" ")
|
||||
.append(other.getName())
|
||||
.append(": ")
|
||||
.append(other.getDescription().getVersion())
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
@ -24,8 +24,8 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.commands.PlaceholderCommand;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.clip.placeholderapi.expansion.manager.LocalExpansionManager;
|
||||
import me.clip.placeholderapi.util.Futures;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
@ -65,7 +65,7 @@ public final class CommandExpansionRegister extends PlaceholderCommand
|
||||
return;
|
||||
}
|
||||
|
||||
manager.findExpansionsInFile(file).whenComplete((classes, exception) -> {
|
||||
Futures.onMainThread(plugin, manager.findExpansionInFile(file), (clazz, exception) -> {
|
||||
if (exception != null)
|
||||
{
|
||||
Msg.msg(sender,
|
||||
@ -75,25 +75,25 @@ public final class CommandExpansionRegister extends PlaceholderCommand
|
||||
return;
|
||||
}
|
||||
|
||||
if (classes.isEmpty())
|
||||
if (clazz == null)
|
||||
{
|
||||
Msg.msg(sender,
|
||||
"&cNo expansion class found in file: &f" + file);
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
final Optional<PlaceholderExpansion> expansion = manager.register(classes.get(0));
|
||||
if (!expansion.isPresent())
|
||||
{
|
||||
Msg.msg(sender,
|
||||
"&cFailed to register expansion from &f" + params.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
final Optional<PlaceholderExpansion> expansion = manager.register(clazz);
|
||||
if (!expansion.isPresent())
|
||||
{
|
||||
Msg.msg(sender,
|
||||
"&aSuccessfully registered expansion: &f" + expansion.get().getName());
|
||||
});
|
||||
"&cFailed to register expansion from &f" + params.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
Msg.msg(sender,
|
||||
"&aSuccessfully registered expansion: &f" + expansion.get().getName());
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public final class CommandExpansionUnregister extends PlaceholderCommand
|
||||
}
|
||||
|
||||
|
||||
final String message = !plugin.getLocalExpansionManager().unregister(expansion.get()) ?
|
||||
final String message = !expansion.get().unregister() ?
|
||||
"&cFailed to unregister expansion: &f" :
|
||||
"&aSuccessfully unregistered expansion: &f";
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
package me.clip.placeholderapi.configuration;
|
||||
|
||||
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public enum ExpansionSort implements Comparator<CloudExpansion>
|
||||
{
|
||||
|
||||
NAME(Comparator.comparing(CloudExpansion::getName)),
|
||||
AUTHOR(Comparator.comparing(CloudExpansion::getAuthor)),
|
||||
LATEST(Comparator.comparing(CloudExpansion::getLastUpdate).reversed());
|
||||
|
||||
|
||||
@NotNull
|
||||
private final Comparator<CloudExpansion> comparator;
|
||||
|
||||
ExpansionSort(@NotNull final Comparator<CloudExpansion> comparator)
|
||||
{
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final int compare(final CloudExpansion expansion1, final CloudExpansion expansion2)
|
||||
{
|
||||
return comparator.compare(expansion1, expansion2);
|
||||
}
|
||||
|
||||
}
|
@ -23,6 +23,8 @@ package me.clip.placeholderapi.configuration;
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public final class PlaceholderAPIConfig
|
||||
{
|
||||
|
||||
@ -64,6 +66,22 @@ public final class PlaceholderAPIConfig
|
||||
}
|
||||
|
||||
|
||||
public Optional<ExpansionSort> getExpansionSort()
|
||||
{
|
||||
final String option = plugin.getConfig().getString("cloud_sorting", ExpansionSort.LATEST.name());
|
||||
|
||||
try
|
||||
{
|
||||
//noinspection ConstantConditions (bad spigot annotation)
|
||||
return Optional.of(ExpansionSort.valueOf(option.toUpperCase()));
|
||||
}
|
||||
catch (final IllegalArgumentException ignored)
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public String dateFormat()
|
||||
{
|
||||
|
@ -20,180 +20,278 @@
|
||||
|
||||
package me.clip.placeholderapi.expansion;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.PlaceholderHook;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class PlaceholderExpansion extends PlaceholderHook {
|
||||
public abstract class PlaceholderExpansion extends PlaceholderHook
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of this expansion
|
||||
*
|
||||
* @return {@link #getIdentifier()} by default, name of this expansion if specified
|
||||
*/
|
||||
public String getName() {
|
||||
return getIdentifier();
|
||||
}
|
||||
/**
|
||||
* The placeholder identifier of this expansion
|
||||
*
|
||||
* @return placeholder identifier that is associated with this expansion
|
||||
*/
|
||||
@NotNull
|
||||
public abstract String getIdentifier();
|
||||
|
||||
/**
|
||||
* The placeholder identifier of this expansion
|
||||
*
|
||||
* @return placeholder identifier that is associated with this expansion
|
||||
*/
|
||||
public abstract String getIdentifier();
|
||||
/**
|
||||
* The author of this expansion
|
||||
*
|
||||
* @return name of the author for this expansion
|
||||
*/
|
||||
@NotNull
|
||||
public abstract String getAuthor();
|
||||
|
||||
/**
|
||||
* The author of this expansion
|
||||
*
|
||||
* @return name of the author for this expansion
|
||||
*/
|
||||
public abstract String getAuthor();
|
||||
/**
|
||||
* The version of this expansion
|
||||
*
|
||||
* @return current version of this expansion
|
||||
*/
|
||||
@NotNull
|
||||
public abstract String getVersion();
|
||||
|
||||
/**
|
||||
* The version of this expansion
|
||||
*
|
||||
* @return current version of this expansion
|
||||
*/
|
||||
public abstract String getVersion();
|
||||
|
||||
/**
|
||||
* The name of the plugin that this expansion hooks into. by default will return the deprecated
|
||||
* {@link #getPlugin()} method
|
||||
*
|
||||
* @return plugin name that this expansion requires to function
|
||||
*/
|
||||
public String getRequiredPlugin() {
|
||||
return getPlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* The placeholders associated with this expansion
|
||||
*
|
||||
* @return placeholder list that this expansion provides
|
||||
*/
|
||||
public List<String> getPlaceholders() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expansions that do not use the ecloud and instead register from the dependency should set this
|
||||
* to true to ensure that your placeholder expansion is not unregistered when the papi reload
|
||||
* command is used
|
||||
*
|
||||
* @return if this expansion should persist through placeholder reloads
|
||||
*/
|
||||
public boolean persist() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this placeholder identifier has already been registered
|
||||
*
|
||||
* @return true if the identifier for this expansion is already registered
|
||||
*/
|
||||
public boolean isRegistered() {
|
||||
Validate.notNull(getIdentifier(), "Placeholder identifier can not be null!");
|
||||
return PlaceholderAPI.isRegistered(getIdentifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* If any requirements need to be checked before this expansion should register, you can check
|
||||
* them here
|
||||
*
|
||||
* @return true if this hook meets all the requirements to register
|
||||
*/
|
||||
public boolean canRegister() {
|
||||
return getRequiredPlugin() == null || Bukkit.getPluginManager().getPlugin(getRequiredPlugin()) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to register this PlaceholderExpansion
|
||||
*
|
||||
* @return true if this expansion is now registered with PlaceholderAPI
|
||||
*/
|
||||
public boolean register() {
|
||||
Validate.notNull(getIdentifier(), "Placeholder identifier can not be null!");
|
||||
return canRegister() && getPlaceholderAPI().getLocalExpansionManager().register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick getter for the {@link PlaceholderAPIPlugin} instance
|
||||
*
|
||||
* @return {@link PlaceholderAPIPlugin} instance
|
||||
*/
|
||||
public PlaceholderAPIPlugin getPlaceholderAPI() {
|
||||
return PlaceholderAPIPlugin.getInstance();
|
||||
}
|
||||
|
||||
public String getString(String path, String def) {
|
||||
return getPlaceholderAPI().getConfig()
|
||||
.getString("expansions." + getIdentifier() + "." + path, def);
|
||||
}
|
||||
|
||||
public int getInt(String path, int def) {
|
||||
return getPlaceholderAPI().getConfig()
|
||||
.getInt("expansions." + getIdentifier() + "." + path, def);
|
||||
}
|
||||
|
||||
public long getLong(String path, long def) {
|
||||
return getPlaceholderAPI().getConfig()
|
||||
.getLong("expansions." + getIdentifier() + "." + path, def);
|
||||
}
|
||||
|
||||
public double getDouble(String path, double def) {
|
||||
return getPlaceholderAPI().getConfig()
|
||||
.getDouble("expansions." + getIdentifier() + "." + path, def);
|
||||
}
|
||||
|
||||
public List<String> getStringList(String path) {
|
||||
return getPlaceholderAPI().getConfig()
|
||||
.getStringList("expansions." + getIdentifier() + "." + path);
|
||||
}
|
||||
|
||||
public Object get(String path, Object def) {
|
||||
return getPlaceholderAPI().getConfig().get("expansions." + getIdentifier() + "." + path, def);
|
||||
}
|
||||
|
||||
public ConfigurationSection getConfigSection(String path) {
|
||||
return getPlaceholderAPI().getConfig()
|
||||
.getConfigurationSection("expansions." + getIdentifier() + "." + path);
|
||||
}
|
||||
|
||||
public ConfigurationSection getConfigSection() {
|
||||
return getPlaceholderAPI().getConfig().getConfigurationSection("expansions." + getIdentifier());
|
||||
}
|
||||
|
||||
public boolean configurationContains(String path) {
|
||||
return getPlaceholderAPI().getConfig().contains("expansions." + getIdentifier() + "." + path);
|
||||
}
|
||||
@Nullable
|
||||
@Override /* override for now >:) */
|
||||
public String onRequest(@Nullable final OfflinePlayer player, @NotNull final String params)
|
||||
{
|
||||
return super.onRequest(player, params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated As of versions greater than 2.8.7, use {@link #getRequiredPlugin()}
|
||||
*/
|
||||
@Deprecated
|
||||
public String getPlugin() {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* The name of this expansion
|
||||
*
|
||||
* @return {@link #getIdentifier()} by default, name of this expansion if specified
|
||||
*/
|
||||
@NotNull
|
||||
public String getName()
|
||||
{
|
||||
return getIdentifier();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated As of versions greater than 2.8.7, use the expansion cloud to show a description
|
||||
*/
|
||||
@Deprecated
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* The name of the plugin that this expansion hooks into. by default will null
|
||||
*
|
||||
* @return plugin name that this expansion requires to function
|
||||
*/
|
||||
@Nullable
|
||||
public String getRequiredPlugin()
|
||||
{
|
||||
return getPlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* The placeholders associated with this expansion
|
||||
*
|
||||
* @return placeholder list that this expansion provides
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getPlaceholders()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Expansions that do not use the ecloud and instead register from the dependency should set this
|
||||
* to true to ensure that your placeholder expansion is not unregistered when the papi reload
|
||||
* command is used
|
||||
*
|
||||
* @return if this expansion should persist through placeholder reloads
|
||||
*/
|
||||
public boolean persist()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if this placeholder identifier has already been registered
|
||||
*
|
||||
* @return true if the identifier for this expansion is already registered
|
||||
*/
|
||||
public final boolean isRegistered()
|
||||
{
|
||||
return getPlaceholderAPI().getLocalExpansionManager().findExpansionByIdentifier(getIdentifier()).map(it -> it.equals(this)).orElse(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If any requirements need to be checked before this expansion should register, you can check
|
||||
* them here
|
||||
*
|
||||
* @return true if this hook meets all the requirements to register
|
||||
*/
|
||||
public boolean canRegister()
|
||||
{
|
||||
return getRequiredPlugin() == null || Bukkit.getPluginManager().getPlugin(getRequiredPlugin()) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to register this PlaceholderExpansion
|
||||
*
|
||||
* @return true if this expansion is now registered with PlaceholderAPI
|
||||
* @deprecated This is going to be final in the future, startup and shutdown logic will have their own methods soon.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean register()
|
||||
{
|
||||
return canRegister() && getPlaceholderAPI().getLocalExpansionManager().register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to unregister this PlaceholderExpansion
|
||||
*
|
||||
* @return true if this expansion is now unregistered with PlaceholderAPI
|
||||
*/
|
||||
public final boolean unregister()
|
||||
{
|
||||
return getPlaceholderAPI().getLocalExpansionManager().unregister(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quick getter for the {@link PlaceholderAPIPlugin} instance
|
||||
*
|
||||
* @return {@link PlaceholderAPIPlugin} instance
|
||||
*/
|
||||
@NotNull
|
||||
public final PlaceholderAPIPlugin getPlaceholderAPI()
|
||||
{
|
||||
return PlaceholderAPIPlugin.getInstance();
|
||||
}
|
||||
|
||||
|
||||
// === Configuration ===
|
||||
|
||||
@Nullable
|
||||
public final ConfigurationSection getConfigSection()
|
||||
{
|
||||
return getPlaceholderAPI().getConfig().getConfigurationSection("expansions." + getIdentifier());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final ConfigurationSection getConfigSection(@NotNull final String path)
|
||||
{
|
||||
final ConfigurationSection section = getConfigSection();
|
||||
return section == null ? null : section.getConfigurationSection(path);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Contract("_, !null -> !null")
|
||||
public final Object get(@NotNull final String path, final Object def)
|
||||
{
|
||||
final ConfigurationSection section = getConfigSection();
|
||||
return section == null ? def : section.get(path, def);
|
||||
}
|
||||
|
||||
public final int getInt(@NotNull final String path, final int def)
|
||||
{
|
||||
final ConfigurationSection section = getConfigSection();
|
||||
return section == null ? def : section.getInt(path, def);
|
||||
}
|
||||
|
||||
public final long getLong(@NotNull final String path, final long def)
|
||||
{
|
||||
final ConfigurationSection section = getConfigSection();
|
||||
return section == null ? def : section.getLong(path, def);
|
||||
}
|
||||
|
||||
public final double getDouble(@NotNull final String path, final double def)
|
||||
{
|
||||
final ConfigurationSection section = getConfigSection();
|
||||
return section == null ? def : section.getDouble(path, def);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Contract("_, !null -> !null")
|
||||
public final String getString(@NotNull final String path, @Nullable final String def)
|
||||
{
|
||||
final ConfigurationSection section = getConfigSection();
|
||||
return section == null ? def : section.getString(path, def);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public final List<String> getStringList(@NotNull final String path)
|
||||
{
|
||||
final ConfigurationSection section = getConfigSection();
|
||||
return section == null ? Collections.emptyList() : section.getStringList(path);
|
||||
}
|
||||
|
||||
public final boolean configurationContains(@NotNull final String path)
|
||||
{
|
||||
final ConfigurationSection section = getConfigSection();
|
||||
return section != null && section.contains(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(final Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof PlaceholderExpansion))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final PlaceholderExpansion expansion = (PlaceholderExpansion) o;
|
||||
|
||||
return getIdentifier().equals(expansion.getIdentifier()) &&
|
||||
getAuthor().equals(expansion.getAuthor()) &&
|
||||
getVersion().equals(expansion.getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode()
|
||||
{
|
||||
return Objects.hash(getIdentifier(), getAuthor(), getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString()
|
||||
{
|
||||
return String.format("PlaceholderExpansion[name: '%s', author: '%s', version: '%s']", getName(), getAuthor(), getVersion());
|
||||
}
|
||||
|
||||
// === Deprecated API ===
|
||||
|
||||
/**
|
||||
* @deprecated As of versions greater than 2.8.7, use {@link #getRequiredPlugin()}
|
||||
*/
|
||||
@Deprecated
|
||||
public String getPlugin()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated As of versions greater than 2.8.7, use the expansion cloud to show a description
|
||||
*/
|
||||
@Deprecated
|
||||
public String getDescription()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated As of versions greater than 2.8.7, use the expansion cloud to display a link
|
||||
*/
|
||||
@Deprecated
|
||||
public String getLink()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated As of versions greater than 2.8.7, use the expansion cloud to display a link
|
||||
*/
|
||||
@Deprecated
|
||||
public String getLink() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import me.clip.placeholderapi.events.ExpansionUnregisterEvent;
|
||||
import me.clip.placeholderapi.expansion.*;
|
||||
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
||||
import me.clip.placeholderapi.util.FileUtil;
|
||||
import me.clip.placeholderapi.util.Futures;
|
||||
import me.clip.placeholderapi.util.Msg;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -43,8 +44,12 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.logging.Level;
|
||||
@ -134,6 +139,27 @@ public final class LocalExpansionManager implements Listener
|
||||
}
|
||||
|
||||
|
||||
public Optional<PlaceholderExpansion> register(@NotNull final Class<? extends PlaceholderExpansion> clazz)
|
||||
{
|
||||
try
|
||||
{
|
||||
final PlaceholderExpansion expansion = createExpansionInstance(clazz);
|
||||
if (expansion == null || !expansion.register())
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(expansion);
|
||||
}
|
||||
catch (final LinkageError ex)
|
||||
{
|
||||
plugin.getLogger().severe("expansion class " + clazz.getSimpleName() + " is outdated: \n" +
|
||||
"Failed to load due to a [" + ex.getClass().getSimpleName() + "], attempted to use " + ex.getMessage());
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not call this method yourself, use {@link PlaceholderExpansion#register()}
|
||||
*/
|
||||
@ -198,7 +224,7 @@ public final class LocalExpansionManager implements Listener
|
||||
}
|
||||
|
||||
final PlaceholderExpansion removed = expansions.get(expansion.getIdentifier());
|
||||
if (removed != null && !unregister(removed))
|
||||
if (removed != null && !removed.unregister())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -238,27 +264,9 @@ public final class LocalExpansionManager implements Listener
|
||||
return true;
|
||||
}
|
||||
|
||||
public Optional<PlaceholderExpansion> register(@NotNull final Class<? extends PlaceholderExpansion> clazz)
|
||||
{
|
||||
try
|
||||
{
|
||||
final PlaceholderExpansion expansion = createExpansionInstance(clazz);
|
||||
if (expansion == null || !expansion.register())
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(expansion);
|
||||
}
|
||||
catch (final LinkageError ex)
|
||||
{
|
||||
plugin.getLogger().severe("expansion class " + clazz.getSimpleName() + " is outdated: \n" +
|
||||
"Failed to load due to a [" + ex.getClass().getSimpleName() + "], attempted to use " + ex.getMessage());
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not call this method yourself, use {@link PlaceholderExpansion#unregister()}
|
||||
*/
|
||||
public boolean unregister(@NotNull final PlaceholderExpansion expansion)
|
||||
{
|
||||
if (expansions.remove(expansion.getIdentifier()) == null)
|
||||
@ -301,18 +309,17 @@ public final class LocalExpansionManager implements Listener
|
||||
{
|
||||
plugin.getLogger().info("Placeholder expansion registration initializing...");
|
||||
|
||||
findExpansionsOnDisk().whenCompleteAsync((classes, exception) -> {
|
||||
Futures.onMainThread(plugin, findExpansionsOnDisk(), (classes, exception) -> {
|
||||
if (exception != null)
|
||||
{
|
||||
plugin.getLogger().log(Level.SEVERE, "failed to load class files of expansions", exception);
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
final long registered = classes.stream().map(this::register).filter(Optional::isPresent).count();
|
||||
Msg.msg(sender,
|
||||
registered == 0 ? "&6No expansions were registered!" : registered + "&a placeholder hooks successfully registered!");
|
||||
});
|
||||
final long registered = classes.stream().map(this::register).filter(Optional::isPresent).count();
|
||||
|
||||
Msg.msg(sender,
|
||||
registered == 0 ? "&6No expansions were registered!" : registered + "&a placeholder hooks successfully registered!");
|
||||
});
|
||||
}
|
||||
|
||||
@ -325,39 +332,32 @@ public final class LocalExpansionManager implements Listener
|
||||
continue;
|
||||
}
|
||||
|
||||
unregister(expansion);
|
||||
expansion.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public CompletableFuture<List<Class<? extends PlaceholderExpansion>>> findExpansionsOnDisk()
|
||||
public CompletableFuture<@NotNull List<@NotNull Class<? extends PlaceholderExpansion>>> findExpansionsOnDisk()
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try
|
||||
{
|
||||
return FileUtil.getClasses(getExpansionsFolder(), PlaceholderExpansion.class);
|
||||
}
|
||||
catch (final IOException | ClassNotFoundException ex)
|
||||
{
|
||||
throw new CompletionException(ex);
|
||||
}
|
||||
});
|
||||
return Arrays.stream(folder.listFiles((dir, name) -> name.endsWith(".jar")))
|
||||
.map(this::findExpansionInFile)
|
||||
.collect(Futures.collector());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CompletableFuture<List<Class<? extends PlaceholderExpansion>>> findExpansionsInFile(@NotNull final File file)
|
||||
public CompletableFuture<@Nullable Class<? extends PlaceholderExpansion>> findExpansionInFile(@NotNull final File file)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try
|
||||
{
|
||||
final List<@NotNull Class<? extends PlaceholderExpansion>> classes = FileUtil.getClasses(getExpansionsFolder(), PlaceholderExpansion.class, file.getName());
|
||||
if (classes.size() > 1)
|
||||
{
|
||||
throw new IllegalStateException("multiple expansion classes in one file!");
|
||||
}
|
||||
|
||||
return classes;
|
||||
return FileUtil.findClass(file, PlaceholderExpansion.class);
|
||||
}
|
||||
catch (final VerifyError ex)
|
||||
{
|
||||
plugin.getLogger().severe("expansion file " + file.getName() + " is outdated: \n" +
|
||||
"Failed to load due to a [" + ex.getClass().getSimpleName() + "], attempted to use" + ex.getMessage().substring(ex.getMessage().lastIndexOf(' ')));
|
||||
return null;
|
||||
}
|
||||
catch (final Exception ex)
|
||||
{
|
||||
@ -376,6 +376,11 @@ public final class LocalExpansionManager implements Listener
|
||||
}
|
||||
catch (final Exception ex)
|
||||
{
|
||||
if (ex.getCause() instanceof LinkageError)
|
||||
{
|
||||
throw ((LinkageError) ex.getCause());
|
||||
}
|
||||
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to load placeholder expansion from class: " + clazz.getName(), ex);
|
||||
return null;
|
||||
}
|
||||
@ -412,7 +417,7 @@ public final class LocalExpansionManager implements Listener
|
||||
continue;
|
||||
}
|
||||
|
||||
unregister(expansion);
|
||||
expansion.unregister();
|
||||
plugin.getLogger().info("Unregistered placeholder expansion: " + expansion.getName());
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
package me.clip.placeholderapi.replacer;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderHook;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -40,8 +40,9 @@ public final class CharsReplacer implements Replacer
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public @NotNull String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function<String, @Nullable PlaceholderHook> lookup)
|
||||
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());
|
||||
@ -162,7 +163,7 @@ public final class CharsReplacer implements Replacer
|
||||
continue;
|
||||
}
|
||||
|
||||
final PlaceholderHook placeholder = lookup.apply(identifierString);
|
||||
final PlaceholderExpansion placeholder = lookup.apply(identifierString);
|
||||
if (placeholder == null)
|
||||
{
|
||||
builder.append(closure.head).append(identifierString);
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
package me.clip.placeholderapi.replacer;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderHook;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -42,8 +42,9 @@ public final class RegexReplacer implements Replacer
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public @NotNull String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function<String, @Nullable PlaceholderHook> lookup)
|
||||
public String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function<String, @Nullable PlaceholderExpansion> lookup)
|
||||
{
|
||||
final Matcher matcher = pattern.matcher(text);
|
||||
if (!matcher.find())
|
||||
@ -58,13 +59,13 @@ public final class RegexReplacer implements Replacer
|
||||
final String identifier = matcher.group("identifier");
|
||||
final String parameters = matcher.group("parameters");
|
||||
|
||||
final PlaceholderHook hook = lookup.apply(identifier);
|
||||
if (hook == null)
|
||||
final PlaceholderExpansion expansion = lookup.apply(identifier);
|
||||
if (expansion == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final String requested = hook.onRequest(player, parameters);
|
||||
final String requested = expansion.onRequest(player, parameters);
|
||||
matcher.appendReplacement(builder, requested != null ? requested : matcher.group(0));
|
||||
}
|
||||
while (matcher.find());
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
package me.clip.placeholderapi.replacer;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderHook;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -31,7 +31,7 @@ public interface Replacer
|
||||
{
|
||||
|
||||
@NotNull
|
||||
String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function<String, @Nullable PlaceholderHook> lookup);
|
||||
String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function<String, @Nullable PlaceholderExpansion> lookup);
|
||||
|
||||
|
||||
enum Closure
|
||||
|
@ -27,47 +27,22 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
public class FileUtil
|
||||
{
|
||||
|
||||
@NotNull
|
||||
public static <T> List<@NotNull Class<? extends T>> getClasses(@NotNull final File folder, @NotNull final Class<T> clazz) throws IOException, ClassNotFoundException
|
||||
@Nullable
|
||||
public static <T> Class<? extends T> findClass(@NotNull final File file, @NotNull final Class<T> clazz) throws IOException, ClassNotFoundException
|
||||
{
|
||||
return getClasses(folder, clazz, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> List<@NotNull Class<? extends T>> getClasses(@NotNull final File folder, @NotNull final Class<T> clazz, @Nullable final String target) throws IOException, ClassNotFoundException
|
||||
{
|
||||
if (!folder.exists())
|
||||
if (!file.exists())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
return null;
|
||||
}
|
||||
|
||||
final File[] jars = folder.listFiles((dir, name) -> name.endsWith(".jar") && (target == null || name.replace(".jar", "").equalsIgnoreCase(target.replace(".jar", ""))));
|
||||
if (jars == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final URL jar = file.toURI().toURL();
|
||||
|
||||
final List<@NotNull Class<? extends T>> list = new ArrayList<>();
|
||||
|
||||
for (final File file : jars)
|
||||
{
|
||||
gather(file.toURI().toURL(), clazz, list);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static <T> void gather(@NotNull final URL jar, @NotNull final Class<T> clazz, @NotNull final List<@NotNull Class<? extends T>> list) throws IOException, ClassNotFoundException
|
||||
{
|
||||
try (final URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader()); final JarInputStream stream = new JarInputStream(jar.openStream()))
|
||||
{
|
||||
JarEntry entry;
|
||||
@ -84,7 +59,7 @@ public class FileUtil
|
||||
final Class<?> loaded = loader.loadClass(name.substring(0, name.lastIndexOf('.')).replace('/', '.'));
|
||||
if (clazz.isAssignableFrom(loaded))
|
||||
{
|
||||
list.add(loaded.asSubclass(clazz));
|
||||
return loaded.asSubclass(clazz);
|
||||
}
|
||||
}
|
||||
catch (final NoClassDefFoundError ignored)
|
||||
@ -92,6 +67,8 @@ public class FileUtil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
63
src/main/java/me/clip/placeholderapi/util/Futures.java
Normal file
63
src/main/java/me/clip/placeholderapi/util/Futures.java
Normal file
@ -0,0 +1,63 @@
|
||||
package me.clip.placeholderapi.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class Futures
|
||||
{
|
||||
|
||||
private Futures()
|
||||
{}
|
||||
|
||||
|
||||
public static <T> void onMainThread(@NotNull final Plugin plugin, @NotNull final CompletableFuture<T> future, @NotNull final BiConsumer<T, Throwable> consumer)
|
||||
{
|
||||
future.whenComplete((value, exception) -> {
|
||||
if (Bukkit.isPrimaryThread())
|
||||
{
|
||||
consumer.accept(value, exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
Bukkit.getScheduler().runTask(plugin, () -> consumer.accept(value, exception));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static <T> Collector<CompletableFuture<T>, ?, CompletableFuture<List<T>>> collector()
|
||||
{
|
||||
return Collectors.collectingAndThen(Collectors.toList(), Futures::of);
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static <T> CompletableFuture<List<T>> of(@NotNull final Stream<CompletableFuture<T>> futures)
|
||||
{
|
||||
return of(futures.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> CompletableFuture<List<T>> of(@NotNull final Collection<CompletableFuture<T>> futures)
|
||||
{
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||
.thenApplyAsync($ -> awaitCompletion(futures));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static <T> List<T> awaitCompletion(@NotNull final Collection<CompletableFuture<T>> futures)
|
||||
{
|
||||
return futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
# Download placeholders: /papi ecloud
|
||||
check_updates: true
|
||||
cloud_enabled: true
|
||||
cloud_sorting: "name"
|
||||
cloud_allow_unverified_expansions: false
|
||||
boolean:
|
||||
'true': 'yes'
|
||||
|
@ -21,165 +21,61 @@
|
||||
package me.clip.placeholderapi;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.clip.placeholderapi.replacer.CharsReplacer;
|
||||
import me.clip.placeholderapi.replacer.RegexReplacer;
|
||||
import me.clip.placeholderapi.replacer.Replacer;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface Values
|
||||
{
|
||||
|
||||
String SMALL_TEXT = "My name is %player_name%";
|
||||
String LARGE_TEXT = "My name is %player_name% and my location is (%player_x%, %player_y%, %player_z%), this placeholder is invalid %server_name%";
|
||||
|
||||
ImmutableMap<String, PlaceholderHook> PLACEHOLDERS = ImmutableMap.<String, PlaceholderHook>builder()
|
||||
.put("player", new MockPlayerPlaceholderHook())
|
||||
ImmutableMap<String, PlaceholderExpansion> PLACEHOLDERS = ImmutableMap.<String, PlaceholderExpansion>builder()
|
||||
.put("player", new MockPlayerPlaceholderExpansion())
|
||||
.build();
|
||||
|
||||
|
||||
Replacer CHARS_REPLACER = new CharsReplacer(Replacer.Closure.PERCENT);
|
||||
Replacer REGEX_REPLACER = new RegexReplacer(Replacer.Closure.PERCENT);
|
||||
Replacer TESTS_REPLACER = new Replacer()
|
||||
{
|
||||
private final Set<Character> COLOR_CODES = ImmutableSet.of('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'k', 'l', 'm', 'o', 'r', 'x');
|
||||
|
||||
private final boolean colorize = true;
|
||||
private final Replacer.Closure closure = Replacer.Closure.PERCENT;
|
||||
|
||||
@Override
|
||||
public @NotNull String apply(final @NotNull String text, final @Nullable OfflinePlayer player, final @NotNull Function<String, @Nullable PlaceholderHook> lookup)
|
||||
{
|
||||
char[] chars = text.toCharArray();
|
||||
StringBuilder builder = new StringBuilder(chars.length);
|
||||
|
||||
// This won't cause memory leaks. It's inside a method. And we want to use setLength instead of
|
||||
// creating a new string builder to use the maximum capacity and avoid initializing new objects.
|
||||
StringBuilder identifier = new StringBuilder(50);
|
||||
PlaceholderHook handler = null;
|
||||
|
||||
// Stages:
|
||||
// Stage -1: Look for the color code in the next character.
|
||||
// Stage 0: No closures detected, or the detected identifier is invalid. We're going forward while appending the characters normally.
|
||||
// Stage 1: The closure has been detected, looking for the placeholder identifier...
|
||||
// Stage 2: Detected the identifier and the parameter. Translating the placeholder...
|
||||
int stage = 0;
|
||||
|
||||
for (char ch : chars)
|
||||
{
|
||||
if (stage == -1 && COLOR_CODES.contains(ch))
|
||||
{
|
||||
builder.append(ChatColor.COLOR_CHAR).append(ch);
|
||||
stage = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the placeholder starts or ends.
|
||||
if (ch == closure.head || ch == closure.tail)
|
||||
{
|
||||
// If the placeholder ends.
|
||||
if (stage == 2)
|
||||
{
|
||||
String parameter = identifier.toString();
|
||||
String translated = handler.onRequest(player, parameter);
|
||||
|
||||
if (translated == null)
|
||||
{
|
||||
String name = "";
|
||||
builder.append(closure.head).append(name).append('_').append(parameter).append(closure.tail);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.append(translated);
|
||||
}
|
||||
|
||||
identifier.setLength(0);
|
||||
stage = 0;
|
||||
continue;
|
||||
}
|
||||
else if (stage == 1)
|
||||
{ // If it just started | Double closures | If it's still hasn't detected the indentifier, reset.
|
||||
builder.append(closure.head).append(identifier);
|
||||
}
|
||||
|
||||
identifier.setLength(0);
|
||||
stage = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Placeholder identifier started.
|
||||
if (stage == 1)
|
||||
{
|
||||
// Compare the current character with the idenfitier's.
|
||||
// We reached the end of our identifier.
|
||||
if (ch == '_')
|
||||
{
|
||||
handler = lookup.apply(identifier.toString());
|
||||
if (handler == null)
|
||||
{
|
||||
builder.append(closure.head).append(identifier).append('_');
|
||||
stage = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
identifier.setLength(0);
|
||||
stage = 2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep building the identifier name.
|
||||
identifier.append(ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Building the placeholder parameter.
|
||||
if (stage == 2)
|
||||
{
|
||||
identifier.append(ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Nothing placeholder related was found.
|
||||
if (colorize && ch == '&')
|
||||
{
|
||||
stage = -1;
|
||||
continue;
|
||||
}
|
||||
builder.append(ch);
|
||||
}
|
||||
|
||||
if (identifier != null)
|
||||
{
|
||||
if (stage > 0)
|
||||
{
|
||||
builder.append(closure.tail);
|
||||
}
|
||||
builder.append(identifier);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
final class MockPlayerPlaceholderHook extends PlaceholderHook
|
||||
final class MockPlayerPlaceholderExpansion extends PlaceholderExpansion
|
||||
{
|
||||
|
||||
public static final String PLAYER_X = String.valueOf(10);
|
||||
public static final String PLAYER_Y = String.valueOf(20);
|
||||
public static final String PLAYER_Z = String.valueOf(30);
|
||||
public static final String PLAYER_X = "10";
|
||||
public static final String PLAYER_Y = "20";
|
||||
public static final String PLAYER_Z = "30";
|
||||
public static final String PLAYER_NAME = "Sxtanna";
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String onRequest(final OfflinePlayer player, final String params)
|
||||
public String getIdentifier()
|
||||
{
|
||||
return "player";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getAuthor()
|
||||
{
|
||||
return "Sxtanna";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getVersion()
|
||||
{
|
||||
return "1.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onRequest(@Nullable final OfflinePlayer player, @NotNull final String params)
|
||||
{
|
||||
final String[] parts = params.split("_");
|
||||
if (parts.length == 0)
|
||||
|
@ -23,7 +23,10 @@ package me.clip.placeholderapi.replacer;
|
||||
import me.clip.placeholderapi.Values;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderHook.*;
|
||||
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_NAME;
|
||||
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_X;
|
||||
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_Y;
|
||||
import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_Z;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public final class ReplacerUnitTester
|
||||
|
Loading…
Reference in New Issue
Block a user