diff --git a/api/src/main/java/net/md_5/bungee/api/ServerConnectRequest.java b/api/src/main/java/net/md_5/bungee/api/ServerConnectRequest.java new file mode 100644 index 000000000..be0b5ec02 --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/ServerConnectRequest.java @@ -0,0 +1,101 @@ +package net.md_5.bungee.api; + +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.event.ServerConnectEvent; + +/** + * A request to connect a server. + */ +@Getter +@Builder +public class ServerConnectRequest +{ + + /** + * The result from this callback after request has been executed by proxy. + */ + public enum Result + { + + /** + * ServerConnectEvent to the new server was canceled. + */ + EVENT_CANCEL, + /** + * Already connected to target server. + */ + ALREADY_CONNECTED, + /** + * Already connecting to target server. + */ + ALREADY_CONNECTING, + /** + * Successfully connected to server. + */ + SUCCESS, + /** + * Connection failed, error can be accessed from callback method handle. + */ + FAIL + } + + /** + * Target server to connect to. + */ + @NonNull + private final ServerInfo target; + /** + * Reason for connecting to server. + */ + @NonNull + private final ServerConnectEvent.Reason reason; + /** + * Callback to execute post request. + */ + private final Callback result; + /** + * Timeout in milliseconds for request. + */ + private final int connectTimeout; + /** + * Should the player be attempted to connect to the next server in their + * queue if the initial request fails. + */ + private final boolean retry; + + /** + * Class that sets default properties/adds methods to the lombok builder + * generated class. + */ + public static class ServerConnectRequestBuilder + { + + private Callback result; + private int connectTimeout = 5000; // TODO: Configurable + + /** + * Sets the callback to execute on explicit succession of the request. + * + * @param callback the callback to execute + * @return this builder for chaining + * @deprecated recommended to use callback providing generic type of + * {@link Result} + */ + @Deprecated + public ServerConnectRequestBuilder callback(final Callback callback) + { + this.result = new Callback() + { + @Override + public void done(final Result result, final Throwable error) + { + callback.done( ( result == Result.SUCCESS ) ? Boolean.TRUE : Boolean.FALSE, error ); + } + }; + return this; + } + } +} 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 333d39729..e3e999b0d 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 @@ -6,6 +6,7 @@ import java.util.UUID; import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ServerConnectRequest; import net.md_5.bungee.api.SkinConfiguration; import net.md_5.bungee.api.Title; import net.md_5.bungee.api.chat.BaseComponent; @@ -123,6 +124,15 @@ public interface ProxiedPlayer extends Connection, CommandSender */ void connect(ServerInfo target, Callback callback, ServerConnectEvent.Reason reason); + /** + * Connects / transfers this user to the specified connection, gracefully + * closing the current one. Depending on the implementation, this method + * might return before the user has been connected. + * + * @param request request to connect with + */ + void connect(ServerConnectRequest request); + /** * Gets the server this player is connected to. * 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 dfa4b71b8..8f84aa173 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -27,6 +27,7 @@ import lombok.Setter; import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.ServerConnectRequest; import net.md_5.bungee.api.SkinConfiguration; import net.md_5.bungee.api.Title; import net.md_5.bungee.api.chat.BaseComponent; @@ -253,12 +254,21 @@ public final class UserConnection implements ProxiedPlayer { Preconditions.checkNotNull( info, "info" ); - ServerConnectEvent event = new ServerConnectEvent( this, info, reason ); + connect( ServerConnectRequest.builder().callback( callback ).retry( retry ).reason( reason ).target( info ).build() ); + } + + @Override + public void connect(final ServerConnectRequest request) + { + Preconditions.checkNotNull( request, "request" ); + + final Callback callback = request.getResult(); + ServerConnectEvent event = new ServerConnectEvent( this, request.getTarget(), request.getReason() ); if ( bungee.getPluginManager().callEvent( event ).isCancelled() ) { if ( callback != null ) { - callback.done( false, null ); + callback.done( ServerConnectRequest.Result.EVENT_CANCEL, null ); } if ( getServer() == null && !ch.isClosing() ) @@ -274,7 +284,7 @@ public final class UserConnection implements ProxiedPlayer { if ( callback != null ) { - callback.done( false, null ); + callback.done( ServerConnectRequest.Result.ALREADY_CONNECTED, null ); } sendMessage( bungee.getTranslation( "already_connected" ) ); @@ -284,7 +294,7 @@ public final class UserConnection implements ProxiedPlayer { if ( callback != null ) { - callback.done( false, null ); + callback.done( ServerConnectRequest.Result.ALREADY_CONNECTING, null ); } sendMessage( bungee.getTranslation( "already_connecting" ) ); @@ -312,7 +322,7 @@ public final class UserConnection implements ProxiedPlayer { if ( callback != null ) { - callback.done( future.isSuccess(), future.cause() ); + callback.done( ( future.isSuccess() ) ? ServerConnectRequest.Result.SUCCESS : ServerConnectRequest.Result.FAIL, future.cause() ); } if ( !future.isSuccess() ) @@ -321,7 +331,7 @@ public final class UserConnection implements ProxiedPlayer pendingConnects.remove( target ); ServerInfo def = updateAndGetNextServer( target ); - if ( retry && def != null && ( getServer() == null || def != getServer().getInfo() ) ) + if ( request.isRetry() && def != null && ( getServer() == null || def != getServer().getInfo() ) ) { sendMessage( bungee.getTranslation( "fallback_lobby" ) ); connect( def, null, true, ServerConnectEvent.Reason.LOBBY_FALLBACK ); @@ -339,7 +349,7 @@ public final class UserConnection implements ProxiedPlayer .channel( PipelineUtils.getChannel() ) .group( ch.getHandle().eventLoop() ) .handler( initializer ) - .option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable + .option( ChannelOption.CONNECT_TIMEOUT_MILLIS, request.getConnectTimeout() ) .remoteAddress( target.getAddress() ); // Windows is bugged, multi homed users will just have to live with random connecting IPs if ( getPendingConnection().getListener().isSetLocalAddress() && !PlatformDependent.isWindows() )