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 719731fa..4d916129 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 @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.logging.Logger; import lombok.Getter; import net.md_5.bungee.api.config.ConfigurationAdapter; +import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.plugin.Plugin; @@ -74,13 +75,6 @@ public abstract class ProxyServer */ public abstract Server getServer(String name); - /** - * Return all servers configured as proxy targets. - * - * @return all known target servers - */ - public abstract Collection getServers(); - /** * Get the {@link PluginManager} associated with loading plugins and * dispatching events. It is recommended that implementations use the diff --git a/api/src/main/java/net/md_5/bungee/api/config/ServerInfo.java b/api/src/main/java/net/md_5/bungee/api/config/ServerInfo.java index 65aee741..a53f9f5c 100644 --- a/api/src/main/java/net/md_5/bungee/api/config/ServerInfo.java +++ b/api/src/main/java/net/md_5/bungee/api/config/ServerInfo.java @@ -1,8 +1,13 @@ package net.md_5.bungee.api.config; import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.Synchronized; +import net.md_5.bungee.api.connection.ProxiedPlayer; /** * Class used to represent a server to connect to. @@ -24,4 +29,41 @@ public class ServerInfo * Permission node required to access this server. */ private String permission; + /** + * Players connected to a server defined by these properties. + */ + private final Collection players = new ArrayList<>(); + + /** + * Add a player to the internal set of this server. + * + * @param player the player to add + */ + @Synchronized("players") + public void addPlayer(ProxiedPlayer player) + { + players.add(player); + } + + /** + * Remove a player form the internal set of this server. + * + * @param player the player to remove + */ + @Synchronized("players") + public void removePlayer(ProxiedPlayer player) + { + players.remove(player); + } + + /** + * Get the set of all players on this server. + * + * @return an unmodifiable collection of all players on this server + */ + @Synchronized("players") + public Collection getPlayers() + { + return Collections.unmodifiableCollection(players); + } } diff --git a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java index c61ad882..59515a8b 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java @@ -1,8 +1,7 @@ package net.md_5.bungee.api.connection; -import lombok.Getter; -import lombok.Setter; import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.config.ServerInfo; /** * Represents a player who's connection is being connected to somewhere else, @@ -31,9 +30,9 @@ public interface ProxiedPlayer extends Connection, CommandSender * closing the current one. Depending on the implementation, this method * might return before the user has been connected. * - * @param server the new server to connect to + * @param target the new server to connect to */ - public void connect(Server server); + public void connect(ServerInfo target); /** * Gets the server this player is connected to. diff --git a/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java b/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java index b050d582..2637bc32 100644 --- a/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java @@ -4,8 +4,8 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; +import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.plugin.Event; @Data @@ -22,5 +22,5 @@ public class ServerConnectEvent extends Event /** * Server the player will be connected to. */ - private Server target; + private ServerInfo target; } 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 cff36b5f..24645bf7 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -8,6 +8,7 @@ import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.net.Socket; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Timer; @@ -16,6 +17,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; import java.util.logging.Logger; import lombok.Getter; import lombok.Setter; @@ -24,15 +26,14 @@ import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ReconnectHandler; import net.md_5.bungee.api.TabListHandler; import net.md_5.bungee.api.config.ConfigurationAdapter; +import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.command.*; +import net.md_5.bungee.config.YamlConfig; import net.md_5.bungee.packet.DefinedPacket; -import net.md_5.bungee.tablist.GlobalPingTabList; -import net.md_5.bungee.tablist.GlobalTabList; -import net.md_5.bungee.tablist.ServerUniqueTabList; /** * Main BungeeCord proxy class. @@ -67,7 +68,7 @@ public class BungeeCord extends ProxyServer /** * Server socket listener. */ - private ListenThread listener; + private Collection listeners = new HashSet<>(); /** * Fully qualified connections. */ @@ -87,17 +88,18 @@ public class BungeeCord extends ProxyServer @Getter @Setter private ReconnectHandler reconnectHandler; + @Getter + @Setter + private ConfigurationAdapter configurationAdapter = new YamlConfig(); { - getPluginManager().registerCommand(new CommandReload()); getPluginManager().registerCommand(new CommandReload()); getPluginManager().registerCommand(new CommandEnd()); getPluginManager().registerCommand(new CommandList()); getPluginManager().registerCommand(new CommandServer()); getPluginManager().registerCommand(new CommandIP()); getPluginManager().registerCommand(new CommandAlert()); - getPluginManager().registerCommand(new CommandMotd()); getPluginManager().registerCommand(new CommandBungee()); } @@ -125,7 +127,7 @@ public class BungeeCord extends ProxyServer String line = br.readLine(); if (line != null) { - boolean handled = getInstance().getPluginManager().dispatchCommand(ConsoleCommandSender.instance, line); + boolean handled = getInstance().getPluginManager().dispatchCommand(ConsoleCommandSender.getInstance(), line); if (!handled) { System.err.println("Command not found"); @@ -149,24 +151,15 @@ public class BungeeCord extends ProxyServer plugins.mkdir(); pluginManager.loadPlugins(plugins); - switch (config.tabList) + for (ListenerInfo info : config.getListeners()) { - default: - case 1: - tabListHandler = new GlobalPingTabList(); - break; - case 2: - tabListHandler = new GlobalTabList(); - break; - case 3: - tabListHandler = new ServerUniqueTabList(); - break; + InetSocketAddress addr = info.getHost(); + $().info("Listening on " + addr); + ListenThread listener = new ListenThread(addr); + listener.start(); + listeners.add(listener); } - InetSocketAddress addr = Util.getAddr(config.bindHost); - listener = new ListenThread(addr); - listener.start(); - saveThread.scheduleAtFixedRate(new TimerTask() { @Override @@ -175,18 +168,11 @@ public class BungeeCord extends ProxyServer getReconnectHandler().save(); } }, 0, TimeUnit.MINUTES.toMillis(5)); - $().info("Listening on " + addr); - if (config.metricsEnabled) - { - new Metrics().start(); - } + new Metrics().start(); } - /** - * Destroy this proxy instance cleanly by kicking all users, saving the - * configuration and closing all sockets. - */ + @Override public void stop() { this.isRunning = false; @@ -196,14 +182,17 @@ public class BungeeCord extends ProxyServer plugin.onDisable(); } - $().info("Closing listen thread"); - try + for (ListenThread listener : listeners) { - listener.socket.close(); - listener.join(); - } catch (InterruptedException | IOException ex) - { - $().severe("Could not close listen thread"); + $().log(Level.INFO, "Closing listen thread {0}", listener.socket); + try + { + listener.socket.close(); + listener.join(); + } catch (InterruptedException | IOException ex) + { + $().severe("Could not close listen thread"); + } } $().info("Closing pending connections"); @@ -216,6 +205,7 @@ public class BungeeCord extends ProxyServer } $().info("Saving reconnect locations"); + reconnectHandler.save(); saveThread.cancel(); $().info("Thank you and goodbye"); @@ -231,7 +221,7 @@ public class BungeeCord extends ProxyServer */ public void setSocketOptions(Socket socket) throws IOException { - socket.setSoTimeout(config.timeout); + socket.setSoTimeout(config.getTimeout()); socket.setTrafficClass(0x18); socket.setTcpNoDelay(true); } @@ -283,18 +273,7 @@ public class BungeeCord extends ProxyServer @Override public Server getServer(String name) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public Collection getServers() - { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void setConfigurationAdapter(ConfigurationAdapter adapter) - { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + List users = connectionsByServer.get(name); + return (users != null && !users.isEmpty()) ? users.get(0).getServer() : null; } } 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 aa4a7da9..3e6f3205 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -18,8 +18,8 @@ import java.util.concurrent.ConcurrentLinkedQueue; import lombok.Getter; import lombok.Synchronized; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.event.ChatEvent; import net.md_5.bungee.api.event.PluginMessageEvent; import net.md_5.bungee.api.event.ServerConnectEvent; @@ -56,8 +56,6 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer name = handshake.username; displayName = handshake.username; this.loginPackets = loginPackets; - BungeeCord.instance.connections.put(name, this); - BungeeCord.instance.tabListHandler.onJoin(this); } @Override @@ -68,17 +66,21 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer ProxyServer.getInstance().getTabListHandler().onConnect(this); } - public void connect(Server server) + @Override + public void connect(ServerInfo target) { - ServerConnectEvent event = new ServerConnectEvent(this, server); - BungeeCord.getInstance().getPluginManager().callEvent(event); - InetSocketAddress addr = BungeeCord.instance.config.getServer(event.getNewServer()); - connect(server, addr); - } + if (server == null) + { + // First join + ProxyServer.getInstance().getPlayers().add(this); + ProxyServer.getInstance().getTabListHandler().onConnect(this); + } - private void connect(String name, InetSocketAddress serverAddr) - { - BungeeCord.instance.tabListHandler.onServerChange(this); + ServerConnectEvent event = new ServerConnectEvent(this, target); + BungeeCord.getInstance().getPluginManager().callEvent(event); + target = event.getTarget(); // Update in case the event changed target + + ProxyServer.getInstance().getTabListHandler().onServerChange(this); try { reconnecting = true; @@ -89,9 +91,10 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer out.write(new Packet9Respawn((byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT").getPacket()); } - ServerConnection newServer = ServerConnection.connect(this, name, serverAddr, handshake, true); + ServerConnection newServer = ServerConnection.connect(this, name, target.getAddress(), handshake, true); if (server == null) { + // Once again, first connection clientEntityId = newServer.loginPacket.entityId; serverEntityId = newServer.loginPacket.entityId; out.write(newServer.loginPacket.getPacket()); @@ -113,27 +116,18 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer serverEntityId = login.entityId; out.write(new Packet9Respawn(login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType).getPacket()); } + + // Reconnect process has finished, lets get the player moving again reconnecting = false; - downBridge = new DownstreamBridge(); - if (server != null) - { - List conns = BungeeCord.instance.connectionsByServer.get(server.name); - if (conns != null) - { - conns.remove(this); - } - } + + // Remove from the old by server list + server.getInfo().removePlayer(this); + // Add to new + target.addPlayer(this); + + // Start the bridges and move on server = newServer; - List conns = BungeeCord.instance.connectionsByServer.get(server.name); - if (conns == null) - { - conns = new ArrayList<>(); - BungeeCord.instance.connectionsByServer.put(server.name, conns); - } - if (!conns.contains(this)) - { - conns.add(this); - } + downBridge = new DownstreamBridge(); downBridge.start(); } catch (KickException ex) { @@ -147,18 +141,9 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer private void destroySelf(String reason) { - if (BungeeCord.instance.isRunning) - { - BungeeCord.instance.connections.remove(name); - if (server != null) - { - List conns = BungeeCord.instance.connectionsByServer.get(server.name); - if (conns != null) - { - conns.remove(this); - } - } - } + server.getInfo().removePlayer(this); + ProxyServer.getInstance().getPlayers().remove(this); + disconnect(reason); if (server != null) { @@ -193,14 +178,14 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer } @Override - @Synchronized(value = "permMutex") + @Synchronized("permMutex") public Collection getGroups() { return Collections.unmodifiableCollection(groups); } @Override - @Synchronized(value = "permMutex") + @Synchronized("permMutex") public void addGroups(String... groups) { for (String group : groups) @@ -214,7 +199,7 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer } @Override - @Synchronized(value = "permMutex") + @Synchronized( "permMutex") public void removeGroups(String... groups) { for (String group : groups) @@ -228,7 +213,7 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer } @Override - @Synchronized(value = "permMutex") + @Synchronized("permMutex") public boolean hasPermission(String permission) { Boolean val = permissions.get(permission); @@ -236,7 +221,7 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer } @Override - @Synchronized(value = "permMutex") + @Synchronized( "permMutex") public void setPermission(String permission, boolean value) { permissions.put(permission, value); @@ -387,7 +372,7 @@ public class UserConnection extends GenericConnection implements ProxiedPlayer in.readFully(data); break; case "BungeeCord::Connect": - Server server = ProxyServer.getInstance().getServer(in.readUTF()); + ServerInfo server = ProxyServer.getInstance().getConfigurationAdapter().getServers().get(in.readUTF()); if (server != null) { connect(server); diff --git a/proxy/src/main/java/net/md_5/bungee/command/CommandServer.java b/proxy/src/main/java/net/md_5/bungee/command/CommandServer.java index 92f01c99..c3b36db8 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/CommandServer.java +++ b/proxy/src/main/java/net/md_5/bungee/command/CommandServer.java @@ -1,9 +1,10 @@ package net.md_5.bungee.command; -import java.util.Collection; +import java.util.Map; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.plugin.Command; @@ -27,20 +28,20 @@ public class CommandServer extends Command return; } ProxiedPlayer player = (ProxiedPlayer) sender; - Collection servers = ProxyServer.getInstance().getServers(); + Map servers = ProxyServer.getInstance().getConfigurationAdapter().getServers(); if (args.length == 0) { StringBuilder serverList = new StringBuilder(); - for (Server server : servers) + for (String server : servers.keySet()) { - serverList.append(server.getInfo().getName()); + serverList.append(server); serverList.append(", "); } serverList.setLength(serverList.length() - 2); player.sendMessage(ChatColor.GOLD + "You may connect to the following servers at this time: " + serverList.toString()); } else { - Server server = ProxyServer.getInstance().getServer(args[0]); + ServerInfo server = servers.get(args[0]); if (server == null) { player.sendMessage(ChatColor.RED + "The specified server does not exist");