From 5db60dbe715d4f186caee02e5cbbba4df5b23d90 Mon Sep 17 00:00:00 2001 From: Troy Frew Date: Wed, 29 Jun 2016 04:29:25 +0200 Subject: [PATCH] Add dynamic server addition/removal api. The provided methods will not move a player if a server is removed or the server ip/port is changed, however the methods return the old ServerInfo object for you to handle that yourself if needed. Thanks to Overcast for the idea diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java index b6845bc4..edbae4ea 100644 --- a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java +++ b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java @@ -37,11 +37,95 @@ public interface ProxyConfig /** * Set of all servers. - * * @return servers + * @deprecated The returned map may be modified concurrently by the proxy. + * The safe alternative is {@link #getServersCopy()}. */ + @Deprecated // Waterfall Map getServers(); + // Waterfall start - Dynamic server addition/removal api + /** + * Return all servers registered to this proxy, keyed by name. The returned map + * is an immutable snapshot of the actual server collection. It cannot be modified, + * and it will not change. + * + * @return all registered remote server destinations + */ + Map getServersCopy(); + + /** + * Gets the server info of a server. + * + * @param name the name of the configured server + * @return the server info belonging to the specified server + */ + ServerInfo getServerInfo(String name); + + /** + * Register the given server to the proxy. + * Any currently registered server with the same name will be replaced. + * This change is not saved to config.yml + * + * @param server The server to register with the proxy + * + * @return the previously registered server with the same name, or null if there was no such server. + */ + ServerInfo addServer(ServerInfo server); + + /** + * Register all of the given servers to the proxy. + * This change is not saved to config.yml + * + * @param servers The collection of servers to register with the proxy + * + * @return true if any servers were added or replaced. + */ + boolean addServers(Collection servers); + + /** + * Un-register the server with the given name from the proxy. + * This change is not saved to config.yml + * + * @param name The name of the server to unregister + * + * @return the server that was removed, or null if there is no server with the given name. + */ + ServerInfo removeServerNamed(String name); + + /** + * Un-register the given server from the proxy. + * The server is matched by name only, other fields in the given {@link ServerInfo} are ignored. + * This change is not saved to config.yml + * + * @param server the server to unregister from the proxy + * + * @return the server that was removed, or null if there is no server with a matching name. + */ + ServerInfo removeServer(ServerInfo server); + + /** + * Un-register servers with any of the given names from the proxy. + * This change is not saved to config.yml + * + * @param names a collection of server names to be unregistered + * + * @return true if any servers were removed. + */ + boolean removeServersNamed(Collection names); + + /** + * Un-register all of the given servers from the proxy. + * The servers are matched by name only, other fields in the given {@link ServerInfo} are ignored. + * This change is not saved to config.yml + * + * @param servers a collection of servers to be unregistered + * + * @return true if any servers were removed. + */ + boolean removeServers(Collection servers); + // Waterfall end + /** * Does the server authenticate with Mojang. * diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java index 1f330bd2..a4011335 100644 --- a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java +++ b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java @@ -96,9 +96,25 @@ public abstract class ProxyServer * return a fresh map each time. * * @return all registered remote server destinations + * + * @deprecated The returned map is part of the proxy's internal state, + * and may be modified concurrently by the proxy. + * The safe alternative is {@link #getServersCopy()}. */ + @Deprecated // Waterfall public abstract Map getServers(); + // Waterfall begin - Cloned servers map + /** + * Return all servers registered to this proxy, keyed by name. The returned map + * is an immutable snapshot of the actual server collection. It cannot be modified, + * and it will not change. + * + * @return all registered remote server destinations + */ + public abstract Map getServersCopy(); + // Waterfall end + /** * Gets the server info of a server. * diff --git a/config/src/main/java/net/md_5/bungee/config/Configuration.java b/config/src/main/java/net/md_5/bungee/config/Configuration.java index 262b29c8..d7ed3e11 100644 --- a/config/src/main/java/net/md_5/bungee/config/Configuration.java +++ b/config/src/main/java/net/md_5/bungee/config/Configuration.java @@ -44,6 +44,13 @@ public final class Configuration } } + // Waterfall start - Allow configuration objects to be cloned + public Configuration(Configuration values, Configuration defaults) + { + this( values.self, defaults ); + } + // Waterfall end + private Configuration getSectionFor(String path) { int index = path.indexOf( SEPARATOR ); diff --git a/module/cmd-server/src/main/java/net/md_5/bungee/module/cmd/server/CommandServer.java b/module/cmd-server/src/main/java/net/md_5/bungee/module/cmd/server/CommandServer.java index 59c104d3..698b420f 100644 --- a/module/cmd-server/src/main/java/net/md_5/bungee/module/cmd/server/CommandServer.java +++ b/module/cmd-server/src/main/java/net/md_5/bungee/module/cmd/server/CommandServer.java @@ -83,7 +83,7 @@ public class CommandServer extends Command implements TabExecutor @Override public Iterable onTabComplete(final CommandSender sender, final String[] args) { - return ( args.length > 1 ) ? Collections.EMPTY_LIST : Iterables.transform( Iterables.filter( ProxyServer.getInstance().getServers().values(), new Predicate() + return ( args.length > 1 ) ? Collections.EMPTY_LIST : Iterables.transform( Iterables.filter( ProxyServer.getInstance().getServersCopy().values(), new Predicate() // Waterfall: use #getServersCopy() { private final String lower = ( args.length == 0 ) ? "" : args[0].toLowerCase( Locale.ROOT ); 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 93a96bfd..5af9c3bb 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -664,10 +664,18 @@ public class BungeeCord extends ProxyServer return config.getServers(); } + // Waterfall start + @Override + public Map getServersCopy() + { + return config.getServersCopy(); + } + // Waterfall end + @Override public ServerInfo getServerInfo(String name) { - return getServers().get( name ); + return config.getServerInfo( name ); // Waterfall } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java index ecd3b619..7094fe0b 100644 --- a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java +++ b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java @@ -1,6 +1,7 @@ package net.md_5.bungee.conf; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; // Waterfall import gnu.trove.map.TMap; import java.io.File; import java.io.IOException; @@ -11,6 +12,7 @@ import java.util.UUID; import java.util.logging.Level; import javax.imageio.ImageIO; import lombok.Getter; +import lombok.Synchronized; // Waterfall import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.Favicon; @@ -42,6 +44,7 @@ public abstract class Configuration implements ProxyConfig * Set of all listeners. */ private Collection listeners; + private final Object serversLock = new Object(); // Waterfall /** * Set of all servers. */ @@ -72,6 +75,7 @@ public abstract class Configuration implements ProxyConfig private boolean preventProxyConnections; private boolean forgeSupport; + @Synchronized("serversLock") // Waterfall public void load() { ConfigurationAdapter adapter = ProxyServer.getInstance().getConfigurationAdapter(); @@ -119,7 +123,7 @@ public abstract class Configuration implements ProxyConfig servers = new CaseInsensitiveMap<>( newServers ); } else { - Map oldServers = this.servers; + Map oldServers = getServersCopy(); for ( ServerInfo oldServer : oldServers.values() ) { @@ -182,4 +186,71 @@ public abstract class Configuration implements ProxyConfig { return favicon; } + + // Waterfall start + @Override + @Synchronized("serversLock") + public Map getServersCopy() { + return ImmutableMap.copyOf( servers ); + } + + @Override + @Synchronized("serversLock") + public ServerInfo getServerInfo(String name) + { + return this.servers.get( name ); + } + + @Override + @Synchronized("serversLock") + public ServerInfo addServer(ServerInfo server) + { + return this.servers.put( server.getName(), server ); + } + + @Override + @Synchronized("serversLock") + public boolean addServers(Collection servers) + { + boolean changed = false; + for ( ServerInfo server : servers ) + { + if ( server != this.servers.put( server.getName(), server ) ) changed = true; + } + return changed; + } + + @Override + @Synchronized("serversLock") + public ServerInfo removeServerNamed(String name) + { + return this.servers.remove( name ); + } + + @Override + @Synchronized("serversLock") + public ServerInfo removeServer(ServerInfo server) + { + return this.servers.remove( server.getName() ); + } + + @Override + @Synchronized("serversLock") + public boolean removeServersNamed(Collection names) + { + return this.servers.keySet().removeAll( names ); + } + + @Override + @Synchronized("serversLock") + public boolean removeServers(Collection servers) + { + boolean changed = false; + for ( ServerInfo server : servers ) + { + if ( null != this.servers.remove( server.getName() ) ) changed = true; + } + return changed; + } + // Waterfall end } -- 2.42.0