Rework SubServers.Bungee's app lifecycle

SubServers.Bungee now puts code in 2 new places to better load/unload it's data.

When supported, this should give it better compatabilty with ordanary plugins. Also, it no longer has to 'shutdown' to handle /greload. Isn't that neat?
This commit is contained in:
ME1312 2021-02-06 23:53:03 -05:00
parent 25fff82af6
commit 3dea3b67d0
No known key found for this signature in database
GPG Key ID: FEFFE2F698E88FA8
20 changed files with 230 additions and 269 deletions

View File

@ -30,13 +30,13 @@
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiUtil</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiEngine</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -26,14 +26,6 @@ public interface BungeeAPI {
return ((BungeeCommon) ProxyServer.getInstance()).api.run();
}
/**
* Adds a SubAPI Listener
*
* @param enable An Event that will be called when SubAPI is ready
* @param disable An Event that will be called before SubAPI is disabled (your plugin should reset it's values in case this is a hard-reset instead of a shutdown)
*/
void addListener(Runnable enable, Runnable disable);
/**
* Get the number of players on this network across all known proxies
*

View File

@ -19,7 +19,6 @@ public class Logger {
* @param prefix Prefix
* @return Logger
*/
@SuppressWarnings("deprecation")
public static java.util.logging.Logger get(String prefix) {
if (!existing.keySet().contains(prefix)) {
java.util.logging.Logger log = Util.getDespiteException(() -> Util.reflect(Class.forName("net.ME1312.Galaxi.Library.Log.Logger").getDeclaredMethod("toPrimitive"),
@ -29,23 +28,14 @@ public class Logger {
log = java.util.logging.Logger.getAnonymousLogger();
log.setUseParentHandlers(false);
log.addHandler(new Handler() {
private boolean open = true;
@Override
public void publish(LogRecord record) {
if (open)
BungeeCommon.getInstance().getLogger().log(record.getLevel(), prefix + " > " + record.getMessage(), record.getParameters());
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
open = false;
}
public void flush() {}
public void close() {}
});
}
existing.put(prefix, log);

View File

@ -30,14 +30,14 @@
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiUtil</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiEngine</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -465,13 +465,11 @@ public abstract class Host implements ExtraDataHandler {
*/
public boolean destroy() {
try {
List<String> subservers = new ArrayList<String>();
subservers.addAll(getSubServers().keySet());
String[] subservers = getSubServers().keySet().toArray(new String[0]);
for (String server : subservers) {
forceRemoveSubServer(server);
}
subservers.clear();
getCreator().terminate();
getCreator().waitFor();
return true;

View File

@ -124,9 +124,9 @@ public class InternalHost extends Host {
server.stop();
server.waitFor();
}
servers.remove(name.toLowerCase());
if (UPnP.isUPnPAvailable() && UPnP.isMappedTCP(server.getAddress().getPort()))
UPnP.closePortTCP(server.getAddress().getPort());
servers.remove(name.toLowerCase());
return true;
} else return false;
}

View File

@ -1,19 +1,23 @@
package net.ME1312.SubServers.Bungee.Library.Compatibility;
import net.ME1312.Galaxi.Library.Callback.ExceptionRunnable;
import net.ME1312.Galaxi.Library.Util;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.PluginDescription;
import java.io.File;
import java.io.IOException;
public final class Plugin extends net.md_5.bungee.api.plugin.Plugin {
private static final PluginDescription description = new PluginDescription();
private final boolean invalid;
private final ExceptionRunnable enable;
private final Runnable disable;
@Deprecated
public Plugin() {
this.invalid = true;
enable = null;
disable = null;
}
private static PluginDescription describe() {
@ -25,9 +29,10 @@ public final class Plugin extends net.md_5.bungee.api.plugin.Plugin {
return description;
}
public Plugin(ProxyServer proxy) {
public Plugin(ProxyServer proxy, ExceptionRunnable enable, Runnable disable) {
super(proxy, describe());
this.invalid = false;
this.enable = enable;
this.disable = disable;
// 2020 BungeeCord builds don't run init(), but future builds may uncomment that line. We wouldn't want to repeat ourselves.
if (getDescription() == null) Util.isException(() -> Util.reflect(net.md_5.bungee.api.plugin.Plugin.class.getDeclaredMethod("init", ProxyServer.class, PluginDescription.class), this, proxy, description));
@ -35,6 +40,17 @@ public final class Plugin extends net.md_5.bungee.api.plugin.Plugin {
@Override
public void onEnable() {
if (invalid) throw new IllegalStateException("SubServers.Bungee does not run as a plugin, but a wrapper. For more information on how to install, please visit this page: https://github.com/ME1312/SubServers-2/wiki/Install");
if (enable == null) {
throw new IllegalStateException("SubServers.Bungee does not run as a plugin, but a wrapper. For more information on how to install, please visit this page: https://github.com/ME1312/SubServers-2/wiki/Install");
} else try {
enable.run();
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public void onDisable() {
if (disable != null) disable.run();
}
}

View File

@ -28,9 +28,7 @@ import java.util.*;
* SubAPI Class
*/
public final class SubAPI implements BungeeAPI {
LinkedList<Runnable> enableListeners = new LinkedList<Runnable>();
LinkedList<Runnable> reloadListeners = new LinkedList<Runnable>();
LinkedList<Runnable> disableListeners = new LinkedList<Runnable>();
private static HashMap<String, Object> knownSignatures = new HashMap<String, Object>();
boolean ready = false;
private final SubProxy plugin;
@ -63,25 +61,11 @@ public final class SubAPI implements BungeeAPI {
}
/**
* Adds a SubAPI Listener
* Adds a SubAPI Reload Listener
*
* @param enable An Event that will be called when SubAPI is ready
* @param disable An Event that will be called before SubAPI is disabled (your plugin should reset it's values in case this is a hard-reset instead of a shutdown)
*/
public void addListener(Runnable enable, Runnable disable) {
if (enable != null) enableListeners.add(enable);
if (disable != null) disableListeners.add(disable);
}
/**
* Adds a SubAPI Listener
*
* @param enable An Event that will be called when SubAPI is ready
* @param reload An Event that will be called after SubAPI is soft-reloaded
* @param disable An Event that will be called before SubAPI is disabled (your plugin should reset it's values in case this is a hard-reset instead of a shutdown)
*/
public void addListener(Runnable enable, Runnable reload, Runnable disable) {
addListener(enable, disable);
public void addListener(Runnable reload) {
if (reload != null) reloadListeners.add(reload);
}

View File

@ -121,16 +121,27 @@ public final class SubCommand extends CommandX {
} catch (Exception e) {}
}, "SubServers.Bungee::Update_Check").start();
} else if (args[0].equalsIgnoreCase("reload")) {
new Thread(() -> {
if (args.length > 1) {
switch (args[1].toLowerCase()) {
case "*":
case "all":
case "hard":
case "system":
case "bungee":
case "bungeecord":
case "subdata":
case "network":
plugin.getPluginManager().dispatchCommand(ConsoleCommandSender.getInstance(), "greload");
plugin.stopListeners();
plugin.getLogger().info("Closing player connections");
for (ProxiedPlayer player : plugin.getPlayers()) {
Util.isException(() -> player.disconnect(plugin.getTranslation("restart")));
}
plugin.shutdown();
case "soft":
case "bungee":
case "bungeecord":
case "plugin":
case "plugins":
plugin.getPluginManager().dispatchCommand(sender, "greload");
break;
case "host":
case "hosts":
@ -167,6 +178,7 @@ public final class SubCommand extends CommandX {
e.printStackTrace();
}
}
}, "SubServers.Bungee::Reload_Command_Handler").start();
} else if (args[0].equalsIgnoreCase("list")) {
String div = ChatColor.RESET + ", ";
int i = 0;
@ -830,7 +842,7 @@ public final class SubCommand extends CommandX {
" Help: /sub help",
" List: /sub list",
" Version: /sub version",
" Reload: /sub reload [all|config|templates]",
" Reload: /sub reload [hard|bungee|servers|templates]",
" Info: /sub info [proxy|host|group|server|player] <Name>",
" Start Server: /sub start <Subservers>",
" Restart Server: /sub restart <Subservers>",
@ -987,12 +999,12 @@ public final class SubCommand extends CommandX {
} else if (!(sender instanceof ProxiedPlayer) && (args[0].equals("reload") || args[0].equals("restore"))) {
if (args[0].equals("reload")) {
List<String> list = new ArrayList<String>(),
completes = Arrays.asList("all", "config", "templates");
completes = Arrays.asList("hard", "bungee", "servers", "templates");
if (args.length == 2) {
for (String complete : completes) {
if (complete.toLowerCase().startsWith(last)) list.add(Last + complete.substring(last.length()));
}
return new ContainedPair<>((list.size() <= 0)?plugin.api.getLang("SubServers", "Command.Generic.Unknown").replace("$str$", args[0]):null, list);
return new ContainedPair<>(null, list);
} else {
return new ContainedPair<>(null, Collections.emptyList());
}

View File

@ -11,7 +11,6 @@ import net.ME1312.SubData.Server.ClientHandler;
import net.ME1312.SubData.Server.Encryption.AES;
import net.ME1312.SubData.Server.Encryption.DHE;
import net.ME1312.SubData.Server.Encryption.RSA;
import net.ME1312.SubData.Server.Library.DataSize;
import net.ME1312.SubData.Server.SubDataClient;
import net.ME1312.SubData.Server.SubDataServer;
import net.ME1312.SubServers.Bungee.Event.SubRemoveProxyEvent;
@ -47,6 +46,7 @@ import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.*;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.event.EventHandler;
@ -62,6 +62,7 @@ import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.logging.Handler;
/**
* Main Plugin Class
@ -241,8 +242,9 @@ public final class SubProxy extends BungeeCommon implements Listener {
api.addHostDriver(net.ME1312.SubServers.Bungee.Host.Internal.InternalHost.class, "virtual");
api.addHostDriver(net.ME1312.SubServers.Bungee.Host.External.ExternalHost.class, "network");
plugin = Util.getDespiteException(() -> new Plugin(this), null);
plugin = Util.getDespiteException(() -> new Plugin(this, this::reload, this::shutdown), null);
if (plugin == null) Logger.get("SubServers").warning("Could not initialize plugin object emulation");
else Util.isException(() -> Util.<LinkedHashMap<String, net.md_5.bungee.api.plugin.Plugin>>reflect(PluginManager.class.getDeclaredField("plugins"), getPluginManager()).put(null, plugin));
getPluginManager().registerListener(plugin, this);
@ -292,21 +294,21 @@ public final class SubProxy extends BungeeCommon implements Listener {
@SuppressWarnings("unchecked")
public void startListeners() {
try {
reload();
if (posted || !api.ready) reload();
if (UPnP.isUPnPAvailable()) {
if (config.get().getMap("Settings").getMap("UPnP", new ObjectMap<String>()).getBoolean("Forward-Proxy", true)) for (ListenerInfo listener : getConfig().getListeners()) {
UPnP.openPortTCP(listener.getHost().getPort());
}
} else {
getLogger().warning("UPnP is currently unavailable. Ports may not be automatically forwarded on this device.");
getLogger().warning("UPnP service is unavailable. SubServers can't port-forward for you from this device.");
}
super.startListeners();
if (!posted) {
post();
posted = true;
post();
}
} catch (IOException e) {
e.printStackTrace();
@ -429,16 +431,7 @@ public final class SubProxy extends BungeeCommon implements Listener {
int subservers = 0;
Logger.get("SubServers").info(((status)?"Rel":"L")+"oading SubServers...");
if (!posted) Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (running) {
Logger.get("SubServers").info("Received request from system to shutdown");
try {
shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}, "SubServers.Bungee::System_Shutdown"));
if (!posted) Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown, "SubServers.Bungee::System_Shutdown"));
running = true;
List<String> autorun = new LinkedList<String>();
for (String name : this.servers.get().getMap("Servers").getKeys()) {
@ -605,7 +598,8 @@ public final class SubProxy extends BungeeCommon implements Listener {
}
int plugins = 0;
List<Runnable> listeners = (status)?api.reloadListeners:api.enableListeners;
if (status) {
List<Runnable> listeners = api.reloadListeners;
if (listeners.size() > 0) {
Logger.get("SubServers").info(((status)?"Rel":"L")+"oading SubAPI Plugins...");
for (Runnable obj : listeners) {
@ -618,7 +612,6 @@ public final class SubProxy extends BungeeCommon implements Listener {
}
}
if (status) {
for (Host host : api.getHosts().values()) if (host instanceof ClientHandler && ((ClientHandler) host).getSubData()[0] != null) ((SubDataClient) ((ClientHandler) host).getSubData()[0]).sendPacket(new PacketOutExReload(null));
for (Server server : api.getServers().values()) if (server.getSubData()[0] != null) ((SubDataClient) server.getSubData()[0]).sendPacket(new PacketOutExReload(null));
}
@ -733,23 +726,6 @@ public final class SubProxy extends BungeeCommon implements Listener {
}
}
}, TimeUnit.SECONDS.toMillis(rpec_s), TimeUnit.SECONDS.toMillis(rpec_i));
new Timer("SubServers.Bungee::Garbo_Collector").schedule(new TimerTask() {
@Override
public void run() {
long start = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
System.gc();
Timer timer = new Timer("SubServers.Bungee::Garbo_Collector_Result");
timer.schedule(new TimerTask() {
@Override
public void run() {
long end = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Logger.get("SGC").info("Cleared " + (start - end) + " bytes of extremely useless memory data. Now using " + end + " bytes.");
if (subdata != null) Logger.get("SDD").info(subdata.getClients().size() + " SubData channels are open.");
timer.cancel();
}
}, TimeUnit.MINUTES.toMillis(1));
}
}, TimeUnit.HOURS.toMillis(1), TimeUnit.HOURS.toMillis(1));
}
/**
@ -759,57 +735,49 @@ public final class SubProxy extends BungeeCommon implements Listener {
*/
@Override
public void stopListeners() {
try {
if (running) {
legServers.clear();
legServers.putAll(getServersCopy());
if (api.disableListeners.size() > 0) {
Logger.get("SubServers").info("Resetting SubAPI Plugins...");
for (Runnable listener : api.disableListeners) {
try {
listener.run();
} catch (Throwable e) {
new InvocationTargetException(e, "Problem disabling plugin").printStackTrace();
}
}
}
shutdown();
subdata.close();
for (ListenerInfo listener : getConfig().getListeners()) {
if (UPnP.isUPnPAvailable() && UPnP.isMappedTCP(listener.getHost().getPort())) UPnP.closePortTCP(listener.getHost().getPort());
}
} catch (Exception e) {
e.printStackTrace();
}
ListenerInfo[] listeners = getConfig().getListeners().toArray(new ListenerInfo[0]);
super.stopListeners();
} private void shutdown() throws Exception {
if (UPnP.isUPnPAvailable()) {
for (ListenerInfo listener : listeners) {
if (UPnP.isMappedTCP(listener.getHost().getPort())) UPnP.closePortTCP(listener.getHost().getPort());
}
}
}
} protected void shutdown() {
if (running) {
api.ready = false;
Logger.get("SubServers").info("Resetting Hosts and Server Data");
List<String> hosts = new ArrayList<String>();
hosts.addAll(this.hosts.keySet());
Logger.get("SubServers").info("Stopping hosted servers");
String[] hosts = this.hosts.keySet().toArray(new String[0]);
for (String host : hosts) {
api.forceRemoveHost(host);
}
Logger.get("SubServers").info("Removing dynamic data");
running = false;
this.hosts.clear();
exServers.clear();
this.hosts.clear();
for (String proxy : proxies.keySet()) {
getPluginManager().callEvent(new SubRemoveProxyEvent(proxies.get(proxy)));
}
proxies.clear();
for (ListenerInfo listener : getConfig().getListeners()) {
if (UPnP.isUPnPAvailable() && UPnP.isMappedTCP(listener.getHost().getPort())) UPnP.closePortTCP(listener.getHost().getPort());
String[] proxies = this.proxies.keySet().toArray(new String[0]);
for (String proxy : proxies) {
getPluginManager().callEvent(new SubRemoveProxyEvent(this.proxies.get(proxy)));
}
this.proxies.clear();
rPlayerLinkS.clear();
rPlayerLinkP.clear();
rPlayers.clear();
try {
subdata.close();
Thread.sleep(500);
} catch (InterruptedException | IOException e) {}
}
}
String getNewSignature() {

View File

@ -48,7 +48,7 @@
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiUtil</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>

View File

@ -20,7 +20,7 @@
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiUtil</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -30,7 +30,7 @@
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiUtil</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>

View File

@ -45,12 +45,12 @@ public final class ConsolePlugin extends Plugin implements Listener {
e.printStackTrace();
}
SubAPI.getInstance().addListener(null, new Runnable() {
SubAPI.getInstance().addListener(new Runnable() {
@Override
public void run() {
reload();
}
}, null);
});
}
private void reload() {

View File

@ -20,7 +20,7 @@
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiEngine</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>

View File

@ -224,14 +224,18 @@ public final class ExHost {
updcount++;
}
}
if (updcount > 0) log.info.println("SubServers.Host v" + updversion + " is available. You are " + updcount + " version" + ((updcount == 1)?"":"s") + " behind.");
if (updcount > 0) {
log.info.println("SubServers.Host v" + updversion + " is available. You are " + updcount + " version" + ((updcount == 1)?"":"s") + " behind.");
return true;
}
} catch (Exception e) {}
return false;
});
engine.start(this::stop);
if (!UPnP.isUPnPAvailable()) {
log.warn.println("UPnP is currently unavailable. Ports may not be automatically forwarded on this device.");
log.warn.println("UPnP service is unavailable. SubServers can't port-forward for you from this device.");
}
} catch (Exception e) {
if (engine == null) {
@ -289,19 +293,19 @@ public final class ExHost {
private void stop() {
if (running) {
log.info.println("Shutting down...");
log.info.println("Stopping hosted servers");
String[] subservers = servers.keySet().toArray(new String[0]);
List<String> subservers = new ArrayList<String>();
subservers.addAll(servers.keySet());
for (String server : subservers) {
servers.get(server).stop();
for (String name : subservers) {
SubServerImpl server = servers.get(name);
server.stop();
try {
servers.get(server).waitFor();
server.waitFor();
} catch (Exception e) {
log.error.println(e);
}
if (UPnP.isUPnPAvailable() && UPnP.isMappedTCP(servers.get(server).getPort())) UPnP.closePortTCP(servers.get(server).getPort());
servers.remove(name);
if (UPnP.isUPnPAvailable() && UPnP.isMappedTCP(server.getPort())) UPnP.closePortTCP(server.getPort());
}
servers.clear();

View File

@ -30,14 +30,14 @@
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiUtil</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.ME1312.Galaxi</groupId>
<artifactId>GalaxiEngine</artifactId>
<version>21w04a</version>
<version>21w06a</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -268,7 +268,7 @@ public final class ExProxy extends BungeeCommon implements Listener {
new Timer("SubServers.Sync::RemotePlayer_Error_Checking").schedule(new TimerTask() {
@Override
public void run() {
if (api.getSubDataNetwork()[0] != null && !api.getSubDataNetwork()[0].isClosed()) {
if (api.getName() != null && api.getSubDataNetwork()[0] != null && !api.getSubDataNetwork()[0].isClosed()) {
api.getProxy(api.getName(), proxy -> {
synchronized (rPlayers) {
ArrayList<CachedPlayer> add = new ArrayList<CachedPlayer>();
@ -376,7 +376,7 @@ public final class ExProxy extends BungeeCommon implements Listener {
@Override
public void stopListeners() {
try {
Logger.get("SubServers").info("Resetting Server Data");
Logger.get("SubServers").info("Removing synced data");
servers.clear();
reconnect = false;
@ -389,8 +389,10 @@ public final class ExProxy extends BungeeCommon implements Listener {
subdata.clear();
subdata.put(0, null);
if (UPnP.isUPnPAvailable()) {
for (ListenerInfo listener : getConfig().getListeners()) {
if (UPnP.isUPnPAvailable() && UPnP.isMappedTCP(listener.getHost().getPort())) UPnP.closePortTCP(listener.getHost().getPort());
if (UPnP.isMappedTCP(listener.getHost().getPort())) UPnP.closePortTCP(listener.getHost().getPort());
}
}
rPlayerLinkS.clear();

View File

@ -20,8 +20,6 @@ import java.util.*;
* SubAPI Class
*/
public final class SubAPI extends ClientAPI implements BungeeAPI {
LinkedList<Runnable> enableListeners = new LinkedList<Runnable>();
LinkedList<Runnable> disableListeners = new LinkedList<Runnable>();
private final ExProxy plugin;
private static SubAPI api;
String name;
@ -52,17 +50,6 @@ public final class SubAPI extends ClientAPI implements BungeeAPI {
return plugin;
}
/**
* Adds a SubAPI Listener
*
* @param enable An Event that will be called when SubAPI is ready
* @param disable An Event that will be called before SubAPI is disabled (your plugin should reset it's values in case this is a hard-reset instead of a shutdown)
*/
public void addListener(Runnable enable, Runnable disable) {
if (enable != null) enableListeners.add(enable);
if (disable != null) disableListeners.add(disable);
}
/**
* Get the Proxy Name
*

View File

@ -1463,6 +1463,9 @@ public final class SubCommand extends CommandX {
@SuppressWarnings("deprecation")
@Override
public void execute(CommandSender sender, String[] args) {
if (plugin.lang == null) {
throw new IllegalStateException("There are no lang options available at this time");
} else {
if (sender instanceof ProxiedPlayer) {
if (args.length > 0) {
Map<String, ServerImpl> servers = plugin.servers;
@ -1498,6 +1501,7 @@ public final class SubCommand extends CommandX {
sender.sendMessage(plugin.api.getLang("SubServers", "Command.Generic.Player-Only"));
}
}
}
/**
* Suggest command arguments
@ -1507,7 +1511,7 @@ public final class SubCommand extends CommandX {
* @return The validator's response and list of possible arguments
*/
public Pair<String, List<String>> suggestArguments(CommandSender sender, String[] args) {
if (args.length <= 1) {
if (plugin.lang != null && args.length <= 1) {
String last = (args.length > 0)?args[args.length - 1].toLowerCase():"";
List<String> list = new ArrayList<String>();
if (last.length() == 0) {
@ -1557,6 +1561,9 @@ public final class SubCommand extends CommandX {
@SuppressWarnings("deprecation")
@Override
public void execute(CommandSender sender, String[] args) {
if (plugin.lang == null) {
throw new IllegalStateException("There are no lang options available at this time");
} else {
List<String> messages = new LinkedList<String>();
int players = 0;
for (ServerImpl server : plugin.servers.values()) {
@ -1580,4 +1587,5 @@ public final class SubCommand extends CommandX {
sender.sendMessage(plugin.api.getLang("SubServers", "Bungee.List.Total").replace("$int$", Integer.toString(players)));
}
}
}
}