Add the start of a config loader.

This commit is contained in:
md_5 2013-01-19 10:13:55 +11:00
parent 4ba6993039
commit 8bff34b8b6
11 changed files with 255 additions and 339 deletions

View File

@ -1,6 +1,7 @@
package net.md_5.bungee.api.config; package net.md_5.bungee.api.config;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
/** /**
* This class allows plugins to set their own configuration adapter to load * 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. * Gets an integer from the specified path.
* *
* @param path the path to retrieve the integer from * @param path the path to retrieve the integer from
* @param def the default value
* @return the retrieved integer * @return the retrieved integer
*/ */
public int getInt(String path); public int getInt(String path, int def);
/** /**
* Gets a string from the specified path. * Gets a string from the specified path.
* *
* @param path the path to retrieve the string from. * @param path the path to retrieve the string from.
* @param def the default value
* @return the retrieved string * @return the retrieved string
*/ */
public String getString(String path); public String getString(String path, String def);
/**
* Get a string list from the specified path.
*
* @param path the path to retrieve the list from.
* @return the retrieved list.
*/
public Collection<String> getStrings(String path);
/** /**
* Get the configuration all servers which may be accessible via the proxy. * 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<ServerInfo> getServers(); public Map<String, ServerInfo> getServers();
/** /**
* Get information about all hosts to bind the proxy to. * Get information about all hosts to bind the proxy to.

View File

@ -23,4 +23,13 @@ public class ListenerInfo
* Max amount of slots displayed on the ping page. * Max amount of slots displayed on the ping page.
*/ */
private final int maxPlayers; 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;
} }

View File

@ -1,5 +1,6 @@
package net.md_5.bungee; package net.md_5.bungee;
import net.md_5.bungee.config.Configuration;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;

View File

@ -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<String, String> reconnectLocations;
/**
* Config file.
*/
private transient File file = new File("config.yml");
/**
* Yaml instance.
*/
private transient Yaml yaml;
/**
* Loaded config.
*/
private transient Map<String, Object> 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<String, String> servers = new HashMap<String, String>()
{
{
put(defaultServerName, "127.0.0.1:1338");
put("pvp", "127.0.0.1:1337");
}
};
/**
* Forced servers.
*/
public Map<String, String> forcedServers = new HashMap<String, String>()
{
{
put("pvp.md-5.net", "pvp");
}
};
/**
* Proxy admins.
*/
public List<String> admins = new ArrayList<String>()
{
{
add("Insert Admins Here");
}
};
/**
* Proxy moderators.
*/
public List<String> moderators = new ArrayList<String>()
{
{
add("Insert Moderators Here");
}
};
/**
* Commands which will be blocked completely.
*/
public List<String> disabledCommands = new ArrayList<String>()
{
{
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> 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);
}
}

View File

@ -1,5 +1,6 @@
package net.md_5.bungee; package net.md_5.bungee;
import net.md_5.bungee.config.Configuration;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;

View File

@ -23,7 +23,7 @@ public class Logger extends java.util.logging.Logger
super("RubberBand", null); super("RubberBand", null);
try 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); handler.setFormatter(formatter);
addHandler(handler); addHandler(handler);
} catch (IOException ex) } catch (IOException ex)

View File

@ -74,7 +74,7 @@ public class Metrics extends Thread
{ {
// Construct the post data // Construct the post data
final StringBuilder data = new StringBuilder(); 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, "version", ProxyServer.getInstance().getVersion());
encodeDataPair(data, "server", "0"); encodeDataPair(data, "server", "0");
encodeDataPair(data, "players", Integer.toString(ProxyServer.getInstance().getPlayers().size())); encodeDataPair(data, "players", Integer.toString(ProxyServer.getInstance().getPlayers().size()));

View File

@ -47,6 +47,7 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer
// Permissions // Permissions
private final Collection<String> groups = new HashSet<>(); private final Collection<String> groups = new HashSet<>();
private final Map<String, Boolean> permissions = new HashMap<>(); private final Map<String, Boolean> permissions = new HashMap<>();
private final Object permMutex = new Object();
public UserConnection(Socket socket, PacketInputStream in, OutputStream out, Packet2Handshake handshake, List<byte[]> loginPackets) public UserConnection(Socket socket, PacketInputStream in, OutputStream out, Packet2Handshake handshake, List<byte[]> loginPackets)
{ {

View File

@ -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));
}
}

View File

@ -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<ListenerInfo> listeners;
/**
* Set of all servers.
*/
private Map<String, ServerInfo> 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()));
}
}
}

View File

@ -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> T get(String path, T def)
{
return get(path, def, config);
}
private <T> 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<String, ServerInfo> getServers()
{
Map<String, Map<String, Object>> base = get("servers", Collections.EMPTY_MAP);
Map<String, ServerInfo> ret = new HashMap<>();
for (Map.Entry<String, Map<String, Object>> entry : base.entrySet())
{
Map<String, Object> 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<ListenerInfo> getListeners()
{
Map<String, Map<String, Object>> base = get("listeners", Collections.EMPTY_MAP);
Collection<ListenerInfo> ret = new HashSet<>();
for (Map.Entry<String, Map<String, Object>> entry : base.entrySet())
{
Map<String, Object> 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<String> getGroups(String player)
{
return get("groups." + player, Collections.EMPTY_SET);
}
@Override
@SuppressWarnings("unchecked")
public Collection<String> getPermissions(String group)
{
return get("permissions." + group, Collections.EMPTY_SET);
}
}