geforkt von Mirrors/Velocity
Merge branch 'dev/1.1.0' into decode-multiple
Dieser Commit ist enthalten in:
Commit
932f3bc2bf
@ -114,7 +114,7 @@ public final class ServerPing {
|
|||||||
if (players != null) {
|
if (players != null) {
|
||||||
builder.onlinePlayers = players.online;
|
builder.onlinePlayers = players.online;
|
||||||
builder.maximumPlayers = players.max;
|
builder.maximumPlayers = players.max;
|
||||||
builder.samplePlayers.addAll(players.sample);
|
builder.samplePlayers.addAll(players.getSample());
|
||||||
} else {
|
} else {
|
||||||
builder.nullOutPlayers = true;
|
builder.nullOutPlayers = true;
|
||||||
}
|
}
|
||||||
@ -367,7 +367,7 @@ public final class ServerPing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<SamplePlayer> getSample() {
|
public List<SamplePlayer> getSample() {
|
||||||
return sample;
|
return sample == null ? ImmutableList.of() : sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -35,7 +35,7 @@ import org.asynchttpclient.Response;
|
|||||||
public class Metrics {
|
public class Metrics {
|
||||||
|
|
||||||
// The version of this bStats class
|
// The version of this bStats class
|
||||||
private static final int B_STATS_VERSION = 1;
|
private static final int B_STATS_METRICS_REVISION = 2;
|
||||||
|
|
||||||
// The url to which the data is sent
|
// The url to which the data is sent
|
||||||
private static final String URL = "https://bstats.org/submitData/server-implementation";
|
private static final String URL = "https://bstats.org/submitData/server-implementation";
|
||||||
@ -49,6 +49,9 @@ public class Metrics {
|
|||||||
// The name of the server software
|
// The name of the server software
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
// The plugin ID for the server software as assigned by bStats.
|
||||||
|
private final int pluginId;
|
||||||
|
|
||||||
// The uuid of the server
|
// The uuid of the server
|
||||||
private final String serverUuid;
|
private final String serverUuid;
|
||||||
|
|
||||||
@ -60,13 +63,15 @@ public class Metrics {
|
|||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
* @param name The name of the server software.
|
* @param name The name of the server software.
|
||||||
|
* @param pluginId The plugin ID for the server software as assigned by bStats.
|
||||||
* @param serverUuid The uuid of the server.
|
* @param serverUuid The uuid of the server.
|
||||||
* @param logFailedRequests Whether failed requests should be logged or not.
|
* @param logFailedRequests Whether failed requests should be logged or not.
|
||||||
* @param server The Velocity server instance.
|
* @param server The Velocity server instance.
|
||||||
*/
|
*/
|
||||||
private Metrics(String name, String serverUuid, boolean logFailedRequests,
|
private Metrics(String name, int pluginId, String serverUuid, boolean logFailedRequests,
|
||||||
VelocityServer server) {
|
VelocityServer server) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.pluginId = pluginId;
|
||||||
this.serverUuid = serverUuid;
|
this.serverUuid = serverUuid;
|
||||||
Metrics.logFailedRequests = logFailedRequests;
|
Metrics.logFailedRequests = logFailedRequests;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
@ -114,6 +119,8 @@ public class Metrics {
|
|||||||
JsonObject data = new JsonObject();
|
JsonObject data = new JsonObject();
|
||||||
|
|
||||||
data.addProperty("pluginName", name); // Append the name of the server software
|
data.addProperty("pluginName", name); // Append the name of the server software
|
||||||
|
data.addProperty("id", pluginId);
|
||||||
|
data.addProperty("metricsRevision", B_STATS_METRICS_REVISION);
|
||||||
JsonArray customCharts = new JsonArray();
|
JsonArray customCharts = new JsonArray();
|
||||||
for (CustomChart customChart : charts) {
|
for (CustomChart customChart : charts) {
|
||||||
// Add the data of the custom charts
|
// Add the data of the custom charts
|
||||||
@ -573,7 +580,7 @@ public class Metrics {
|
|||||||
boolean logFailedRequests = metricsConfig.isLogFailure();
|
boolean logFailedRequests = metricsConfig.isLogFailure();
|
||||||
// Only start Metrics, if it's enabled in the config
|
// Only start Metrics, if it's enabled in the config
|
||||||
if (metricsConfig.isEnabled()) {
|
if (metricsConfig.isEnabled()) {
|
||||||
Metrics metrics = new Metrics("Velocity", serverUuid, logFailedRequests, server);
|
Metrics metrics = new Metrics("Velocity", 4752, serverUuid, logFailedRequests, server);
|
||||||
|
|
||||||
metrics.addCustomChart(
|
metrics.addCustomChart(
|
||||||
new Metrics.SingleLineChart("players", server::getPlayerCount)
|
new Metrics.SingleLineChart("players", server::getPlayerCount)
|
||||||
|
@ -360,11 +360,12 @@ public class VelocityServer implements ProxyServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuts down the proxy.
|
* Shuts down the proxy, kicking players with the specified {@param reason}.
|
||||||
*
|
*
|
||||||
* @param explicitExit whether the user explicitly shut down the proxy
|
* @param explicitExit whether the user explicitly shut down the proxy
|
||||||
|
* @param reason message to kick online players with
|
||||||
*/
|
*/
|
||||||
public void shutdown(boolean explicitExit) {
|
public void shutdown(boolean explicitExit, TextComponent reason) {
|
||||||
if (eventManager == null || pluginManager == null || cm == null || scheduler == null) {
|
if (eventManager == null || pluginManager == null || cm == null || scheduler == null) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
@ -382,7 +383,7 @@ public class VelocityServer implements ProxyServer {
|
|||||||
|
|
||||||
ImmutableList<ConnectedPlayer> players = ImmutableList.copyOf(connectionsByUuid.values());
|
ImmutableList<ConnectedPlayer> players = ImmutableList.copyOf(connectionsByUuid.values());
|
||||||
for (ConnectedPlayer player : players) {
|
for (ConnectedPlayer player : players) {
|
||||||
player.disconnect(TextComponent.of("Proxy shutting down."));
|
player.disconnect(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -425,6 +426,15 @@ public class VelocityServer implements ProxyServer {
|
|||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link #shutdown(boolean, TextComponent)} with the default reason "Proxy shutting down."
|
||||||
|
*
|
||||||
|
* @param explicitExit whether the user explicitly shut down the proxy
|
||||||
|
*/
|
||||||
|
public void shutdown(boolean explicitExit) {
|
||||||
|
shutdown(explicitExit, TextComponent.of("Proxy shutting down."));
|
||||||
|
}
|
||||||
|
|
||||||
public AsyncHttpClient getAsyncHttpClient() {
|
public AsyncHttpClient getAsyncHttpClient() {
|
||||||
return ensureInitialized(cm).getHttpClient();
|
return ensureInitialized(cm).getHttpClient();
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import com.velocitypowered.api.command.CommandSource;
|
|||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import net.kyori.text.TextComponent;
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.format.TextColor;
|
import net.kyori.text.format.TextColor;
|
||||||
|
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
public class ShutdownCommand implements Command {
|
public class ShutdownCommand implements Command {
|
||||||
@ -17,7 +18,12 @@ public class ShutdownCommand implements Command {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
public void execute(CommandSource source, String @NonNull [] args) {
|
||||||
|
if (args.length == 0) {
|
||||||
server.shutdown(true);
|
server.shutdown(true);
|
||||||
|
} else {
|
||||||
|
String reason = String.join(" ", args);
|
||||||
|
server.shutdown(true, LegacyComponentSerializer.legacy().deserialize(reason, '&'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,6 +16,7 @@ import com.velocitypowered.natives.encryption.VelocityCipher;
|
|||||||
import com.velocitypowered.natives.encryption.VelocityCipherFactory;
|
import com.velocitypowered.natives.encryption.VelocityCipherFactory;
|
||||||
import com.velocitypowered.natives.util.Natives;
|
import com.velocitypowered.natives.util.Natives;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
|
import com.velocitypowered.proxy.network.netty.DiscardHandler;
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftCipherDecoder;
|
import com.velocitypowered.proxy.protocol.netty.MinecraftCipherDecoder;
|
||||||
@ -151,6 +152,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.pipeline().addBefore(MINECRAFT_DECODER, "discard", DiscardHandler.HANDLER);
|
||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.velocitypowered.proxy.network.netty;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandler.Sharable;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
|
||||||
|
@Sharable
|
||||||
|
public class DiscardHandler extends ChannelInboundHandlerAdapter {
|
||||||
|
|
||||||
|
public static final DiscardHandler HANDLER = new DiscardHandler();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
|
ReferenceCountUtil.release(msg);
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,19 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.name.Names;
|
||||||
|
import com.velocitypowered.api.command.CommandManager;
|
||||||
|
import com.velocitypowered.api.event.EventManager;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
import com.velocitypowered.api.plugin.PluginManager;
|
import com.velocitypowered.api.plugin.PluginManager;
|
||||||
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
||||||
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
||||||
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
|
||||||
import com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader;
|
import com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader;
|
||||||
import com.velocitypowered.proxy.plugin.util.PluginDependencyUtils;
|
import com.velocitypowered.proxy.plugin.util.PluginDependencyUtils;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -18,12 +26,13 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
@ -61,7 +70,7 @@ public class VelocityPluginManager implements PluginManager {
|
|||||||
.newDirectoryStream(directory, p -> p.toFile().isFile() && p.toString().endsWith(".jar"))) {
|
.newDirectoryStream(directory, p -> p.toFile().isFile() && p.toString().endsWith(".jar"))) {
|
||||||
for (Path path : stream) {
|
for (Path path : stream) {
|
||||||
try {
|
try {
|
||||||
found.add(loader.loadPlugin(path));
|
found.add(loader.loadPluginDescription(path));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Unable to load plugin {}", path, e);
|
logger.error("Unable to load plugin {}", path, e);
|
||||||
}
|
}
|
||||||
@ -73,38 +82,62 @@ public class VelocityPluginManager implements PluginManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the loaded plugins twice. First, sort the already-loaded plugins by their IDs, so as
|
|
||||||
// to make the topographic sort deterministic (since the order will differ depending on the
|
|
||||||
// first node chosen in the graph, which is the first plugin we found). Afterwards, we execute
|
|
||||||
// a depth-first search over the loaded plugins.
|
|
||||||
found.sort(Comparator.comparing(PluginDescription::getId));
|
|
||||||
List<PluginDescription> sortedPlugins = PluginDependencyUtils.sortCandidates(found);
|
List<PluginDescription> sortedPlugins = PluginDependencyUtils.sortCandidates(found);
|
||||||
|
|
||||||
|
Set<String> loadedPluginsById = new HashSet<>();
|
||||||
|
Map<PluginContainer, Module> pluginContainers = new HashMap<>();
|
||||||
// Now load the plugins
|
// Now load the plugins
|
||||||
pluginLoad:
|
pluginLoad:
|
||||||
for (PluginDescription plugin : sortedPlugins) {
|
for (PluginDescription candidate : sortedPlugins) {
|
||||||
// Verify dependencies
|
// Verify dependencies
|
||||||
for (PluginDependency dependency : plugin.getDependencies()) {
|
for (PluginDependency dependency : candidate.getDependencies()) {
|
||||||
if (!dependency.isOptional() && !isLoaded(dependency.getId())) {
|
if (!dependency.isOptional() && !loadedPluginsById.contains(dependency.getId())) {
|
||||||
logger.error("Can't load plugin {} due to missing dependency {}", plugin.getId(),
|
logger.error("Can't load plugin {} due to missing dependency {}", candidate.getId(),
|
||||||
dependency.getId());
|
dependency.getId());
|
||||||
continue pluginLoad;
|
continue pluginLoad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually create the plugin
|
try {
|
||||||
PluginContainer pluginObject;
|
PluginDescription realPlugin = loader.loadPlugin(candidate);
|
||||||
|
VelocityPluginContainer container = new VelocityPluginContainer(realPlugin);
|
||||||
|
pluginContainers.put(container, loader.createModule(container));
|
||||||
|
loadedPluginsById.add(realPlugin.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Can't create module for plugin {}", candidate.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a global Guice module that with common bindings for every plugin
|
||||||
|
AbstractModule commonModule = new AbstractModule() {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(ProxyServer.class).toInstance(server);
|
||||||
|
bind(PluginManager.class).toInstance(server.getPluginManager());
|
||||||
|
bind(EventManager.class).toInstance(server.getEventManager());
|
||||||
|
bind(CommandManager.class).toInstance(server.getCommandManager());
|
||||||
|
for (PluginContainer container : pluginContainers.keySet()) {
|
||||||
|
bind(PluginContainer.class)
|
||||||
|
.annotatedWith(Names.named(container.getDescription().getId()))
|
||||||
|
.toInstance(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (Map.Entry<PluginContainer, Module> plugin : pluginContainers.entrySet()) {
|
||||||
|
PluginContainer container = plugin.getKey();
|
||||||
|
PluginDescription description = container.getDescription();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pluginObject = loader.createPlugin(plugin);
|
loader.createPlugin(container, plugin.getValue(), commonModule);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Can't create plugin {}", plugin.getId(), e);
|
logger.error("Can't create plugin {}", description.getId(), e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Loaded plugin {} {} by {}", plugin.getId(), plugin.getVersion()
|
logger.info("Loaded plugin {} {} by {}", description.getId(), description.getVersion()
|
||||||
.orElse("<UNKNOWN>"), Joiner.on(", ").join(plugin.getAuthors()));
|
.orElse("<UNKNOWN>"), Joiner.on(", ").join(description.getAuthors()));
|
||||||
registerPlugin(pluginObject);
|
registerPlugin(container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package com.velocitypowered.proxy.plugin.loader;
|
package com.velocitypowered.proxy.plugin.loader;
|
||||||
|
|
||||||
|
import com.google.inject.Module;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -9,7 +11,36 @@ import java.nio.file.Path;
|
|||||||
*/
|
*/
|
||||||
public interface PluginLoader {
|
public interface PluginLoader {
|
||||||
|
|
||||||
PluginDescription loadPlugin(Path source) throws Exception;
|
PluginDescription loadPluginDescription(Path source) throws Exception;
|
||||||
|
|
||||||
PluginContainer createPlugin(PluginDescription plugin) throws Exception;
|
PluginDescription loadPlugin(PluginDescription source) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Module} for the provided {@link PluginContainer}
|
||||||
|
* and verifies that the container's {@link PluginDescription} is correct.
|
||||||
|
*
|
||||||
|
* <p>Does not create an instance of the plugin.</p>
|
||||||
|
*
|
||||||
|
* @param container the plugin container
|
||||||
|
* @return the module containing bindings specific to this plugin
|
||||||
|
* @throws IllegalArgumentException If the {@link PluginDescription}
|
||||||
|
* is missing the path
|
||||||
|
*/
|
||||||
|
Module createModule(PluginContainer container) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of the plugin as specified by the
|
||||||
|
* plugin's main class found in the {@link PluginDescription}.
|
||||||
|
*
|
||||||
|
* <p>The provided {@link Module modules} are used to create an
|
||||||
|
* injector which is then used to create the plugin instance.</p>
|
||||||
|
*
|
||||||
|
* <p>The plugin instance is set in the provided {@link PluginContainer}.</p>
|
||||||
|
*
|
||||||
|
* @param container the plugin container
|
||||||
|
* @param modules the modules to be used when creating this plugin's injector
|
||||||
|
* @throws IllegalStateException If the plugin instance could not be
|
||||||
|
* created from the provided modules
|
||||||
|
*/
|
||||||
|
void createPlugin(PluginContainer container, Module... modules) throws Exception;
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,10 @@ import java.util.Optional;
|
|||||||
public class VelocityPluginContainer implements PluginContainer {
|
public class VelocityPluginContainer implements PluginContainer {
|
||||||
|
|
||||||
private final PluginDescription description;
|
private final PluginDescription description;
|
||||||
private final Object instance;
|
private Object instance;
|
||||||
|
|
||||||
public VelocityPluginContainer(PluginDescription description, Object instance) {
|
public VelocityPluginContainer(PluginDescription description) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.instance = instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -23,4 +22,8 @@ public class VelocityPluginContainer implements PluginContainer {
|
|||||||
public Optional<?> getInstance() {
|
public Optional<?> getInstance() {
|
||||||
return Optional.ofNullable(instance);
|
return Optional.ofNullable(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setInstance(Object instance) {
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.velocitypowered.proxy.plugin.loader.java;
|
|||||||
|
|
||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Module;
|
||||||
import com.velocitypowered.api.plugin.InvalidPluginException;
|
import com.velocitypowered.api.plugin.InvalidPluginException;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
@ -13,8 +14,6 @@ import com.velocitypowered.proxy.plugin.PluginClassLoader;
|
|||||||
import com.velocitypowered.proxy.plugin.loader.PluginLoader;
|
import com.velocitypowered.proxy.plugin.loader.PluginLoader;
|
||||||
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
||||||
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
|
||||||
import com.velocitypowered.proxy.plugin.loader.java.JavaVelocityPluginDescription;
|
|
||||||
import com.velocitypowered.proxy.plugin.loader.java.VelocityPluginModule;
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
@ -39,7 +38,7 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PluginDescription loadPlugin(Path source) throws Exception {
|
public PluginDescription loadPluginDescription(Path source) throws Exception {
|
||||||
Optional<SerializedPluginDescription> serialized = getSerializedPluginInfo(source);
|
Optional<SerializedPluginDescription> serialized = getSerializedPluginInfo(source);
|
||||||
|
|
||||||
if (!serialized.isPresent()) {
|
if (!serialized.isPresent()) {
|
||||||
@ -51,17 +50,28 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
throw new InvalidPluginException("Plugin ID '" + pd.getId() + "' is invalid.");
|
throw new InvalidPluginException("Plugin ID '" + pd.getId() + "' is invalid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginClassLoader loader = new PluginClassLoader(
|
return createCandidateDescription(pd, source);
|
||||||
new URL[]{source.toUri().toURL()}
|
|
||||||
);
|
|
||||||
loader.addToClassloaders();
|
|
||||||
|
|
||||||
Class mainClass = loader.loadClass(pd.getMain());
|
|
||||||
return createDescription(pd, source, mainClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PluginContainer createPlugin(PluginDescription description) throws Exception {
|
public PluginDescription loadPlugin(PluginDescription source) throws Exception {
|
||||||
|
if (!(source instanceof JavaVelocityPluginDescriptionCandidate)) {
|
||||||
|
throw new IllegalArgumentException("Description provided isn't of the Java plugin loader");
|
||||||
|
}
|
||||||
|
PluginClassLoader loader = new PluginClassLoader(
|
||||||
|
new URL[]{source.getSource().get().toUri().toURL()}
|
||||||
|
);
|
||||||
|
loader.addToClassloaders();
|
||||||
|
|
||||||
|
JavaVelocityPluginDescriptionCandidate candidate =
|
||||||
|
(JavaVelocityPluginDescriptionCandidate) source;
|
||||||
|
Class mainClass = loader.loadClass(candidate.getMainClass());
|
||||||
|
return createDescription(candidate, mainClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Module createModule(PluginContainer container) throws Exception {
|
||||||
|
PluginDescription description = container.getDescription();
|
||||||
if (!(description instanceof JavaVelocityPluginDescription)) {
|
if (!(description instanceof JavaVelocityPluginDescription)) {
|
||||||
throw new IllegalArgumentException("Description provided isn't of the Java plugin loader");
|
throw new IllegalArgumentException("Description provided isn't of the Java plugin loader");
|
||||||
}
|
}
|
||||||
@ -73,16 +83,29 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
throw new IllegalArgumentException("No path in plugin description");
|
throw new IllegalArgumentException("No path in plugin description");
|
||||||
}
|
}
|
||||||
|
|
||||||
Injector injector = Guice
|
return new VelocityPluginModule(server, javaDescription, container, baseDirectory);
|
||||||
.createInjector(new VelocityPluginModule(server, javaDescription, baseDirectory));
|
}
|
||||||
Object instance = injector.getInstance(javaDescription.getMainClass());
|
|
||||||
|
@Override
|
||||||
|
public void createPlugin(PluginContainer container, Module... modules) {
|
||||||
|
if (!(container instanceof VelocityPluginContainer)) {
|
||||||
|
throw new IllegalArgumentException("Container provided isn't of the Java plugin loader");
|
||||||
|
}
|
||||||
|
PluginDescription description = container.getDescription();
|
||||||
|
if (!(description instanceof JavaVelocityPluginDescription)) {
|
||||||
|
throw new IllegalArgumentException("Description provided isn't of the Java plugin loader");
|
||||||
|
}
|
||||||
|
|
||||||
|
Injector injector = Guice.createInjector(modules);
|
||||||
|
Object instance = injector
|
||||||
|
.getInstance(((JavaVelocityPluginDescription) description).getMainClass());
|
||||||
|
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Got nothing from injector for plugin " + javaDescription.getId());
|
"Got nothing from injector for plugin " + description.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new VelocityPluginContainer(description, instance);
|
((VelocityPluginContainer) container).setInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<SerializedPluginDescription> getSerializedPluginInfo(Path source)
|
private Optional<SerializedPluginDescription> getSerializedPluginInfo(Path source)
|
||||||
@ -114,15 +137,16 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private VelocityPluginDescription createDescription(SerializedPluginDescription description,
|
private VelocityPluginDescription createCandidateDescription(
|
||||||
Path source, Class mainClass) {
|
SerializedPluginDescription description,
|
||||||
|
Path source) {
|
||||||
Set<PluginDependency> dependencies = new HashSet<>();
|
Set<PluginDependency> dependencies = new HashSet<>();
|
||||||
|
|
||||||
for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) {
|
for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) {
|
||||||
dependencies.add(toDependencyMeta(dependency));
|
dependencies.add(toDependencyMeta(dependency));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new JavaVelocityPluginDescription(
|
return new JavaVelocityPluginDescriptionCandidate(
|
||||||
description.getId(),
|
description.getId(),
|
||||||
description.getName(),
|
description.getName(),
|
||||||
description.getVersion(),
|
description.getVersion(),
|
||||||
@ -131,6 +155,22 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
description.getAuthors(),
|
description.getAuthors(),
|
||||||
dependencies,
|
dependencies,
|
||||||
source,
|
source,
|
||||||
|
description.getMain()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VelocityPluginDescription createDescription(
|
||||||
|
JavaVelocityPluginDescriptionCandidate description,
|
||||||
|
Class mainClass) {
|
||||||
|
return new JavaVelocityPluginDescription(
|
||||||
|
description.getId(),
|
||||||
|
description.getName().orElse(null),
|
||||||
|
description.getVersion().orElse(null),
|
||||||
|
description.getDescription().orElse(null),
|
||||||
|
description.getUrl().orElse(null),
|
||||||
|
description.getAuthors(),
|
||||||
|
description.getDependencies(),
|
||||||
|
description.getSource().orElse(null),
|
||||||
mainClass
|
mainClass
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.velocitypowered.proxy.plugin.loader.java;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
||||||
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
class JavaVelocityPluginDescriptionCandidate extends VelocityPluginDescription {
|
||||||
|
|
||||||
|
private final String mainClass;
|
||||||
|
|
||||||
|
JavaVelocityPluginDescriptionCandidate(String id, @Nullable String name, @Nullable String version,
|
||||||
|
@Nullable String description, @Nullable String url,
|
||||||
|
@Nullable List<String> authors, Collection<PluginDependency> dependencies, Path source,
|
||||||
|
String mainClass) {
|
||||||
|
super(id, name, version, description, url, authors, dependencies, source);
|
||||||
|
this.mainClass = checkNotNull(mainClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getMainClass() {
|
||||||
|
return mainClass;
|
||||||
|
}
|
||||||
|
}
|
@ -3,10 +3,8 @@ package com.velocitypowered.proxy.plugin.loader.java;
|
|||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
import com.google.inject.Scopes;
|
import com.google.inject.Scopes;
|
||||||
import com.velocitypowered.api.command.CommandManager;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.event.EventManager;
|
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
import com.velocitypowered.api.plugin.PluginManager;
|
|
||||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -17,12 +15,14 @@ class VelocityPluginModule implements Module {
|
|||||||
|
|
||||||
private final ProxyServer server;
|
private final ProxyServer server;
|
||||||
private final JavaVelocityPluginDescription description;
|
private final JavaVelocityPluginDescription description;
|
||||||
|
private final PluginContainer pluginContainer;
|
||||||
private final Path basePluginPath;
|
private final Path basePluginPath;
|
||||||
|
|
||||||
VelocityPluginModule(ProxyServer server, JavaVelocityPluginDescription description,
|
VelocityPluginModule(ProxyServer server, JavaVelocityPluginDescription description,
|
||||||
Path basePluginPath) {
|
PluginContainer pluginContainer, Path basePluginPath) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
this.pluginContainer = pluginContainer;
|
||||||
this.basePluginPath = basePluginPath;
|
this.basePluginPath = basePluginPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,12 +31,9 @@ class VelocityPluginModule implements Module {
|
|||||||
binder.bind(description.getMainClass()).in(Scopes.SINGLETON);
|
binder.bind(description.getMainClass()).in(Scopes.SINGLETON);
|
||||||
|
|
||||||
binder.bind(Logger.class).toInstance(LoggerFactory.getLogger(description.getId()));
|
binder.bind(Logger.class).toInstance(LoggerFactory.getLogger(description.getId()));
|
||||||
binder.bind(ProxyServer.class).toInstance(server);
|
|
||||||
binder.bind(Path.class).annotatedWith(DataDirectory.class)
|
binder.bind(Path.class).annotatedWith(DataDirectory.class)
|
||||||
.toInstance(basePluginPath.resolve(description.getId()));
|
.toInstance(basePluginPath.resolve(description.getId()));
|
||||||
binder.bind(PluginDescription.class).toInstance(description);
|
binder.bind(PluginDescription.class).toInstance(description);
|
||||||
binder.bind(PluginManager.class).toInstance(server.getPluginManager());
|
binder.bind(PluginContainer.class).toInstance(pluginContainer);
|
||||||
binder.bind(EventManager.class).toInstance(server.getEventManager());
|
|
||||||
binder.bind(CommandManager.class).toInstance(server.getCommandManager());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import com.velocitypowered.api.plugin.PluginDescription;
|
|||||||
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -28,17 +29,20 @@ public class PluginDependencyUtils {
|
|||||||
* @throws IllegalStateException if there is a circular loop in the dependency graph
|
* @throws IllegalStateException if there is a circular loop in the dependency graph
|
||||||
*/
|
*/
|
||||||
public static List<PluginDescription> sortCandidates(List<PluginDescription> candidates) {
|
public static List<PluginDescription> sortCandidates(List<PluginDescription> candidates) {
|
||||||
|
List<PluginDescription> sortedCandidates = new ArrayList<>(candidates);
|
||||||
|
sortedCandidates.sort(Comparator.comparing(PluginDescription::getId));
|
||||||
|
|
||||||
// Create a graph and populate it with plugin dependencies. Specifically, each graph has plugin
|
// Create a graph and populate it with plugin dependencies. Specifically, each graph has plugin
|
||||||
// nodes, and edges that represent the dependencies that plugin relies on. Non-existent plugins
|
// nodes, and edges that represent the dependencies that plugin relies on. Non-existent plugins
|
||||||
// are ignored.
|
// are ignored.
|
||||||
MutableGraph<PluginDescription> graph = GraphBuilder.directed()
|
MutableGraph<PluginDescription> graph = GraphBuilder.directed()
|
||||||
.allowsSelfLoops(false)
|
.allowsSelfLoops(false)
|
||||||
.expectedNodeCount(candidates.size())
|
.expectedNodeCount(sortedCandidates.size())
|
||||||
.build();
|
.build();
|
||||||
Map<String, PluginDescription> candidateMap = Maps.uniqueIndex(candidates,
|
Map<String, PluginDescription> candidateMap = Maps.uniqueIndex(sortedCandidates,
|
||||||
PluginDescription::getId);
|
PluginDescription::getId);
|
||||||
|
|
||||||
for (PluginDescription description : candidates) {
|
for (PluginDescription description : sortedCandidates) {
|
||||||
graph.addNode(description);
|
graph.addNode(description);
|
||||||
|
|
||||||
for (PluginDependency dependency : description.getDependencies()) {
|
for (PluginDependency dependency : description.getDependencies()) {
|
||||||
|
@ -2,17 +2,26 @@ package com.velocitypowered.proxy.protocol.netty;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
|
import com.velocitypowered.proxy.network.Connections;
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
|
import com.velocitypowered.proxy.util.except.QuietException;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
import io.netty.handler.codec.CorruptedFrameException;
|
||||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = Boolean.getBoolean("velocity.packet-decode-logging");
|
||||||
|
private static final QuietException DECODE_FAILED =
|
||||||
|
new QuietException("A packet did not decode successfully (invalid data). If you are a "
|
||||||
|
+ "developer, launch Velocity with -Dvelocity.packet-decode-logging=true to see more.");
|
||||||
|
|
||||||
private final ProtocolUtils.Direction direction;
|
private final ProtocolUtils.Direction direction;
|
||||||
private StateRegistry state;
|
private StateRegistry state;
|
||||||
private StateRegistry.PacketRegistry.ProtocolRegistry registry;
|
private StateRegistry.PacketRegistry.ProtocolRegistry registry;
|
||||||
@ -46,17 +55,34 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
|||||||
try {
|
try {
|
||||||
packet.decode(msg, direction, registry.version);
|
packet.decode(msg, direction, registry.version);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new CorruptedFrameException(
|
throw handleDecodeFailure(e, packet, packetId);
|
||||||
"Error decoding " + packet.getClass() + " " + getExtraConnectionDetail(packetId), e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.isReadable()) {
|
if (msg.isReadable()) {
|
||||||
throw new CorruptedFrameException("Did not read full packet for " + packet.getClass() + " "
|
throw handleNotReadEnough(packet, packetId);
|
||||||
+ getExtraConnectionDetail(packetId));
|
|
||||||
}
|
}
|
||||||
out.add(packet);
|
out.add(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Exception handleNotReadEnough(MinecraftPacket packet, int packetId) {
|
||||||
|
if (DEBUG) {
|
||||||
|
return new CorruptedFrameException("Did not read full packet for " + packet.getClass() + " "
|
||||||
|
+ getExtraConnectionDetail(packetId));
|
||||||
|
} else {
|
||||||
|
return DECODE_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Exception handleDecodeFailure(Exception cause, MinecraftPacket packet, int packetId) {
|
||||||
|
if (DEBUG) {
|
||||||
|
return new CorruptedFrameException(
|
||||||
|
"Error decoding " + packet.getClass() + " " + getExtraConnectionDetail(packetId), cause);
|
||||||
|
} else {
|
||||||
|
return DECODE_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String getExtraConnectionDetail(int packetId) {
|
private String getExtraConnectionDetail(int packetId) {
|
||||||
return "Direction " + direction + " Protocol " + registry.version + " State " + state
|
return "Direction " + direction + " Protocol " + registry.version + " State " + state
|
||||||
+ " ID " + Integer.toHexString(packetId);
|
+ " ID " + Integer.toHexString(packetId);
|
||||||
|
@ -14,49 +14,64 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
class PluginDependencyUtilsTest {
|
class PluginDependencyUtilsTest {
|
||||||
|
|
||||||
private static final PluginDescription NO_DEPENDENCY_1_EXAMPLE = testDescription("example");
|
private static final PluginDescription NO_DEPENDENCY = testDescription("trivial");
|
||||||
private static final PluginDescription NEVER_DEPENDED = testDescription("and-again");
|
private static final PluginDescription NO_DEPENDENCY_2 = testDescription("trivial2");
|
||||||
private static final PluginDescription SOFT_DEPENDENCY_EXISTS = testDescription("soft",
|
private static final PluginDescription HAS_DEPENDENCY_1 = testDescription("dependent1",
|
||||||
ImmutableList.of(new PluginDependency("example", "", true)));
|
new PluginDependency("trivial", null, false));
|
||||||
private static final PluginDescription SOFT_DEPENDENCY_DOES_NOT_EXIST = testDescription("fluffy",
|
private static final PluginDescription HAS_DEPENDENCY_2 = testDescription("dependent2",
|
||||||
ImmutableList.of(new PluginDependency("i-dont-exist", "", false)));
|
new PluginDependency("dependent1", null, false));
|
||||||
private static final PluginDescription MULTI_DEPENDENCY = testDescription("multi-depend",
|
private static final PluginDescription HAS_DEPENDENCY_3 = testDescription("dependent3",
|
||||||
ImmutableList.of(
|
new PluginDependency("trivial", null, false));
|
||||||
new PluginDependency("example", "", false)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
private static final PluginDescription TEST_WITH_DUPLICATE_DEPEND = testDescription("dup-depend",
|
|
||||||
ImmutableList.of(
|
|
||||||
new PluginDependency("multi-depend", "", false)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
private static final PluginDescription CIRCULAR_DEPENDENCY_1 = testDescription("circle",
|
private static final PluginDescription CIRCULAR_DEPENDENCY_1 = testDescription("circle",
|
||||||
ImmutableList.of(new PluginDependency("oval", "", false)));
|
new PluginDependency("oval", "", false));
|
||||||
private static final PluginDescription CIRCULAR_DEPENDENCY_2 = testDescription("oval",
|
private static final PluginDescription CIRCULAR_DEPENDENCY_2 = testDescription("oval",
|
||||||
ImmutableList.of(new PluginDependency("circle", "", false)));
|
new PluginDependency("circle", "", false));
|
||||||
|
|
||||||
private static final ImmutableList<PluginDescription> EXPECTED = ImmutableList.of(
|
|
||||||
NEVER_DEPENDED,
|
|
||||||
NO_DEPENDENCY_1_EXAMPLE,
|
|
||||||
MULTI_DEPENDENCY,
|
|
||||||
TEST_WITH_DUPLICATE_DEPEND,
|
|
||||||
SOFT_DEPENDENCY_DOES_NOT_EXIST,
|
|
||||||
SOFT_DEPENDENCY_EXISTS
|
|
||||||
);
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void sortCandidates() throws Exception {
|
void sortCandidatesTrivial() throws Exception {
|
||||||
List<PluginDescription> descriptionList = new ArrayList<>();
|
List<PluginDescription> descriptionList = new ArrayList<>();
|
||||||
descriptionList.add(NO_DEPENDENCY_1_EXAMPLE);
|
assertEquals(descriptionList, PluginDependencyUtils.sortCandidates(descriptionList));
|
||||||
descriptionList.add(NEVER_DEPENDED);
|
}
|
||||||
descriptionList.add(SOFT_DEPENDENCY_DOES_NOT_EXIST);
|
|
||||||
descriptionList.add(SOFT_DEPENDENCY_EXISTS);
|
|
||||||
descriptionList.add(MULTI_DEPENDENCY);
|
|
||||||
descriptionList.add(TEST_WITH_DUPLICATE_DEPEND);
|
|
||||||
descriptionList.sort(Comparator.comparing(PluginDescription::getId));
|
|
||||||
|
|
||||||
assertEquals(EXPECTED, PluginDependencyUtils.sortCandidates(descriptionList));
|
@Test
|
||||||
|
void sortCandidatesSingleton() throws Exception {
|
||||||
|
List<PluginDescription> plugins = ImmutableList.of(NO_DEPENDENCY);
|
||||||
|
assertEquals(plugins, PluginDependencyUtils.sortCandidates(plugins));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sortCandidatesBasicDependency() throws Exception {
|
||||||
|
List<PluginDescription> plugins = ImmutableList.of(HAS_DEPENDENCY_1, NO_DEPENDENCY);
|
||||||
|
List<PluginDescription> expected = ImmutableList.of(NO_DEPENDENCY, HAS_DEPENDENCY_1);
|
||||||
|
assertEquals(expected, PluginDependencyUtils.sortCandidates(plugins));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sortCandidatesNestedDependency() throws Exception {
|
||||||
|
List<PluginDescription> plugins = ImmutableList.of(HAS_DEPENDENCY_1, HAS_DEPENDENCY_2,
|
||||||
|
NO_DEPENDENCY);
|
||||||
|
List<PluginDescription> expected = ImmutableList.of(NO_DEPENDENCY, HAS_DEPENDENCY_1,
|
||||||
|
HAS_DEPENDENCY_2);
|
||||||
|
assertEquals(expected, PluginDependencyUtils.sortCandidates(plugins));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sortCandidatesTypical() throws Exception {
|
||||||
|
List<PluginDescription> plugins = ImmutableList.of(HAS_DEPENDENCY_2, NO_DEPENDENCY_2,
|
||||||
|
HAS_DEPENDENCY_1, NO_DEPENDENCY);
|
||||||
|
List<PluginDescription> expected = ImmutableList.of(NO_DEPENDENCY, HAS_DEPENDENCY_1,
|
||||||
|
HAS_DEPENDENCY_2, NO_DEPENDENCY_2);
|
||||||
|
assertEquals(expected, PluginDependencyUtils.sortCandidates(plugins));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sortCandidatesMultiplePluginsDependentOnOne() throws Exception {
|
||||||
|
List<PluginDescription> plugins = ImmutableList.of(HAS_DEPENDENCY_3, HAS_DEPENDENCY_1,
|
||||||
|
NO_DEPENDENCY);
|
||||||
|
List<PluginDescription> expected = ImmutableList.of(NO_DEPENDENCY, HAS_DEPENDENCY_1,
|
||||||
|
HAS_DEPENDENCY_3);
|
||||||
|
assertEquals(expected, PluginDependencyUtils.sortCandidates(plugins));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -65,14 +80,10 @@ class PluginDependencyUtilsTest {
|
|||||||
assertThrows(IllegalStateException.class, () -> PluginDependencyUtils.sortCandidates(descs));
|
assertThrows(IllegalStateException.class, () -> PluginDependencyUtils.sortCandidates(descs));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PluginDescription testDescription(String id) {
|
private static PluginDescription testDescription(String id, PluginDependency... dependencies) {
|
||||||
return testDescription(id, ImmutableList.of());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PluginDescription testDescription(String id, List<PluginDependency> dependencies) {
|
|
||||||
return new VelocityPluginDescription(
|
return new VelocityPluginDescription(
|
||||||
id, "tuxed", "0.1", null, null, ImmutableList.of(),
|
id, "tuxed", "0.1", null, null, ImmutableList.of(),
|
||||||
dependencies, null
|
ImmutableList.copyOf(dependencies), null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren