mirror of
https://github.com/PlaceholderAPI/PlaceholderAPI
synced 2024-11-18 00:46:55 +01:00
rewrote discovery and registration code to be composable and higher level
This commit is contained in:
parent
ee33de5ec8
commit
8360511c50
@ -5,16 +5,16 @@ 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;
|
||||||
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
||||||
|
import me.clip.placeholderapi.util.Futures;
|
||||||
import me.clip.placeholderapi.util.Msg;
|
import me.clip.placeholderapi.util.Msg;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
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;
|
||||||
@ -67,9 +67,8 @@ public final class CommandECloudUpdate extends PlaceholderCommand
|
|||||||
Msg.msg(sender,
|
Msg.msg(sender,
|
||||||
"&aUpdating expansions: " + expansions.stream().map(CloudExpansion::getName).collect(Collectors.joining("&7, &6", "&8[&6", "&8]&r")));
|
"&aUpdating expansions: " + expansions.stream().map(CloudExpansion::getName).collect(Collectors.joining("&7, &6", "&8[&6", "&8]&r")));
|
||||||
|
|
||||||
downloadExpansions(plugin, expansions)
|
|
||||||
.thenCompose(files -> discoverExpansions(plugin, files))
|
Futures.onMainThread(plugin, downloadAndDiscover(expansions, plugin), (classes, exception) -> {
|
||||||
.whenComplete((classes, exception) -> {
|
|
||||||
if (exception != null)
|
if (exception != null)
|
||||||
{
|
{
|
||||||
Msg.msg(sender,
|
Msg.msg(sender,
|
||||||
@ -80,17 +79,18 @@ public final class CommandECloudUpdate extends PlaceholderCommand
|
|||||||
Msg.msg(sender,
|
Msg.msg(sender,
|
||||||
"&aSuccessfully downloaded updates, registering new versions.");
|
"&aSuccessfully downloaded updates, registering new versions.");
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
|
||||||
final String message = classes.stream()
|
final String message = classes.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
.map(plugin.getLocalExpansionManager()::register)
|
.map(plugin.getLocalExpansionManager()::register)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.map(expansion -> " &a" + expansion.getName() + " &f" + expansion.getVersion())
|
.map(expansion -> " &a" + expansion.getName() + " &f" + expansion.getVersion())
|
||||||
.collect(Collectors.joining("\n"));
|
.collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
Msg.msg(sender,
|
Msg.msg(sender,
|
||||||
"&7Registered expansions:",
|
"&7Registered expansions:", message);
|
||||||
message);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,22 +114,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()
|
return expansions.stream()
|
||||||
.map(expansion -> plugin.getCloudExpansionManager().downloadExpansion(expansion, expansion.getVersion()))
|
.map(expansion -> plugin.getCloudExpansionManager().downloadExpansion(expansion, expansion.getVersion()))
|
||||||
.collect(Collectors.toList());
|
.map(future -> future.thenCompose(plugin.getLocalExpansionManager()::findExpansionInFile))
|
||||||
|
.collect(Futures.collector());
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ 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;
|
||||||
import me.clip.placeholderapi.expansion.manager.LocalExpansionManager;
|
import me.clip.placeholderapi.expansion.manager.LocalExpansionManager;
|
||||||
|
import me.clip.placeholderapi.util.Futures;
|
||||||
import me.clip.placeholderapi.util.Msg;
|
import me.clip.placeholderapi.util.Msg;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
@ -45,7 +45,7 @@ public final class CommandExpansionRegister extends PlaceholderCommand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.findExpansionsInFile(file).whenComplete((classes, exception) -> {
|
Futures.onMainThread(plugin, manager.findExpansionInFile(file), (clazz, exception) -> {
|
||||||
if (exception != null)
|
if (exception != null)
|
||||||
{
|
{
|
||||||
Msg.msg(sender,
|
Msg.msg(sender,
|
||||||
@ -55,15 +55,15 @@ public final class CommandExpansionRegister extends PlaceholderCommand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classes.isEmpty())
|
if (clazz == null)
|
||||||
{
|
{
|
||||||
Msg.msg(sender,
|
Msg.msg(sender,
|
||||||
"&cNo expansion class found in file: &f" + file);
|
"&cNo expansion class found in file: &f" + file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
|
||||||
final Optional<PlaceholderExpansion> expansion = manager.register(classes.get(0));
|
final Optional<PlaceholderExpansion> expansion = manager.register(clazz);
|
||||||
if (!expansion.isPresent())
|
if (!expansion.isPresent())
|
||||||
{
|
{
|
||||||
Msg.msg(sender,
|
Msg.msg(sender,
|
||||||
@ -73,7 +73,7 @@ public final class CommandExpansionRegister extends PlaceholderCommand
|
|||||||
|
|
||||||
Msg.msg(sender,
|
Msg.msg(sender,
|
||||||
"&aSuccessfully registered expansion: &f" + expansion.get().getName());
|
"&aSuccessfully registered expansion: &f" + expansion.get().getName());
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import me.clip.placeholderapi.expansion.Taskable;
|
|||||||
import me.clip.placeholderapi.expansion.VersionSpecific;
|
import me.clip.placeholderapi.expansion.VersionSpecific;
|
||||||
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
import me.clip.placeholderapi.expansion.cloud.CloudExpansion;
|
||||||
import me.clip.placeholderapi.util.FileUtil;
|
import me.clip.placeholderapi.util.FileUtil;
|
||||||
|
import me.clip.placeholderapi.util.Futures;
|
||||||
import me.clip.placeholderapi.util.Msg;
|
import me.clip.placeholderapi.util.Msg;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
@ -28,7 +29,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.jetbrains.annotations.Unmodifiable;
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -290,19 +291,18 @@ public final class LocalExpansionManager implements Listener
|
|||||||
{
|
{
|
||||||
plugin.getLogger().info("Placeholder expansion registration initializing...");
|
plugin.getLogger().info("Placeholder expansion registration initializing...");
|
||||||
|
|
||||||
findExpansionsOnDisk().whenCompleteAsync((classes, exception) -> {
|
Futures.onMainThread(plugin, findExpansionsOnDisk(), (classes, exception) -> {
|
||||||
if (exception != null)
|
if (exception != null)
|
||||||
{
|
{
|
||||||
plugin.getLogger().log(Level.SEVERE, "failed to load class files of expansions", exception);
|
plugin.getLogger().log(Level.SEVERE, "failed to load class files of expansions", exception);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
|
||||||
final long registered = classes.stream().map(this::register).filter(Optional::isPresent).count();
|
final long registered = classes.stream().map(this::register).filter(Optional::isPresent).count();
|
||||||
|
|
||||||
Msg.msg(sender,
|
Msg.msg(sender,
|
||||||
registered == 0 ? "&6No expansions were registered!" : registered + "&a placeholder hooks successfully registered!");
|
registered == 0 ? "&6No expansions were registered!" : registered + "&a placeholder hooks successfully registered!");
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unregisterAll()
|
private void unregisterAll()
|
||||||
@ -320,33 +320,26 @@ public final class LocalExpansionManager implements Listener
|
|||||||
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public CompletableFuture<List<Class<? extends PlaceholderExpansion>>> findExpansionsOnDisk()
|
public CompletableFuture<@NotNull List<@NotNull Class<? extends PlaceholderExpansion>>> findExpansionsOnDisk()
|
||||||
{
|
{
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
return Arrays.stream(folder.listFiles((dir, name) -> name.endsWith(".jar")))
|
||||||
try
|
.map(this::findExpansionInFile)
|
||||||
{
|
.collect(Futures.collector());
|
||||||
return FileUtil.getClasses(getExpansionsFolder(), PlaceholderExpansion.class);
|
|
||||||
}
|
|
||||||
catch (final IOException | ClassNotFoundException ex)
|
|
||||||
{
|
|
||||||
throw new CompletionException(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@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(() -> {
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
final List<@NotNull Class<? extends PlaceholderExpansion>> classes = FileUtil.getClasses(getExpansionsFolder(), PlaceholderExpansion.class, file.getName());
|
return FileUtil.findClass(file, PlaceholderExpansion.class);
|
||||||
if (classes.size() > 1)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("multiple expansion classes in one file!");
|
|
||||||
}
|
}
|
||||||
|
catch (final VerifyError ex)
|
||||||
return classes;
|
{
|
||||||
|
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)
|
catch (final Exception ex)
|
||||||
{
|
{
|
||||||
@ -365,6 +358,11 @@ public final class LocalExpansionManager implements Listener
|
|||||||
}
|
}
|
||||||
catch (final Exception ex)
|
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);
|
plugin.getLogger().log(Level.SEVERE, "Failed to load placeholder expansion from class: " + clazz.getName(), ex);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -24,51 +24,25 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
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.JarEntry;
|
||||||
import java.util.jar.JarInputStream;
|
import java.util.jar.JarInputStream;
|
||||||
|
|
||||||
public class FileUtil
|
public class FileUtil
|
||||||
{
|
{
|
||||||
|
|
||||||
@NotNull
|
@Nullable
|
||||||
public static <T> List<@NotNull Class<? extends T>> getClasses(@NotNull final File folder, @NotNull final Class<T> clazz) throws IOException, ClassNotFoundException
|
public static <T> Class<? extends T> findClass(@NotNull final File file, @NotNull final Class<T> clazz) throws IOException, ClassNotFoundException
|
||||||
{
|
{
|
||||||
return getClasses(folder, clazz, null);
|
if (!file.exists())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
final URL jar = file.toURI().toURL();
|
||||||
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())
|
|
||||||
{
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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()))
|
try (final URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader()); final JarInputStream stream = new JarInputStream(jar.openStream()))
|
||||||
{
|
{
|
||||||
JarEntry entry;
|
JarEntry entry;
|
||||||
@ -85,7 +59,7 @@ public class FileUtil
|
|||||||
final Class<?> loaded = loader.loadClass(name.substring(0, name.lastIndexOf('.')).replace('/', '.'));
|
final Class<?> loaded = loader.loadClass(name.substring(0, name.lastIndexOf('.')).replace('/', '.'));
|
||||||
if (clazz.isAssignableFrom(loaded))
|
if (clazz.isAssignableFrom(loaded))
|
||||||
{
|
{
|
||||||
list.add(loaded.asSubclass(clazz));
|
return loaded.asSubclass(clazz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (final NoClassDefFoundError ignored)
|
catch (final NoClassDefFoundError ignored)
|
||||||
@ -93,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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user