diff --git a/api/src/main/java/net/md_5/bungee/api/config/ConfigurationAdapter.java b/api/src/main/java/net/md_5/bungee/api/config/ConfigurationAdapter.java index 7e5981d5..54f35220 100644 --- a/api/src/main/java/net/md_5/bungee/api/config/ConfigurationAdapter.java +++ b/api/src/main/java/net/md_5/bungee/api/config/ConfigurationAdapter.java @@ -1,6 +1,7 @@ package net.md_5.bungee.api.config; import java.util.Collection; +import java.util.Map; /** * This class allows plugins to set their own configuration adapter to load @@ -13,32 +14,26 @@ public interface ConfigurationAdapter * Gets an integer from the specified path. * * @param path the path to retrieve the integer from + * @param def the default value * @return the retrieved integer */ - public int getInt(String path); + public int getInt(String path, int def); /** * Gets a string from the specified path. * * @param path the path to retrieve the string from. + * @param def the default value * @return the retrieved string */ - public String getString(String path); - - /** - * Get a string list from the specified path. - * - * @param path the path to retrieve the list from. - * @return the retrieved list. - */ - public Collection getStrings(String path); + public String getString(String path, String def); /** * Get the configuration all servers which may be accessible via the proxy. * - * @return all accessible servers + * @return all accessible servers, keyed by name */ - public Collection getServers(); + public Map getServers(); /** * Get information about all hosts to bind the proxy to. diff --git a/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java b/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java index 84765ad7..150a79f3 100644 --- a/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java +++ b/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java @@ -23,4 +23,13 @@ public class ListenerInfo * Max amount of slots displayed on the ping page. */ private final int maxPlayers; + /** + * Name of the server which users will be taken to by default. + */ + private final String defaultServer; + /** + * Whether reconnect locations will be used, or else the user is simply + * transferred to the default server on connect. + */ + private final boolean forceDefault; } diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index c8fde24b..cff36b5f 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -1,5 +1,6 @@ package net.md_5.bungee; +import net.md_5.bungee.config.Configuration; import java.io.BufferedReader; import java.io.File; import java.io.IOException; diff --git a/proxy/src/main/java/net/md_5/bungee/Configuration.java b/proxy/src/main/java/net/md_5/bungee/Configuration.java deleted file mode 100644 index 92ca0ebd..00000000 --- a/proxy/src/main/java/net/md_5/bungee/Configuration.java +++ /dev/null @@ -1,294 +0,0 @@ -package net.md_5.bungee; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import static net.md_5.bungee.Logger.$; -import net.md_5.bungee.api.ChatColor; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.Yaml; - -/** - * Core configuration for the proxy. - */ -public class Configuration -{ - - /** - * Reconnect locations file. - */ - private transient File reconnect = new File("locations.yml"); - /** - * Loaded reconnect locations. - */ - private transient Map reconnectLocations; - /** - * Config file. - */ - private transient File file = new File("config.yml"); - /** - * Yaml instance. - */ - private transient Yaml yaml; - /** - * Loaded config. - */ - private transient Map config; - /** - * Bind host. - */ - public String bindHost = "0.0.0.0:25577"; - /** - * Server ping motd. - */ - public String motd = "BungeeCord Proxy Instance"; - /** - * Name of default server. - */ - public String defaultServerName = "default"; - /** - * Max players as displayed in list ping. Soft limit. - */ - public int maxPlayers = 1; - /** - * Tab list 1: For a tab list that is global over all server (using their - * Minecraft name) and updating their ping frequently 2: Same as 1 but does - * not update their ping frequently, just once, 3: Makes the individual - * servers handle the tab list (server unique). - */ - public int tabList = 1; - /** - * Socket timeout. - */ - public int timeout = 15000; - /** - * All servers. - */ - public Map servers = new HashMap() - { - - { - put(defaultServerName, "127.0.0.1:1338"); - put("pvp", "127.0.0.1:1337"); - } - }; - /** - * Forced servers. - */ - public Map forcedServers = new HashMap() - { - - { - put("pvp.md-5.net", "pvp"); - } - }; - /** - * Proxy admins. - */ - public List admins = new ArrayList() - { - - { - add("Insert Admins Here"); - } - }; - /** - * Proxy moderators. - */ - public List moderators = new ArrayList() - { - - { - add("Insert Moderators Here"); - } - }; - /** - * Commands which will be blocked completely. - */ - public List disabledCommands = new ArrayList() - { - - { - add("glist"); - } - }; - /** - * Maximum number of lines to log before old ones are removed. - */ - public int logNumLines = 1 << 14; - /** - * UUID for Metrics. - */ - public String statsUuid = UUID.randomUUID().toString(); - public boolean metricsEnabled = true; - public boolean forceDefaultServer = false; - - /** - * Load the configuration and save default values. - */ - public void load() - { - try - { - file.createNewFile(); - DumperOptions options = new DumperOptions(); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - yaml = new Yaml(options); - - try (InputStream is = new FileInputStream(file)) - { - config = (Map) yaml.load(is); - } - - if (config == null) - { - config = new LinkedHashMap<>(); - } - - $().info("-------------- Loading configuration ----------------"); - for (Field field : getClass().getDeclaredFields()) - { - if (!Modifier.isTransient(field.getModifiers())) - { - String name = Util.normalize(field.getName()); - try - { - Object def = field.get(this); - Object value = get(name, def); - - field.set(this, value); - - $().info(name + ": " + value); - } catch (IllegalAccessException ex) - { - $().severe("Could not get config node: " + name); - } - } - } - $().info("-----------------------------------------------------"); - - if (servers.get(defaultServerName) == null) - { - throw new IllegalArgumentException("Server '" + defaultServerName + "' not defined"); - } - - if (forcedServers != null) - { - for (String server : forcedServers.values()) - { - if (!servers.containsKey(server)) - { - throw new IllegalArgumentException("Forced server " + server + " is not defined in servers"); - } - } - } - - motd = ChatColor.translateAlternateColorCodes('&', motd); - - reconnect.createNewFile(); - try (FileInputStream recon = new FileInputStream(reconnect)) - { - reconnectLocations = (Map) yaml.load(recon); - } - if (reconnectLocations == null) - { - reconnectLocations = new LinkedHashMap<>(); - } - - } catch (IOException ex) - { - $().severe("Could not load config!"); - ex.printStackTrace(); - } - } - - private T get(String path, T def) - { - if (!config.containsKey(path)) - { - config.put(path, def); - save(file, config); - } - return (T) config.get(path); - } - - private void save(File fileToSave, Map toSave) - { - try - { - try (FileWriter wr = new FileWriter(fileToSave)) - { - yaml.dump(toSave, wr); - } - } catch (IOException ex) - { - $().severe("Could not save config file " + fileToSave); - ex.printStackTrace(); - } - } - - /** - * Get which server a user should be connected to, taking into account their - * name and virtual host. - * - * @param user to get a server for - * @param requestedHost the host which they connected to - * @return the name of the server which they should be connected to. - */ - public String getServer(String user, String requestedHost) - { - String server = (forcedServers == null) ? null : forcedServers.get(requestedHost); - if (server == null) - { - server = reconnectLocations.get(user); - } - if (server == null) - { - server = defaultServerName; - } - return server; - } - - /** - * Save the last server which the user was on. - * - * @param user the name of the user - * @param server which they were last on - */ - public void setServer(UserConnection user, String server) - { - reconnectLocations.put(user.name, server); - } - - /** - * Gets the connectable address of a server defined in the configuration. - * - * @param name the friendly name of a server - * @return the usable {@link InetSocketAddress} mapped to this server - */ - public InetSocketAddress getServer(String name) - { - String server = servers.get((name == null) ? defaultServerName : name); - return (server != null) ? Util.getAddr(server) : getServer(null); - } - - /** - * Save the current mappings of users to servers. - */ - public void saveHosts() - { - save(reconnect, reconnectLocations); - $().info("Saved reconnect locations to " + reconnect); - } -} diff --git a/proxy/src/main/java/net/md_5/bungee/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/InitialHandler.java index 8211eeda..d1c03d92 100644 --- a/proxy/src/main/java/net/md_5/bungee/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/InitialHandler.java @@ -1,5 +1,6 @@ package net.md_5.bungee; +import net.md_5.bungee.config.Configuration; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; diff --git a/proxy/src/main/java/net/md_5/bungee/Logger.java b/proxy/src/main/java/net/md_5/bungee/Logger.java index 63964fe1..5f570159 100644 --- a/proxy/src/main/java/net/md_5/bungee/Logger.java +++ b/proxy/src/main/java/net/md_5/bungee/Logger.java @@ -23,7 +23,7 @@ public class Logger extends java.util.logging.Logger super("RubberBand", null); try { - FileHandler handler = new FileHandler("proxy.log", BungeeCord.getInstance().config.logNumLines, 1, true); + FileHandler handler = new FileHandler("proxy.log", 1 << 14, 1, true); handler.setFormatter(formatter); addHandler(handler); } catch (IOException ex) diff --git a/proxy/src/main/java/net/md_5/bungee/Metrics.java b/proxy/src/main/java/net/md_5/bungee/Metrics.java index 2be00b5a..a770a093 100644 --- a/proxy/src/main/java/net/md_5/bungee/Metrics.java +++ b/proxy/src/main/java/net/md_5/bungee/Metrics.java @@ -74,7 +74,7 @@ public class Metrics extends Thread { // Construct the post data final StringBuilder data = new StringBuilder(); - data.append(encode("guid")).append('=').append(encode(BungeeCord.getInstance().config.statsUuid)); + data.append(encode("guid")).append('=').append(encode(BungeeCord.getInstance().config.getUuid())); encodeDataPair(data, "version", ProxyServer.getInstance().getVersion()); encodeDataPair(data, "server", "0"); encodeDataPair(data, "players", Integer.toString(ProxyServer.getInstance().getPlayers().size())); diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java index 8e1ff036..aa4a7da9 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -47,6 +47,7 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer // Permissions private final Collection groups = new HashSet<>(); private final Map permissions = new HashMap<>(); + private final Object permMutex = new Object(); public UserConnection(Socket socket, PacketInputStream in, OutputStream out, Packet2Handshake handshake, List loginPackets) { diff --git a/proxy/src/main/java/net/md_5/bungee/command/CommandMotd.java b/proxy/src/main/java/net/md_5/bungee/command/CommandMotd.java deleted file mode 100644 index a253f185..00000000 --- a/proxy/src/main/java/net/md_5/bungee/command/CommandMotd.java +++ /dev/null @@ -1,31 +0,0 @@ -package net.md_5.bungee.command; - -import net.md_5.bungee.BungeeCord; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.plugin.Command; - -/** - * Command to set a temp copy of the motd in real-time without stopping the - * proxy. - */ -public class CommandMotd extends Command -{ - - public CommandMotd() - { - super("bungeecord.command.motd"); - } - - @Override - public void execute(CommandSender sender, String[] args) - { - StringBuilder newMOTD = new StringBuilder(); - for (String s : args) - { - newMOTD.append(s); - newMOTD.append(" "); - } - BungeeCord.getInstance().config.motd = ChatColor.translateAlternateColorCodes('&', newMOTD.substring(0, newMOTD.length() - 1)); - } -} diff --git a/proxy/src/main/java/net/md_5/bungee/config/Configuration.java b/proxy/src/main/java/net/md_5/bungee/config/Configuration.java new file mode 100644 index 00000000..0e6c73e1 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/config/Configuration.java @@ -0,0 +1,84 @@ +package net.md_5.bungee.config; + +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Map; +import java.util.UUID; +import lombok.Getter; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ConfigurationAdapter; +import net.md_5.bungee.api.config.ListenerInfo; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.tablist.GlobalPingTabList; +import net.md_5.bungee.tablist.GlobalTabList; +import net.md_5.bungee.tablist.ServerUniqueTabList; + +/** + * Core configuration for the proxy. + */ +@Getter +public class Configuration +{ + + /** + * The default tab list options available for picking. + */ + private enum DefaultTabList + { + + GLOBAL, GLOBAL_PING, SERVER; + } + /** + * Time before users are disconnected due to no network activity. + */ + private int timeout = 30000; + /** + * UUID used for metrics. + */ + private String uuid = UUID.randomUUID().toString(); + /** + * Set of all listeners. + */ + private Collection listeners; + /** + * Set of all servers. + */ + private Map servers; + + public void load() + { + ConfigurationAdapter adapter = ProxyServer.getInstance().getConfigurationAdapter(); + + timeout = adapter.getInt("timeout", timeout); + uuid = adapter.getString("stats", uuid); + + DefaultTabList tab = DefaultTabList.valueOf(adapter.getString("tab_list", "GLOBAL_PING")); + if (tab == null) + { + tab = DefaultTabList.GLOBAL_PING; + } + switch (tab) + { + case GLOBAL: + ProxyServer.getInstance().setTabListHandler(new GlobalTabList()); + break; + case GLOBAL_PING: + ProxyServer.getInstance().setTabListHandler(new GlobalPingTabList()); + break; + case SERVER: + ProxyServer.getInstance().setTabListHandler(new ServerUniqueTabList()); + break; + } + + listeners = adapter.getListeners(); + Preconditions.checkArgument(listeners != null && !listeners.isEmpty(), "No listeners defined."); + + servers = adapter.getServers(); + Preconditions.checkArgument(servers != null && !servers.isEmpty(), "No servers defined"); + + for (ListenerInfo listener : listeners) + { + Preconditions.checkArgument(servers.containsKey(listener.getDefaultServer())); + } + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/config/YamlConfig.java b/proxy/src/main/java/net/md_5/bungee/config/YamlConfig.java new file mode 100644 index 00000000..b9cfe567 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/config/YamlConfig.java @@ -0,0 +1,150 @@ +package net.md_5.bungee.config; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import net.md_5.bungee.Util; +import net.md_5.bungee.api.config.ConfigurationAdapter; +import net.md_5.bungee.api.config.ListenerInfo; +import net.md_5.bungee.api.config.ServerInfo; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +public class YamlConfig implements ConfigurationAdapter +{ + + private boolean loaded; + private Yaml yaml; + private Map config; + + public void load() + { + try + { + File file = new File("config.yml"); + file.createNewFile(); + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + yaml = new Yaml(options); + + try (InputStream is = new FileInputStream(file)) + { + config = (Map) yaml.load(is); + } + + if (config == null) + { + config = new LinkedHashMap<>(); + } + + loaded = true; + } catch (IOException ex) + { + throw new RuntimeException("Could not load configuration!", ex); + } + } + + private T get(String path, T def) + { + return get(path, def, config); + } + + private T get(String path, T def, Map submap) + { + if (!loaded) + { + load(); + } + + int index = path.indexOf('.'); + if (index == -1) + { + Object val = submap.get(path); + return (val != null) ? (T) val : def; + } else + { + String first = path.substring(0, index); + String second = path.substring(index, path.length()); + Map sub = (Map) submap.get(first); + return (sub != null) ? get(second, def, sub) : def; + } + } + + @Override + public int getInt(String path, int def) + { + return get(path, def); + } + + @Override + public String getString(String path, String def) + { + return get(path, def); + } + + @Override + @SuppressWarnings("unchecked") + public Map getServers() + { + Map> base = get("servers", Collections.EMPTY_MAP); + Map ret = new HashMap<>(); + + for (Map.Entry> entry : base.entrySet()) + { + Map val = entry.getValue(); + String name = get("name", null, val); + String permission = get("permission", null, val); + String addr = get("address", null, val); + InetSocketAddress address = Util.getAddr(addr); + ServerInfo info = new ServerInfo(name, address, permission); + ret.put(name, info); + } + + return ret; + } + + @Override + @SuppressWarnings("unchecked") + public Collection getListeners() + { + Map> base = get("listeners", Collections.EMPTY_MAP); + Collection ret = new HashSet<>(); + + for (Map.Entry> entry : base.entrySet()) + { + Map val = entry.getValue(); + String motd = get("motd", null, val); + int maxPlayers = get("motd", null, val); + String defaultServer = get("default", null, val); + boolean forceDefault = get("force_default", null, val); + String host = get("host", null, val); + InetSocketAddress address = Util.getAddr(host); + ListenerInfo info = new ListenerInfo(address, motd, maxPlayers, defaultServer, forceDefault); + ret.add(info); + } + + return ret; + } + + @Override + @SuppressWarnings("unchecked") + public Collection getGroups(String player) + { + return get("groups." + player, Collections.EMPTY_SET); + } + + @Override + @SuppressWarnings("unchecked") + public Collection getPermissions(String group) + { + return get("permissions." + group, Collections.EMPTY_SET); + } +}