diff --git a/Bukkit/Loader/pom.xml b/Bukkit/Loader/pom.xml
index 5f80b67..b4dff27 100644
--- a/Bukkit/Loader/pom.xml
+++ b/Bukkit/Loader/pom.xml
@@ -47,7 +47,7 @@
shade
- true
+ false
antivpn-bukkit.jarinjar
diff --git a/Bukkit/Loader/src/main/java/dev/brighten/antivpn/bukkit/loader/BukkitLoaderPlugin.java b/Bukkit/Loader/src/main/java/dev/brighten/antivpn/bukkit/loader/BukkitLoaderPlugin.java
index 011ca77..63cc412 100644
--- a/Bukkit/Loader/src/main/java/dev/brighten/antivpn/bukkit/loader/BukkitLoaderPlugin.java
+++ b/Bukkit/Loader/src/main/java/dev/brighten/antivpn/bukkit/loader/BukkitLoaderPlugin.java
@@ -4,8 +4,6 @@ import dev.brighten.antivpn.loader.JarInJarClassLoader;
import dev.brighten.antivpn.loader.LoaderBootstrap;
import org.bukkit.plugin.java.JavaPlugin;
-import java.io.IOException;
-
public class BukkitLoaderPlugin extends JavaPlugin {
private static final String JAR_NAME = "antivpn-bukkit.jarinjar";
@@ -13,10 +11,9 @@ public class BukkitLoaderPlugin extends JavaPlugin {
private final LoaderBootstrap plugin;
- public BukkitLoaderPlugin() throws IOException {
- try(JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME)) {
- this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, JavaPlugin.class, this);
- }
+ public BukkitLoaderPlugin() {
+ JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME);
+ this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, JavaPlugin.class, this);
}
@Override
diff --git a/Bukkit/Plugin/pom.xml b/Bukkit/Plugin/pom.xml
index cae0a10..b9bff33 100644
--- a/Bukkit/Plugin/pom.xml
+++ b/Bukkit/Plugin/pom.xml
@@ -47,7 +47,7 @@
shade
- true
+ false
org.bstats
diff --git a/Bukkit/Plugin/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java b/Bukkit/Plugin/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java
index 1153063..540780c 100644
--- a/Bukkit/Plugin/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java
+++ b/Bukkit/Plugin/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java
@@ -14,7 +14,6 @@ import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.event.HandlerList;
-import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.SimplePluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
@@ -33,7 +32,11 @@ public class BukkitPlugin implements LoaderBootstrap {
private File dataFolder;
private final List registeredCommands = new ArrayList<>();
@Getter
- private Plugin plugin;
+ private final JavaPlugin plugin;
+
+ public BukkitPlugin(JavaPlugin plugin) {
+ this.plugin = plugin;
+ }
@Getter
private PlayerCommandRunner playerCommandRunner;
@@ -52,12 +55,10 @@ public class BukkitPlugin implements LoaderBootstrap {
playerCommandRunner = new PlayerCommandRunner();
playerCommandRunner.start();
- plugin = Bukkit.getPluginManager().getPlugin("AntiVPN");
-
// Loading our bStats metrics to be pushed to https://bstats.org
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
Bukkit.getLogger().info("Starting bStats metrics...");
- Metrics metrics = new Metrics((JavaPlugin) plugin, 12615);
+ Metrics metrics = new Metrics(plugin, 12615);
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
new BukkitRunnable() {
public void run() {
diff --git a/Universal/pom.xml b/Universal/pom.xml
index 6954189..956a675 100644
--- a/Universal/pom.xml
+++ b/Universal/pom.xml
@@ -38,7 +38,7 @@
dev.brighten.antivpn
- Velocity
+ VelocityPlugin
${project.version}
compile
diff --git a/Velocity/VelocityLoader/pom.xml b/Velocity/VelocityLoader/pom.xml
new file mode 100644
index 0000000..12ea9cf
--- /dev/null
+++ b/Velocity/VelocityLoader/pom.xml
@@ -0,0 +1,109 @@
+
+
+ 4.0.0
+
+ dev.brighten.antivpn
+ Velocity
+ 1.9.4
+ ../pom.xml
+
+
+ VelocityLoader
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ velocity
+ https://nexus.velocitypowered.com/repository/maven-public/
+
+
+
+
+
+ com.velocitypowered
+ velocity-api
+ 3.4.0-SNAPSHOT
+ provided
+
+
+ org.bstats
+ bstats-velocity
+ 2.2.1
+ compile
+
+
+ dev.brighten.antivpn
+ loader-utils
+ 1.9.4
+ compile
+
+
+ dev.brighten.antivpn
+ VelocityPlugin
+ 1.9.4
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+ 17
+ 17
+ -XDignore.symbol.file
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.6.0
+
+
+ package
+
+ shade
+
+
+ false
+
+
+ antivpn-velocity.jarinjar
+ ${project.parent.basedir}/VelocityPlugin/target/VelocityPlugin-${project.version}.jar
+
+
+
+
+ org.bstats
+
+ dev.brighten.antivpn.velocity.org.bstats
+
+
+ org.yaml.snakeyaml
+ dev.brighten.antivpn.shaded.org.yaml.snakeyaml
+
+
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Velocity/VelocityLoader/src/main/java/dev/brighten/antivpn/velocity/VelocityPluginLoader.java b/Velocity/VelocityLoader/src/main/java/dev/brighten/antivpn/velocity/VelocityPluginLoader.java
new file mode 100644
index 0000000..8de032d
--- /dev/null
+++ b/Velocity/VelocityLoader/src/main/java/dev/brighten/antivpn/velocity/VelocityPluginLoader.java
@@ -0,0 +1,50 @@
+package dev.brighten.antivpn.velocity;
+
+import com.google.inject.Inject;
+import com.velocitypowered.api.event.Subscribe;
+import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
+import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
+import com.velocitypowered.api.plugin.Plugin;
+import com.velocitypowered.api.plugin.annotation.DataDirectory;
+import com.velocitypowered.api.proxy.ProxyServer;
+import dev.brighten.antivpn.loader.JarInJarClassLoader;
+import dev.brighten.antivpn.loader.LoaderBootstrap;
+import org.bstats.velocity.Metrics;
+
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+@Plugin(id = "kaurivpn", name = "KauriVPN", version = "1.7.1", authors = {"funkemunky"})
+public class VelocityPluginLoader {
+
+ private static final String JAR_NAME = "antivpn-velocity.jarinjar";
+ private static final String BOOTSTRAP_CLASS = "dev.brighten.antivpn.velocity.VelocityPlugin";
+
+ private final LoaderBootstrap plugin;
+
+ @Inject
+ public VelocityPluginLoader(ProxyServer server, Logger logger, @DataDirectory Path path, Metrics.Factory metricsFactory) {
+ Map, Object> instances = new HashMap<>();
+ instances.put(ProxyServer.class, server);
+ instances.put(Logger.class, logger);
+ instances.put(Path.class, path);
+ instances.put(String.class, metricsFactory);
+ instances.put(LoaderBootstrap.class, this);
+ JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME);
+ this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, Map.class, instances);
+ plugin.onLoad(path.toFile());
+ }
+
+ @Subscribe
+ public void onInit(ProxyInitializeEvent event) {
+ plugin.onEnable();
+ }
+
+ @Subscribe
+ public void onDisable(ProxyShutdownEvent event) {
+ plugin.onDisable();
+ }
+
+}
diff --git a/Velocity/src/main/resources/velocity-plugin.json b/Velocity/VelocityLoader/src/main/resources/velocity-plugin.json
similarity index 75%
rename from Velocity/src/main/resources/velocity-plugin.json
rename to Velocity/VelocityLoader/src/main/resources/velocity-plugin.json
index 1c547a3..d23d87a 100644
--- a/Velocity/src/main/resources/velocity-plugin.json
+++ b/Velocity/VelocityLoader/src/main/resources/velocity-plugin.json
@@ -1 +1 @@
-{"id":"kaurivpn","name":"KauriVPN","version":"${project.version}","authors":["funkemunky"],"dependencies":[],"main":"dev.brighten.antivpn.velocity.VelocityPlugin"}
+{"id":"kaurivpn","name":"KauriVPN","version":"${project.version}","authors":["funkemunky"],"dependencies":[],"main":"dev.brighten.antivpn.velocity.VelocityPluginLoader"}
diff --git a/Velocity/VelocityPlugin/pom.xml b/Velocity/VelocityPlugin/pom.xml
new file mode 100644
index 0000000..94655c2
--- /dev/null
+++ b/Velocity/VelocityPlugin/pom.xml
@@ -0,0 +1,102 @@
+
+
+ 4.0.0
+
+ dev.brighten.antivpn
+ AntiVPN
+ 1.9.4
+ ../../pom.xml
+
+
+ VelocityPlugin
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ velocity
+ https://nexus.velocitypowered.com/repository/maven-public/
+
+
+
+
+
+ com.velocitypowered
+ velocity-api
+ 3.4.0-SNAPSHOT
+ provided
+
+
+ dev.brighten.antivpn
+ Source
+ 1.9.4
+ compile
+
+
+ org.bstats
+ bstats-velocity
+ 2.2.1
+ compile
+
+
+ dev.brighten.antivpn
+ loader-utils
+ 1.9.4
+ compile
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+ 17
+ 17
+ -XDignore.symbol.file
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.6.0
+
+
+
+ org.bstats
+
+ dev.brighten.antivpn.velocity.org.bstats
+
+
+ org.yaml.snakeyaml
+ dev.brighten.antivpn.shaded.org.yaml.snakeyaml
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java
similarity index 95%
rename from Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java
rename to Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java
index 8f79deb..242379b 100644
--- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java
+++ b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java
@@ -20,14 +20,14 @@ public class VelocityListener extends VPNExecutor {
@Override
public void registerListeners() {
VelocityPlugin.INSTANCE.getServer().getEventManager()
- .register(VelocityPlugin.INSTANCE, this);
+ .register(VelocityPlugin.INSTANCE.getPluginInstance(), this);
- VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, DisconnectEvent.class,
+ VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE.getPluginInstance(), DisconnectEvent.class,
event -> AntiVPN.getInstance()
.getPlayerExecutor()
.unloadPlayer(event.getPlayer().getUniqueId()));
- VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class,
+ VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE.getPluginInstance(), LoginEvent.class,
event -> {
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
.orElse(new OfflinePlayer(
@@ -100,7 +100,7 @@ public class VelocityListener extends VPNExecutor {
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
switch (checkResult.resultType()) {
case DENIED_PROXY -> VelocityPlugin.INSTANCE.getServer().getScheduler()
- .buildTask(VelocityPlugin.INSTANCE, () ->
+ .buildTask(VelocityPlugin.INSTANCE.getPluginInstance(), () ->
event.getPlayer().disconnect(LegacyComponentSerializer.builder()
.character('&')
.build().deserialize(AntiVPN.getInstance().getVpnConfig()
@@ -110,7 +110,7 @@ public class VelocityListener extends VPNExecutor {
.replace("%code%", result.getCountryCode()))))
.delay(1, TimeUnit.SECONDS).schedule();
case DENIED_COUNTRY -> VelocityPlugin.INSTANCE.getServer().getScheduler()
- .buildTask(VelocityPlugin.INSTANCE, () ->
+ .buildTask(VelocityPlugin.INSTANCE.getPluginInstance(), () ->
event.getPlayer().disconnect(LegacyComponentSerializer.builder()
.character('&')
.build().deserialize(AntiVPN.getInstance().getVpnConfig()
@@ -191,7 +191,7 @@ public class VelocityListener extends VPNExecutor {
@Override
public void disablePlugin() {
- VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this);
+ VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE.getPluginInstance(), this);
VelocityPlugin.INSTANCE.getServer().getCommandManager().unregister("antivpn");
}
}
diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayer.java b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayer.java
similarity index 100%
rename from Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayer.java
rename to Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayer.java
diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java
similarity index 100%
rename from Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java
rename to Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java
diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java
similarity index 70%
rename from Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java
rename to Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java
index da0de33..29d3dc4 100644
--- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java
+++ b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java
@@ -1,11 +1,5 @@
package dev.brighten.antivpn.velocity;
-import com.google.inject.Inject;
-import com.velocitypowered.api.event.Subscribe;
-import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
-import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
-import com.velocitypowered.api.plugin.Plugin;
-import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.command.Command;
@@ -13,18 +7,20 @@ import dev.brighten.antivpn.database.VPNDatabase;
import dev.brighten.antivpn.database.local.H2VPN;
import dev.brighten.antivpn.database.mongo.MongoVPN;
import dev.brighten.antivpn.database.sql.MySqlVPN;
+import dev.brighten.antivpn.loader.LoaderBootstrap;
import dev.brighten.antivpn.velocity.command.VelocityCommand;
import lombok.Getter;
import org.bstats.charts.SimplePie;
import org.bstats.velocity.Metrics;
import javax.annotation.Nullable;
+import java.io.File;
import java.nio.file.Path;
+import java.util.Map;
import java.util.logging.Logger;
@Getter
-@Plugin(id = "kaurivpn", name = "KauriVPN", version = "1.7.1", authors = {"funkemunky"})
-public class VelocityPlugin {
+public class VelocityPlugin implements LoaderBootstrap {
private final ProxyServer server;
private final Logger logger;
@@ -37,52 +33,14 @@ public class VelocityPlugin {
public static VelocityPlugin INSTANCE;
- @Inject
- public VelocityPlugin(ProxyServer server, Logger logger, @DataDirectory Path path, Metrics.Factory metricsFactory) {
- this.server = server;
- this.logger = logger;
- this.configDir = path;
- this.metricsFactory = metricsFactory;
- }
+ private final Object pluginInstance;
- @Subscribe
- public void onInit(ProxyInitializeEvent event) {
- INSTANCE = this;
- logger.info("Loading config...");
-
- //Loading plugin
- logger.info("Starting AntiVPN services...");
- AntiVPN.start(new VelocityListener(), new VelocityPlayerExecutor(), configDir.toFile());
-
- if(AntiVPN.getInstance().getVpnConfig().metrics()) {
- logger.info("Starting metrics...");
- metrics = metricsFactory.make(this, 12791);
-
- metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
- }
-
- logger.info("Registering commands...");
- for (Command command : AntiVPN.getInstance().getCommands()) {
- server.getCommandManager().register(server.getCommandManager().metaBuilder(command.name())
- .aliases(command.aliases()).build(), new VelocityCommand(command));
- }
- }
-
- @Subscribe
- public void onDisable(ProxyShutdownEvent event) {
- logger.info("Disabling AntiVPN...");
- AntiVPN.getInstance().getExecutor().log("Disabling AntiVPN...");
-
- if (AntiVPN.getInstance().getDatabase() != null) {
- AntiVPN.getInstance().stop();
- }
-
- if (metrics != null) {
- metrics = null;
- }
-
- INSTANCE = null;
- logger.info("Disabled AntiVPN.");
+ public VelocityPlugin(Map, Object> objectsMap) {
+ this.server = (ProxyServer) objectsMap.get(ProxyServer.class);
+ this.logger = (Logger) objectsMap.get(Logger.class);
+ this.configDir = (Path) objectsMap.get(Path.class);
+ this.metricsFactory = (Metrics.Factory) objectsMap.get(String.class);
+ this.pluginInstance = objectsMap.get(LoaderBootstrap.class);
}
private String getDatabaseType() {
@@ -98,4 +56,50 @@ public class VelocityPlugin {
return "No-Database";
}
}
+
+ @Override
+ public void onLoad(File dataFolder) {
+
+ }
+
+ @Override
+ public void onEnable() {
+ INSTANCE = this;
+ logger.info("Loading config...");
+
+ //Loading plugin
+ logger.info("Starting AntiVPN services...");
+ AntiVPN.start(new VelocityListener(), new VelocityPlayerExecutor(), configDir.toFile());
+
+
+ if(AntiVPN.getInstance().getVpnConfig().metrics()) {
+ logger.info("Starting metrics...");
+ metrics = metricsFactory.make(pluginInstance, 12791);
+
+ metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
+ }
+
+ logger.info("Registering commands...");
+ for (Command command : AntiVPN.getInstance().getCommands()) {
+ server.getCommandManager().register(server.getCommandManager().metaBuilder(command.name())
+ .aliases(command.aliases()).build(), new VelocityCommand(command));
+ }
+ }
+
+ @Override
+ public void onDisable() {
+ logger.info("Disabling AntiVPN...");
+ AntiVPN.getInstance().getExecutor().log("Disabling AntiVPN...");
+
+ if (AntiVPN.getInstance().getDatabase() != null) {
+ AntiVPN.getInstance().stop();
+ }
+
+ if (metrics != null) {
+ metrics = null;
+ }
+
+ INSTANCE = null;
+ logger.info("Disabled AntiVPN.");
+ }
}
diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/command/VelocityCommand.java b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/command/VelocityCommand.java
similarity index 100%
rename from Velocity/src/main/java/dev/brighten/antivpn/velocity/command/VelocityCommand.java
rename to Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/command/VelocityCommand.java
diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/command/VelocityCommandExecutor.java b/Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/command/VelocityCommandExecutor.java
similarity index 100%
rename from Velocity/src/main/java/dev/brighten/antivpn/velocity/command/VelocityCommandExecutor.java
rename to Velocity/VelocityPlugin/src/main/java/dev/brighten/antivpn/velocity/command/VelocityCommandExecutor.java
diff --git a/Velocity/pom.xml b/Velocity/pom.xml
index 660054e..a88309c 100644
--- a/Velocity/pom.xml
+++ b/Velocity/pom.xml
@@ -7,6 +7,7 @@
dev.brighten.antivpn
1.9.4
+ pom
4.0.0
Velocity
@@ -15,80 +16,4 @@
17
17
-
-
-
- velocity
- https://nexus.velocitypowered.com/repository/maven-public/
-
-
-
-
-
- com.velocitypowered
- velocity-api
- 3.4.0-SNAPSHOT
- provided
-
-
- dev.brighten.antivpn
- Source
- 1.9.4
- provided
-
-
- org.bstats
- bstats-velocity
- 2.2.1
- compile
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.13.0
-
- 17
- 17
- -XDignore.symbol.file
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 3.6.0
-
-
-
- org.bstats
-
- dev.brighten.antivpn.velocity.org.bstats
-
-
- org.yaml.snakeyaml
- dev.brighten.antivpn.shaded.org.yaml.snakeyaml
-
-
-
-
-
- package
-
- shade
-
-
-
-
-
-
-
- src/main/resources
- true
-
-
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 25ea794..6cd1b17 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,8 @@
Velocity
Sponge
Universal
+ Velocity/VelocityLoader
+ Velocity/VelocityPlugin