Initial Velocity support

This commit is contained in:
Frank van der Heijden 2021-07-15 17:55:44 +02:00
parent 4ffa7f1b99
commit 4390dc2c56
No known key found for this signature in database
GPG key ID: B808721C2DD5B5B8
36 changed files with 1455 additions and 41 deletions

35
Velocity/build.gradle Normal file
View file

@ -0,0 +1,35 @@
plugins {
id 'net.kyori.blossom' version '1.3.0'
}
group = rootProject.group + '.velocity'
String dependencyDir = group + '.dependencies'
version = rootProject.version
archivesBaseName = rootProject.name + '-Velocity'
repositories {
maven { url 'https://nexus.velocitypowered.com/repository/maven-public/' }
maven { url 'https://libraries.minecraft.net' }
flatDir { dirs '../libs' }
}
dependencies {
implementation name: 'acf-velocity'
implementation 'org.bstats:bstats-velocity:2.2.1'
implementation project(":Common")
compileOnly 'com.velocitypowered:velocity-api:3.0.0'
compileOnly 'com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT'
compileOnly 'com.electronwill.night-config:toml:3.6.3'
annotationProcessor 'com.velocitypowered:velocity-api:3.0.0'
}
shadowJar {
relocate 'org.bstats', dependencyDir + '.bstats'
relocate 'co.aikar.commands', dependencyDir + '.acf'
relocate 'co.aikar.locales', dependencyDir + '.locales'
}
blossom {
replaceTokenIn('src/main/java/net/frankheijden/serverutils/velocity/ServerUtils.java')
replaceToken '${version}', version
}

View file

@ -0,0 +1,115 @@
package net.frankheijden.serverutils.velocity;
import co.aikar.commands.CommandCompletions;
import co.aikar.commands.VelocityCommandCompletionContext;
import co.aikar.commands.VelocityCommandManager;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import java.nio.file.Path;
import net.frankheijden.serverutils.common.ServerUtilsApp;
import net.frankheijden.serverutils.common.config.Config;
import net.frankheijden.serverutils.common.config.Messenger;
import net.frankheijden.serverutils.velocity.commands.CommandPlugins;
import net.frankheijden.serverutils.velocity.commands.CommandServerUtils;
import net.frankheijden.serverutils.velocity.entities.VelocityPlugin;
import net.frankheijden.serverutils.velocity.managers.VelocityPluginManager;
import org.bstats.velocity.Metrics;
import org.slf4j.Logger;
@Plugin(
id = "serverutils",
name = "ServerUtils",
version = "${version}",
description = "A server utility",
url = "https://github.com/FrankHeijden/ServerUtils",
authors = "FrankHeijden"
)
public class ServerUtils {
private static ServerUtils instance;
private static final String CONFIG_RESOURCE = "velocity-config.toml";
private static final String MESSAGES_RESOURCE = "velocity-messages.toml";
private VelocityPlugin plugin;
private VelocityCommandManager commandManager;
@Inject
private ProxyServer proxy;
@Inject
private Logger logger;
@Inject
@DataDirectory
private Path dataDirectory;
@Inject
private Metrics.Factory metricsFactory;
@Inject
@Named("serverutils")
private PluginContainer pluginContainer;
/**
* Initialises and enables ServerUtils.
*/
@Subscribe
public void onEnable(ProxyInitializeEvent event) {
instance = this;
this.plugin = new VelocityPlugin(this);
ServerUtilsApp.init(this, plugin);
metricsFactory.make(this, ServerUtilsApp.BSTATS_METRICS_ID);
this.commandManager = new VelocityCommandManager(proxy, this);
commandManager.registerCommand(new CommandPlugins());
commandManager.registerCommand(new CommandServerUtils(this));
VelocityPluginManager manager = plugin.getPluginManager();
CommandCompletions<VelocityCommandCompletionContext> completions = commandManager.getCommandCompletions();
completions.registerAsyncCompletion("plugins", context -> manager.getPluginNames());
completions.registerAsyncCompletion("pluginJars", context -> manager.getPluginFileNames());
completions.registerAsyncCompletion("commands", context -> manager.getCommands());
reload();
plugin.enable();
ServerUtilsApp.tryCheckForUpdates();
}
public static ServerUtils getInstance() {
return instance;
}
public ProxyServer getProxy() {
return proxy;
}
public Logger getLogger() {
return logger;
}
public Path getDataDirectory() {
return dataDirectory;
}
public VelocityCommandManager getCommandManager() {
return commandManager;
}
public VelocityPlugin getPlugin() {
return plugin;
}
public void reload() {
new Config("config.toml", CONFIG_RESOURCE);
new Messenger("messages.toml", MESSAGES_RESOURCE);
}
}

View file

@ -0,0 +1,46 @@
package net.frankheijden.serverutils.velocity.commands;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import co.aikar.commands.annotation.Description;
import com.velocitypowered.api.command.CommandSource;
import net.frankheijden.serverutils.common.commands.Plugins;
import net.frankheijden.serverutils.common.config.Messenger;
import net.frankheijden.serverutils.velocity.managers.VelocityPluginManager;
import net.frankheijden.serverutils.velocity.utils.VelocityUtils;
@CommandAlias("vpl|vplugins|velocitypl")
public class CommandPlugins extends BaseCommand {
private static final VelocityPluginManager manager = VelocityPluginManager.get();
/**
* Sends the plugin list to the sender.
* The `-v` flag will output the plugins with version.
* The `-m` flag will also output modules in the plugin list.
* @param sender The sender of the command.
*/
@Default
@CommandCompletion("-v")
@CommandPermission("serverutils.plugins")
@Description("Shows the plugins of this proxy.")
public void onPlugins(CommandSource sender, String... args) {
boolean version = contains(args, "-v");
Plugins.sendPlugins(VelocityUtils.wrap(sender), manager.getPluginsSorted(), pl -> {
String ver = version ? Messenger.getMessage("serverutils.plugins.version",
"%version%", pl.getDescription().getVersion().orElse("<UNKNOWN>")) : "";
return Messenger.getMessage("serverutils.plugins.format",
"%plugin%", pl.getDescription().getId()) + ver;
});
}
private static boolean contains(String[] arr, String val) {
for (String s : arr) {
if (s.equalsIgnoreCase(val)) return true;
}
return false;
}
}

View file

@ -0,0 +1,268 @@
package net.frankheijden.serverutils.velocity.commands;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.tree.CommandNode;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import net.frankheijden.serverutils.common.config.Messenger;
import net.frankheijden.serverutils.common.entities.AbstractResult;
import net.frankheijden.serverutils.common.entities.CloseableResult;
import net.frankheijden.serverutils.common.entities.Result;
import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.frankheijden.serverutils.common.utils.FormatBuilder;
import net.frankheijden.serverutils.common.utils.HexUtils;
import net.frankheijden.serverutils.common.utils.ListBuilder;
import net.frankheijden.serverutils.common.utils.ListFormat;
import net.frankheijden.serverutils.velocity.ServerUtils;
import net.frankheijden.serverutils.velocity.entities.VelocityLoadResult;
import net.frankheijden.serverutils.velocity.reflection.RVelocityCommandManager;
import net.frankheijden.serverutils.velocity.utils.VelocityUtils;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@CommandAlias("vsu|vserverutils")
public class CommandServerUtils extends BaseCommand {
private static final Set<String> ALIASES;
static {
ALIASES = new HashSet<>();
ALIASES.add("vserverutils");
ALIASES.add("vplugins");
ALIASES.add("velocitypl");
}
private final ServerUtils plugin;
public CommandServerUtils(ServerUtils plugin) {
this.plugin = plugin;
}
/**
* Shows the help page to the sender.
* @param source The sender of the command.
*/
@Default
@Subcommand("help")
@CommandPermission("serverutils.help")
@Description("Shows a help page with a few commands.")
public void onHelp(CommandSource source) {
ServerCommandSender sender = VelocityUtils.wrap(source);
Messenger.sendMessage(sender, "serverutils.help.header");
FormatBuilder builder = FormatBuilder.create(Messenger.getMessage("serverutils.help.format"))
.orderedKeys("%command%", "%subcommand%", "%help%");
plugin.getCommandManager().getRegisteredRootCommands().stream()
.filter(c -> !ALIASES.contains(c.getCommandName().toLowerCase()))
.forEach(rootCommand -> {
builder.add(rootCommand.getCommandName(), "", rootCommand.getDescription());
rootCommand.getSubCommands().forEach((str, cmd) -> {
if (cmd.getPrefSubCommand().isEmpty()) return;
builder.add(rootCommand.getCommandName(), " " + cmd.getPrefSubCommand(), cmd.getHelpText());
});
});
builder.sendTo(sender);
Messenger.sendMessage(sender, "serverutils.help.footer");
}
/**
* Reloads the configurations of ServerUtils.
* @param sender The sender of the command.
*/
@Subcommand("reload")
@CommandPermission("serverutils.reload")
@Description("Reloads the ServerUtils plugin.")
public void onReload(CommandSource sender) {
plugin.reload();
Messenger.sendMessage(VelocityUtils.wrap(sender), "serverutils.success",
"%action%", "reload",
"%what%", "ServerUtils Bungee configurations");
}
/**
* Loads the specified plugin on the proxy.
* @param source The sender of the command.
* @param jarFile The filename of the plugin in the plugins/ directory.
*/
@Subcommand("loadplugin|lp")
@CommandCompletion("@pluginJars")
@CommandPermission("serverutils.loadplugin")
@Description("Loads the specified jar file as a plugin.")
public void onLoadPlugin(CommandSource source, String jarFile) {
ServerCommandSender sender = VelocityUtils.wrap(source);
VelocityLoadResult loadResult = plugin.getPlugin().getPluginManager().loadPlugin(jarFile);
if (!loadResult.isSuccess()) {
loadResult.getResult().sendTo(sender, "load", jarFile);
return;
}
PluginContainer container = loadResult.get();
Result result = plugin.getPlugin().getPluginManager().enablePlugin(container);
result.sendTo(sender, "load", container.getDescription().getId());
}
/**
* Unloads the specified plugin from the proxy.
* @param source The sender of the command.
* @param pluginName The plugin name.
*/
@Subcommand("unloadplugin|up")
@CommandCompletion("@plugins")
@CommandPermission("serverutils.unloadplugin")
@Description("Disables and unloads the specified plugin.")
public void onUnloadPlugin(CommandSource source, String pluginName) {
CloseableResult result = plugin.getPlugin().getPluginManager().unloadPlugin(pluginName);
result.getResult().sendTo(VelocityUtils.wrap(source), "unload", pluginName);
result.tryClose();
}
/**
* Reloads the specified plugin on the proxy.
* @param sender The sender of the command.
* @param pluginName The plugin name.
*/
@Subcommand("reloadplugin|rp")
@CommandCompletion("@plugins")
@CommandPermission("serverutils.reloadplugin")
@Description("Reloads a specified plugin.")
public void onReloadPlugin(CommandSource sender, String pluginName) {
// Wacky method to have the resources needed for the reload in memory, in case of a self reload.
HexUtils utils = new HexUtils();
Map<String, Object> section = Messenger.getInstance().getConfig().getMap("serverutils");
String result = plugin.getPlugin().getPluginManager().reloadPlugin(pluginName).toString();
String msg = (String) section.get(result.toLowerCase());
if (msg != null && !msg.isEmpty()) {
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(utils.convertHexString(
msg.replace("%action%", "reload").replace("%what%", pluginName))));
}
}
/**
* Watches the given plugin and reloads it when a change is detected to the file.
* @param source The sender of the command.
* @param pluginName The plugin name.
*/
@Subcommand("watchplugin|wp")
@CommandCompletion("@plugins")
@CommandPermission("serverutils.watchplugin")
@Description("Watches the specified plugin for changes.")
public void onWatchPlugin(CommandSource source, String pluginName) {
ServerCommandSender sender = VelocityUtils.wrap(source);
AbstractResult result = plugin.getPlugin().getPluginManager().watchPlugin(sender, pluginName);
result.sendTo(sender, "watch", pluginName);
}
/**
* Stops watching the given plugin.
* @param source The sender of the command.
* @param pluginName The plugin name.
*/
@Subcommand("unwatchplugin|uwp")
@CommandCompletion("@plugins")
@CommandPermission("serverutils.watchplugin")
@Description("Stops watching the specified plugin for changes.")
public void onUnwatchPlugin(CommandSource source, String pluginName) {
AbstractResult result = plugin.getPlugin().getPluginManager().unwatchPlugin(pluginName);
result.sendTo(VelocityUtils.wrap(source), "unwatch", pluginName);
}
/**
* Shows information about the specified plugin.
* @param source The sender of the command.
* @param pluginName The plugin name.
*/
@Subcommand("plugininfo|pi")
@CommandCompletion("@plugins")
@CommandPermission("serverutils.plugininfo")
@Description("Shows information about the specified plugin.")
public void onPluginInfo(CommandSource source, String pluginName) {
ServerCommandSender sender = VelocityUtils.wrap(source);
Optional<PluginContainer> container = plugin.getProxy().getPluginManager().getPlugin(pluginName);
if (!container.isPresent()) {
Result.NOT_EXISTS.sendTo(sender, "fetch", pluginName);
return;
}
PluginDescription desc = container.get().getDescription();
String format = Messenger.getMessage("serverutils.plugininfo.format");
String listFormatString = Messenger.getMessage("serverutils.plugininfo.list_format");
String seperator = Messenger.getMessage("serverutils.plugininfo.seperator");
String lastSeperator = Messenger.getMessage("serverutils.plugininfo.last_seperator");
ListFormat<String> listFormat = str -> listFormatString.replace("%value%", str);
Messenger.sendMessage(sender, "serverutils.plugininfo.header");
FormatBuilder builder = FormatBuilder.create(format)
.orderedKeys("%key%", "%value%")
.add("Id", desc.getId())
.add("Name", desc.getName().orElse(null))
.add("Version", desc.getVersion().orElse("<UNKNOWN>"))
.add("Author" + (desc.getAuthors().size() == 1 ? "" : "s"), ListBuilder.create(desc.getAuthors())
.format(listFormat)
.seperator(seperator)
.lastSeperator(lastSeperator)
.toString())
.add("Description", desc.getDescription().orElse(null))
.add("URL", desc.getUrl().orElse(null))
.add("Source", desc.getSource().map(Path::toString).orElse(null))
.add("Dependencies", ListBuilder.create(desc.getDependencies())
.format(d -> listFormat.format(d.getId()))
.seperator(seperator)
.lastSeperator(lastSeperator)
.toString());
builder.sendTo(sender);
Messenger.sendMessage(sender, "serverutils.plugininfo.footer");
}
/**
* Shows information about a provided command.
* @param source The sender of the command.
* @param command The command to lookup.
*/
@Subcommand("commandinfo|ci")
@CommandCompletion("@commands")
@CommandPermission("serverutils.commandinfo")
@Description("Shows information about the specified command.")
public void onCommandInfo(CommandSource source, String command) {
ServerCommandSender sender = VelocityUtils.wrap(source);
CommandDispatcher<CommandSource> dispatcher = RVelocityCommandManager.getDispatcher(
plugin.getProxy().getCommandManager()
);
CommandNode<CommandSource> node = dispatcher.getRoot().getChild(command);
if (node == null) {
Messenger.sendMessage(sender, "serverutils.commandinfo.not_exists");
return;
}
String format = Messenger.getMessage("serverutils.commandinfo.format");
Messenger.sendMessage(sender, "serverutils.commandinfo.header");
FormatBuilder builder = FormatBuilder.create(format)
.orderedKeys("%key%", "%value%")
.add("Name", node.getName())
.add("Plugin", "<UNKNOWN>"); // TODO: fix plugin command
builder.sendTo(sender);
Messenger.sendMessage(sender, "serverutils.commandinfo.footer");
}
}

View file

@ -0,0 +1,36 @@
package net.frankheijden.serverutils.velocity.entities;
import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.frankheijden.serverutils.common.providers.ChatProvider;
import net.frankheijden.serverutils.common.utils.HexUtils;
import net.frankheijden.serverutils.velocity.ServerUtils;
import net.frankheijden.serverutils.velocity.utils.VelocityUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
public class VelocityChatProvider extends ChatProvider {
private final ServerUtils plugin;
public VelocityChatProvider(ServerUtils plugin) {
this.plugin = plugin;
}
@Override
public ServerCommandSender getConsoleSender() {
return VelocityUtils.wrap(plugin.getProxy().getConsoleCommandSource());
}
@Override
public String color(String str) {
return HexUtils.convertHexString(str);
}
@Override
public void broadcast(String permission, String message) {
Component msg = LegacyComponentSerializer.legacyAmpersand().deserialize(message);
plugin.getProxy().getAllPlayers().stream()
.filter(p -> p.hasPermission(permission))
.forEach(p -> p.sendMessage(msg));
}
}

View file

@ -0,0 +1,34 @@
package net.frankheijden.serverutils.velocity.entities;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
public class VelocityCommandSender implements ServerCommandSender {
private final CommandSource source;
public VelocityCommandSender(CommandSource source) {
this.source = source;
}
@Override
public void sendMessage(String message) {
source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message));
}
@Override
public boolean hasPermission(String permission) {
return source.hasPermission(permission);
}
/**
* Whether or not the given instance is a player.
* @return Boolean true or false.
*/
@Override
public boolean isPlayer() {
return source instanceof Player;
}
}

View file

@ -0,0 +1,20 @@
package net.frankheijden.serverutils.velocity.entities;
import com.velocitypowered.api.plugin.PluginContainer;
import net.frankheijden.serverutils.common.entities.LoadResult;
import net.frankheijden.serverutils.common.entities.Result;
public class VelocityLoadResult extends LoadResult<PluginContainer> {
public VelocityLoadResult(PluginContainer obj, Result result) {
super(obj, result);
}
public VelocityLoadResult(PluginContainer obj) {
super(obj);
}
public VelocityLoadResult(Result result) {
super(result);
}
}

View file

@ -0,0 +1,72 @@
package net.frankheijden.serverutils.velocity.entities;
import com.velocitypowered.api.plugin.PluginDescription;
import java.io.File;
import java.nio.file.Path;
import java.util.logging.Logger;
import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin;
import net.frankheijden.serverutils.velocity.ServerUtils;
import net.frankheijden.serverutils.velocity.managers.VelocityPluginManager;
import net.frankheijden.serverutils.velocity.managers.VelocityTaskManager;
import net.frankheijden.serverutils.velocity.reflection.RJavaPluginLoader;
public class VelocityPlugin extends ServerUtilsPlugin {
private final ServerUtils plugin;
private final VelocityPluginManager pluginManager;
private final VelocityTaskManager taskManager;
private final VelocityResourceProvider resourceProvider;
private final VelocityChatProvider chatProvider;
/**
* Creates a new BungeePlugin instance of ServerUtils.
* @param plugin The ServerUtils plugin.
*/
public VelocityPlugin(ServerUtils plugin) {
this.plugin = plugin;
this.pluginManager = new VelocityPluginManager();
this.taskManager = new VelocityTaskManager(plugin);
this.resourceProvider = new VelocityResourceProvider(plugin);
this.chatProvider = new VelocityChatProvider(plugin);
}
@Override
@SuppressWarnings("unchecked")
public VelocityPluginManager getPluginManager() {
return pluginManager;
}
@Override
@SuppressWarnings("unchecked")
public VelocityTaskManager getTaskManager() {
return taskManager;
}
@Override
public VelocityResourceProvider getResourceProvider() {
return resourceProvider;
}
@Override
public VelocityChatProvider getChatProvider() {
return chatProvider;
}
@Override
public Logger getLogger() {
return Logger.getLogger(plugin.getLogger().getName());
}
@Override
public File getDataFolder() {
return plugin.getDataDirectory().toFile();
}
@Override
@SuppressWarnings("unchecked")
public PluginDescription fetchUpdaterData() {
Path pluginPath = pluginManager.getPluginFile("ServerUtils").toPath();
Object javaPluginLoader = RJavaPluginLoader.newInstance(plugin.getProxy(), pluginPath.getParent());
return RJavaPluginLoader.loadPluginDescription(javaPluginLoader, pluginPath);
}
}

View file

@ -0,0 +1,46 @@
package net.frankheijden.serverutils.velocity.entities;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import net.frankheijden.serverutils.common.config.ServerUtilsConfig;
import net.frankheijden.serverutils.common.providers.ResourceProvider;
import net.frankheijden.serverutils.velocity.ServerUtils;
public class VelocityResourceProvider implements ResourceProvider {
private final ServerUtils plugin;
public VelocityResourceProvider(ServerUtils plugin) {
this.plugin = plugin;
}
@Override
public InputStream getResource(String resource) {
return plugin.getClass().getClassLoader().getResourceAsStream(resource);
}
@Override
public ServerUtilsConfig load(InputStream is) {
try {
Path tmpFile = Files.createTempFile(null, null);
Files.copy(is, tmpFile, StandardCopyOption.REPLACE_EXISTING);
VelocityTomlConfig config = new VelocityTomlConfig(tmpFile.toFile());
Files.delete(tmpFile);
return config;
} catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
@Override
public ServerUtilsConfig load(File file) {
return new VelocityTomlConfig(file);
}
}

View file

@ -0,0 +1,85 @@
package net.frankheijden.serverutils.velocity.entities;
import com.electronwill.nightconfig.core.CommentedConfig;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.toml.TomlFormat;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.frankheijden.serverutils.common.config.ServerUtilsConfig;
public class VelocityTomlConfig implements ServerUtilsConfig {
private final CommentedConfig config;
private final File file;
/**
* Creates a new VelocityTomlConfig instance.
*/
public VelocityTomlConfig(File file) {
CommentedFileConfig config = CommentedFileConfig.of(file, TomlFormat.instance());
config.load();
this.config = config;
this.file = file;
}
public VelocityTomlConfig(CommentedConfig config, File file) {
this.config = config;
this.file = file;
}
@Override
public Object get(String path) {
Object obj = config.get(path);
if (obj instanceof CommentedConfig) {
return new VelocityTomlConfig((CommentedConfig) obj, file);
}
return obj;
}
@Override
public List<String> getStringList(String path) {
return config.getOrElse(path, new ArrayList<>());
}
@Override
public Map<String, Object> getMap(String path) {
CommentedConfig section = config.get(path);
if (section == null) return new HashMap<>();
return section.valueMap();
}
@Override
public void set(String path, Object value) {
config.set(path, value);
}
@Override
public String getString(String path) {
return config.get(path);
}
@Override
public boolean getBoolean(String path) {
return config.get(path);
}
@Override
public Collection<? extends String> getKeys() {
return config.valueMap().keySet();
}
@Override
public void save() throws IOException {
if (config instanceof CommentedFileConfig) {
((CommentedFileConfig) config).save();
} else {
throw new IOException("Config is not an instance of CommentedFileConfig!");
}
}
}

View file

@ -0,0 +1,276 @@
package net.frankheijden.serverutils.velocity.managers;
import com.google.common.base.Joiner;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.name.Names;
import com.mojang.brigadier.tree.CommandNode;
import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.event.EventManager;
import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.permission.PermissionFunction;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.api.plugin.meta.PluginDependency;
import com.velocitypowered.api.proxy.ConsoleCommandSource;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.scheduler.ScheduledTask;
import java.io.Closeable;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import net.frankheijden.serverutils.common.entities.CloseableResult;
import net.frankheijden.serverutils.common.entities.Result;
import net.frankheijden.serverutils.common.managers.AbstractPluginManager;
import net.frankheijden.serverutils.velocity.ServerUtils;
import net.frankheijden.serverutils.velocity.entities.VelocityLoadResult;
import net.frankheijden.serverutils.velocity.reflection.RJavaPluginLoader;
import net.frankheijden.serverutils.velocity.reflection.RVelocityCommandManager;
import net.frankheijden.serverutils.velocity.reflection.RVelocityConsole;
import net.frankheijden.serverutils.velocity.reflection.RVelocityEventManager;
import net.frankheijden.serverutils.velocity.reflection.RVelocityPluginContainer;
import net.frankheijden.serverutils.velocity.reflection.RVelocityPluginManager;
import net.frankheijden.serverutils.velocity.reflection.RVelocityScheduler;
public class VelocityPluginManager extends AbstractPluginManager<PluginContainer> {
private static VelocityPluginManager instance;
private final ProxyServer proxy;
public VelocityPluginManager() {
instance = this;
this.proxy = ServerUtils.getInstance().getProxy();
}
public static VelocityPluginManager get() {
return instance;
}
@Override
public VelocityLoadResult loadPlugin(String pluginFile) {
return loadPlugin(new File(getPluginsFolder(), pluginFile));
}
@Override
public VelocityLoadResult loadPlugin(File file) {
if (!file.exists()) return new VelocityLoadResult(Result.NOT_EXISTS);
Object javaPluginLoader = RJavaPluginLoader.newInstance(proxy, file.toPath().getParent());
PluginDescription candidate = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, file.toPath());
for (PluginDependency dependency : candidate.getDependencies()) {
if (!dependency.isOptional() && !proxy.getPluginManager().isLoaded(dependency.getId())) {
ServerUtils.getInstance().getLogger().error(
"Can't load plugin {} due to missing dependency {}",
candidate.getId(),
dependency.getId()
);
return new VelocityLoadResult(Result.UNKNOWN_DEPENDENCY.arg(dependency.getId()));
}
}
PluginDescription realPlugin = RJavaPluginLoader.loadPlugin(javaPluginLoader, candidate);
PluginContainer container = RVelocityPluginContainer.newInstance(realPlugin);
return new VelocityLoadResult(container);
}
@Override
public Result enablePlugin(PluginContainer container) {
Object javaPluginLoader = RJavaPluginLoader.newInstance(
proxy,
container.getDescription().getSource().map(Path::getParent).orElse(null)
);
PluginDescription realPlugin = container.getDescription();
Module module = RJavaPluginLoader.createModule(javaPluginLoader, container);
AbstractModule commonModule = new AbstractModule() {
@Override
protected void configure() {
bind(ProxyServer.class).toInstance(proxy);
bind(PluginManager.class).toInstance(proxy.getPluginManager());
bind(EventManager.class).toInstance(proxy.getEventManager());
bind(CommandManager.class).toInstance(proxy.getCommandManager());
for (PluginContainer container : proxy.getPluginManager().getPlugins()) {
bind(PluginContainer.class)
.annotatedWith(Names.named(container.getDescription().getId()))
.toInstance(container);
}
bind(PluginContainer.class)
.annotatedWith(Names.named(realPlugin.getId()))
.toInstance(container);
}
};
try {
RJavaPluginLoader.createPlugin(javaPluginLoader, container, module, commonModule);
} catch (Exception ex) {
ServerUtils.getInstance().getLogger().error(
String.format("Can't create plugin %s", container.getDescription().getId()),
ex
);
return Result.ERROR;
}
ServerUtils.getInstance().getLogger().info(
"Loaded plugin {} {} by {}",
realPlugin.getId(),
realPlugin.getVersion().orElse("<UNKNOWN>"),
Joiner.on(", ").join(realPlugin.getAuthors())
);
RVelocityPluginManager.registerPlugin(proxy.getPluginManager(), container);
container.getInstance().ifPresent(instance -> {
RVelocityEventManager.registerInternally(proxy.getEventManager(), container, instance);
RVelocityEventManager.fireForPlugin(
proxy.getEventManager(),
new ProxyInitializeEvent(),
instance
).join();
ConsoleCommandSource console = proxy.getConsoleCommandSource();
PermissionsSetupEvent event = new PermissionsSetupEvent(
console,
s -> PermissionFunction.ALWAYS_TRUE
);
PermissionFunction permissionFunction = RVelocityEventManager.fireForPlugin(
proxy.getEventManager(),
event,
instance
).join().createFunction(console);
if (permissionFunction == null) {
ServerUtils.getInstance().getLogger().error(
"A plugin permission provider {} provided an invalid permission function for the console."
+ " This is a bug in the plugin, not in Velocity."
+ " Falling back to the default permission function.",
event.getProvider().getClass().getName()
);
permissionFunction = PermissionFunction.ALWAYS_TRUE;
}
RVelocityConsole.setPermissionFunction(console, permissionFunction);
});
return Result.SUCCESS;
}
@Override
public Result disablePlugin(PluginContainer plugin) {
Object pluginInstance = plugin.getInstance().orElse(null);
if (pluginInstance == null) return Result.NOT_EXISTS;
RVelocityEventManager.fireForPlugin(
proxy.getEventManager(),
pluginInstance,
new ProxyShutdownEvent()
);
return Result.SUCCESS;
}
@Override
public Result reloadPlugin(String pluginName) {
Optional<PluginContainer> pluginOptional = proxy.getPluginManager().getPlugin(pluginName);
if (!pluginOptional.isPresent()) return Result.NOT_EXISTS;
return reloadPlugin(pluginOptional.get());
}
@Override
public Result reloadPlugin(PluginContainer plugin) {
CloseableResult result = unloadPlugin(plugin);
if (result.getResult() != Result.SUCCESS) return result.getResult();
result.tryClose();
File file = getPluginFile(plugin.getDescription().getId());
if (file == null) return Result.FILE_DELETED;
VelocityLoadResult loadResult = loadPlugin(file);
if (!loadResult.isSuccess()) return loadResult.getResult();
return enablePlugin(loadResult.get());
}
@Override
public CloseableResult unloadPlugin(String pluginName) {
Optional<PluginContainer> pluginOptional = proxy.getPluginManager().getPlugin(pluginName);
if (!pluginOptional.isPresent()) return new CloseableResult(Result.NOT_EXISTS);
return unloadPlugin(pluginOptional.get());
}
@Override
public CloseableResult unloadPlugin(PluginContainer plugin) {
Optional<?> pluginInstanceOptional = plugin.getInstance();
if (!pluginInstanceOptional.isPresent()) return new CloseableResult(Result.INVALID_PLUGIN);
Object pluginInstance = pluginInstanceOptional.get();
proxy.getEventManager().unregisterListeners(pluginInstance);
for (ScheduledTask task : RVelocityScheduler.getTasksByPlugin(proxy.getScheduler()).removeAll(pluginInstance)) {
task.cancel();
}
// TODO: unload commands of plugin
RVelocityPluginManager.getPlugins(proxy.getPluginManager()).remove(plugin.getDescription().getId());
RVelocityPluginManager.getPluginInstances(proxy.getPluginManager()).remove(pluginInstance);
List<Closeable> closeables = new ArrayList<>();
ClassLoader loader = pluginInstance.getClass().getClassLoader();
if (loader instanceof Closeable) {
closeables.add((Closeable) loader);
}
return new CloseableResult(closeables);
}
@Override
public List<PluginContainer> getPlugins() {
return new ArrayList<>(proxy.getPluginManager().getPlugins());
}
@Override
public String getPluginName(PluginContainer plugin) {
return plugin.getDescription().getId();
}
@Override
public File getPluginFile(PluginContainer plugin) {
return plugin.getDescription().getSource()
.map(Path::toFile)
.orElse(null);
}
@Override
public File getPluginFile(String pluginName) {
Object javaPluginLoader = RJavaPluginLoader.newInstance(instance.proxy, getPluginsFolder().toPath());
for (File file : getPluginJars()) {
PluginDescription desc = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, file.toPath());
if (desc.getId().equals(pluginName)) {
return file;
}
}
return null;
}
@Override
public PluginContainer getPlugin(String pluginName) {
return proxy.getPluginManager().getPlugin(pluginName).orElse(null);
}
@Override
public Set<String> getCommands() {
return RVelocityCommandManager.getDispatcher(proxy.getCommandManager()).getRoot().getChildren().stream()
.map(CommandNode::getName)
.collect(Collectors.toSet());
}
}

View file

@ -0,0 +1,41 @@
package net.frankheijden.serverutils.velocity.managers;
import com.velocitypowered.api.scheduler.ScheduledTask;
import java.time.Duration;
import net.frankheijden.serverutils.common.managers.AbstractTaskManager;
import net.frankheijden.serverutils.velocity.ServerUtils;
public class VelocityTaskManager extends AbstractTaskManager<ScheduledTask> {
private final ServerUtils plugin;
public VelocityTaskManager(ServerUtils plugin) {
super(ScheduledTask::cancel);
this.plugin = plugin;
}
@Override
protected ScheduledTask runTaskImpl(Runnable runnable) {
return runTaskAsynchronously(runnable);
}
@Override
public ScheduledTask runTaskLater(Runnable runnable, long delay) {
return plugin.getProxy().getScheduler()
.buildTask(plugin, runnable)
.delay(Duration.ofMillis(delay * 50))
.schedule();
}
@Override
protected ScheduledTask runTaskAsynchronouslyImpl(Runnable runnable) {
return plugin.getProxy().getScheduler()
.buildTask(plugin, runnable)
.schedule();
}
@Override
public void cancelTask(ScheduledTask task) {
task.cancel();
}
}

View file

@ -0,0 +1,59 @@
package net.frankheijden.serverutils.velocity.reflection;
import com.google.inject.Module;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.proxy.ProxyServer;
import dev.frankheijden.minecraftreflection.ClassObject;
import dev.frankheijden.minecraftreflection.MinecraftReflection;
import java.lang.reflect.Array;
import java.nio.file.Path;
public class RJavaPluginLoader {
private static final MinecraftReflection reflection = MinecraftReflection
.of("com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader");
private RJavaPluginLoader() {}
/**
* Constructs a new instance of a JavaPluginLoader.
*/
public static Object newInstance(ProxyServer proxy, Path baseDirectory) {
return reflection.newInstance(
ClassObject.of(ProxyServer.class, proxy),
ClassObject.of(Path.class, baseDirectory)
);
}
public static PluginDescription loadPluginDescription(Object javaPluginLoader, Path source) {
return reflection.invoke(javaPluginLoader, "loadPluginDescription", ClassObject.of(Path.class, source));
}
/**
* Loads the plugin from their candidate PluginDescription.
*/
public static PluginDescription loadPlugin(Object javaPluginLoader, PluginDescription candidate) {
return reflection.invoke(
javaPluginLoader,
"loadPlugin",
ClassObject.of(PluginDescription.class, candidate)
);
}
public static Module createModule(Object javaPluginLoader, PluginContainer container) {
return reflection.invoke(javaPluginLoader, "createModule", ClassObject.of(PluginContainer.class, container));
}
/**
* Creates the plugin.
*/
public static void createPlugin(Object javaPluginLoader, PluginContainer container, Module... modules) {
reflection.invoke(
javaPluginLoader,
"createPlugin",
ClassObject.of(PluginContainer.class, container),
ClassObject.of(Array.newInstance(Module.class, 0).getClass(), modules)
);
}
}

View file

@ -0,0 +1,18 @@
package net.frankheijden.serverutils.velocity.reflection;
import com.mojang.brigadier.CommandDispatcher;
import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.command.CommandSource;
import dev.frankheijden.minecraftreflection.MinecraftReflection;
public class RVelocityCommandManager {
private static final MinecraftReflection reflection = MinecraftReflection
.of("com.velocitypowered.proxy.command.VelocityCommandManager");
private RVelocityCommandManager() {}
public static CommandDispatcher<CommandSource> getDispatcher(CommandManager manager) {
return reflection.get(manager, "dispatcher");
}
}

View file

@ -0,0 +1,17 @@
package net.frankheijden.serverutils.velocity.reflection;
import com.velocitypowered.api.permission.PermissionFunction;
import com.velocitypowered.api.proxy.ConsoleCommandSource;
import dev.frankheijden.minecraftreflection.MinecraftReflection;
public class RVelocityConsole {
private static final MinecraftReflection reflection = MinecraftReflection
.of("com.velocitypowered.proxy.console.VelocityConsole");
private RVelocityConsole() {}
public static void setPermissionFunction(ConsoleCommandSource velocityConsole, PermissionFunction function) {
reflection.set(velocityConsole, "permissionFunction", function);
}
}

View file

@ -0,0 +1,90 @@
package net.frankheijden.serverutils.velocity.reflection;
import com.google.common.collect.Multimap;
import com.velocitypowered.api.event.EventHandler;
import com.velocitypowered.api.event.EventManager;
import com.velocitypowered.api.plugin.PluginContainer;
import dev.frankheijden.minecraftreflection.ClassObject;
import dev.frankheijden.minecraftreflection.MinecraftReflection;
import java.lang.reflect.Array;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class RVelocityEventManager {
private static final MinecraftReflection reflection = MinecraftReflection
.of("com.velocitypowered.proxy.event.VelocityEventManager");
private RVelocityEventManager() {}
@SuppressWarnings("rawtypes")
public static Multimap getHandlersByType(EventManager manager) {
return reflection.get(manager, "handlersByType");
}
/**
* Retrieves the registrations from a plugin for a specific event.
*/
@SuppressWarnings("unchecked")
public static List<Object> getRegistrationsByPlugin(EventManager manager, Object plugin, Class<?> eventClass) {
return (List<Object>) getHandlersByType(manager).get(eventClass).stream()
.filter(r -> RHandlerRegistration.getPlugin(r).getInstance().orElse(null) == plugin)
.collect(Collectors.toList());
}
/**
* Registers the listener for a given plugin.
*/
public static void registerInternally(EventManager manager, PluginContainer container, Object listener) {
reflection.invoke(
manager,
"registerInternally",
ClassObject.of(PluginContainer.class, container),
ClassObject.of(Object.class, listener)
);
}
/**
* Fires an event specifically for one plugin.
*/
public static <E> CompletableFuture<E> fireForPlugin(
EventManager manager,
E event,
Object plugin
) {
List<Object> registrations = getRegistrationsByPlugin(manager, plugin, event.getClass());
CompletableFuture<E> future = new CompletableFuture<>();
Object registrationsEmptyArray = Array.newInstance(RHandlerRegistration.reflection.getClazz(), 0);
Class<?> registrationsArrayClass = registrationsEmptyArray.getClass();
reflection.invoke(
manager,
"fire",
ClassObject.of(CompletableFuture.class, future),
ClassObject.of(Object.class, event),
ClassObject.of(int.class, 0),
ClassObject.of(boolean.class, false),
ClassObject.of(registrationsArrayClass, registrations.toArray((Object[]) registrationsEmptyArray))
);
return future;
}
public static class RHandlerRegistration {
private static final MinecraftReflection reflection = MinecraftReflection
.of("com.velocitypowered.proxy.event.VelocityEventManager$HandlerRegistration");
private RHandlerRegistration() {}
public static PluginContainer getPlugin(Object registration) {
return reflection.get(registration, "plugin");
}
public static EventHandler<Object> getEventHandler(Object registration) {
return reflection.get(registration, "handler");
}
}
}

View file

@ -0,0 +1,18 @@
package net.frankheijden.serverutils.velocity.reflection;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import dev.frankheijden.minecraftreflection.ClassObject;
import dev.frankheijden.minecraftreflection.MinecraftReflection;
public class RVelocityPluginContainer {
private static final MinecraftReflection reflection = MinecraftReflection
.of("com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer");
private RVelocityPluginContainer() {}
public static PluginContainer newInstance(PluginDescription description) {
return reflection.newInstance(ClassObject.of(PluginDescription.class, description));
}
}

View file

@ -0,0 +1,27 @@
package net.frankheijden.serverutils.velocity.reflection;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginManager;
import dev.frankheijden.minecraftreflection.ClassObject;
import dev.frankheijden.minecraftreflection.MinecraftReflection;
import java.util.Map;
public class RVelocityPluginManager {
private static final MinecraftReflection reflection = MinecraftReflection
.of("com.velocitypowered.proxy.plugin.VelocityPluginManager");
private RVelocityPluginManager() {}
public static Map<String, PluginContainer> getPlugins(PluginManager manager) {
return reflection.get(manager, "plugins");
}
public static Map<Object, PluginContainer> getPluginInstances(PluginManager manager) {
return reflection.get(manager, "pluginInstances");
}
public static void registerPlugin(PluginManager manager, PluginContainer container) {
reflection.invoke(manager, "registerPlugin", ClassObject.of(PluginContainer.class, container));
}
}

View file

@ -0,0 +1,18 @@
package net.frankheijden.serverutils.velocity.reflection;
import com.google.common.collect.Multimap;
import com.velocitypowered.api.scheduler.ScheduledTask;
import com.velocitypowered.api.scheduler.Scheduler;
import dev.frankheijden.minecraftreflection.MinecraftReflection;
public class RVelocityScheduler {
private static final MinecraftReflection reflection = MinecraftReflection
.of("com.velocitypowered.proxy.scheduler.VelocityScheduler");
private RVelocityScheduler() {}
public static Multimap<Object, ScheduledTask> getTasksByPlugin(Scheduler scheduler) {
return reflection.get(scheduler, "tasksByPlugin");
}
}

View file

@ -0,0 +1,12 @@
package net.frankheijden.serverutils.velocity.utils;
import com.velocitypowered.api.command.CommandSource;
import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.frankheijden.serverutils.velocity.entities.VelocityCommandSender;
public class VelocityUtils {
public static ServerCommandSender wrap(CommandSource source) {
return new VelocityCommandSender(source);
}
}

View file

@ -0,0 +1,7 @@
[settings]
check-updates-boot = true
check-updates-login = false
download-updates-boot = false
download-updates-login = false
install-updates-boot = false
install-updates-login = false

View file

@ -0,0 +1,67 @@
[serverutils]
success = "&3Successfully %action%ed &b%what%&3!"
warning = "&3Successfully %action%ed &b%what%&3, but with warnings."
error = "&cAn error occurred while %action%ing &4%what%&c, please check the console!"
not_exists = "&cAn error occurred while %action%ing &4%what%&c, plugin does not exist!"
not_enabled = "&cAn error occurred while %action%ing &4%what%&c, plugin is not enabled!"
already_loaded = "&cAn error occurred while %action%ing &4%what%&c, plugin is already loaded!"
already_enabled = "&cAn error occurred while %action%ing &4%what%&c, plugin is already enabled!"
already_disabled = "&cAn error occurred while %action%ing &4%what%&c, plugin is already disabled!"
file_deleted = "&cAccessing the jar file while %action%ing &4%what%&c went wrong, plugin has been deleted!"
invalid_description = "&cAn error occurred while %action%ing &4%what%&c, plugin doesn't have a valid description, please check the console!"
invalid_plugin = "&cAn error occurred while %action%ing &4%what%&c, plugin is invalid!"
unknown_dependency = "&cAn error occurred while %action%ing &4%what%&c, plugin has a dependeny which is not loaded: &4%arg%"
[serverutils.watcher]
start = "&3Started watching &b%what%&3!"
change = "&3Change detected for plugin &b%what%&3, reloading now..."
stopped = "&3Stopped watching &b%what%&3!"
not_watching = "&cWe aren't watching that plugin!"
[serverutils.update]
available = """
&8&m--------=&r&8[ &b&lServerUtils Velocity Update&r &8]&m=---------
&3Current version: &b%old%
&3New version: &b%new%
&3Release info: &b%info%
&8&m-------------------------------------------------"""
downloading = """
&8&m--------=&r&8[ &b&lServerUtils Velocity Update&r &8]&m=----------
&3A new version of ServerUtils will be downloaded and installed after a restart!
&3Current version: &b%old%
&3New version: &b%new%
&3Release info: &b%info%
&8&m-------------------------------------------------"""
download_failed = "&cFailed to download version %new% of ServerUtils. Please update manually."
download_success = "&3ServerUtils has been downloaded and will be installed on the next restart."
[serverutils.help]
header = "&8&m---------=&r&8[ &b&lServerUtils Velocity Help&r &8]&m=---------"
format = "&8/&3%command%&b%subcommand% &f(&7%help%&f)"
footer = "&8&m-------------------------------------------------"
[serverutils.plugins]
header = "&8&m--------=&r&8[ &b&lServerUtils Velocity Plugins&r &8]&m=-------"
prefix = " &3Plugins &8(&a%count%&8)&b: "
format = "&3%plugin%"
seperator = "&b, "
last_seperator = " &band "
version = " &8(&a%version%&8)"
footer = "&8&m-------------------------------------------------"
[serverutils.plugininfo]
header = "&8&m------=&r&8[ &b&lServerUtils Velocity PluginInfo&r &8]&m=------"
format = " &3%key%&8: &b%value%"
list_format = "&b%value%"
seperator = "&8, "
last_seperator = " &8and "
footer = "&8&m-------------------------------------------------"
[serverutils.commandinfo]
header = "&8&m-----=&r&8[ &b&lServerUtils Velocity CommandInfo&r &8]&m=------"
format = " &3%key%&8: &b%value%"
list_format = "&b%value%"
seperator = "&8, "
last_seperator = " &8and "
footer = "&8&m-------------------------------------------------"
not_exists = "&cThat command is not a valid registered command."