From 291069af807b5a20c09a3a4d68d0710ae37f7f27 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 16 Sep 2018 13:59:44 -0400 Subject: [PATCH] Implement RegisteredServer#ping() --- .../proxy/connection/MinecraftConnection.java | 2 - .../server/VelocityRegisteredServer.java | 51 +++++++++++++- .../proxy/server/ping/PingSessionHandler.java | 67 +++++++++++++++++++ 3 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java index b4a68dfbf..0f6844304 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -99,8 +99,6 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { if (association != null) { logger.error("{}: exception encountered", association, cause); - } else { - logger.error("{} encountered an exception", ctx.channel().remoteAddress(), cause); } ctx.close(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java index 21c654957..18dfef0d0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -8,13 +8,31 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.proxy.protocol.ProtocolConstants; +import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; +import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; +import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder; +import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; +import com.velocitypowered.proxy.server.ping.PingSessionHandler; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelInitializer; +import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import static com.velocitypowered.proxy.network.Connections.*; +import static com.velocitypowered.proxy.network.Connections.HANDLER; +import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER; + public class VelocityRegisteredServer implements RegisteredServer { private final VelocityServer server; private final ServerInfo serverInfo; @@ -43,9 +61,36 @@ public class VelocityRegisteredServer implements RegisteredServer { @Override public CompletableFuture ping() { - CompletableFuture p = new CompletableFuture<>(); - p.completeExceptionally(new UnsupportedOperationException("Not currently implemented.")); - return p; + CompletableFuture pingFuture = new CompletableFuture<>(); + server.initializeGenericBootstrap() + .handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + ch.pipeline() + .addLast(READ_TIMEOUT, new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(), TimeUnit.SECONDS)) + .addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder()) + .addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE) + .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolConstants.Direction.CLIENTBOUND)) + .addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolConstants.Direction.SERVERBOUND)); + + MinecraftConnection connection = new MinecraftConnection(ch, server); + connection.setState(StateRegistry.HANDSHAKE); + ch.pipeline().addLast(HANDLER, connection); + } + }) + .connect(serverInfo.getAddress()) + .addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + MinecraftConnection conn = future.channel().pipeline().get(MinecraftConnection.class); + conn.setSessionHandler(new PingSessionHandler(pingFuture, VelocityRegisteredServer.this, conn)); + } else { + pingFuture.completeExceptionally(future.cause()); + } + } + }); + return pingFuture; } public void addPlayer(ConnectedPlayer player) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java new file mode 100644 index 000000000..4ed4dae44 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java @@ -0,0 +1,67 @@ +package com.velocitypowered.proxy.server.ping; + +import com.google.common.base.Preconditions; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.proxy.server.ServerPing; +import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; +import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.packet.Handshake; +import com.velocitypowered.proxy.protocol.packet.StatusRequest; +import com.velocitypowered.proxy.protocol.packet.StatusResponse; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +public class PingSessionHandler implements MinecraftSessionHandler { + private final CompletableFuture result; + private final RegisteredServer server; + private final MinecraftConnection connection; + private boolean completed = false; + + public PingSessionHandler(CompletableFuture result, RegisteredServer server, MinecraftConnection connection) { + this.result = result; + this.server = server; + this.connection = connection; + } + + @Override + public void activated() { + Handshake handshake = new Handshake(); + handshake.setNextStatus(StateRegistry.STATUS_ID); + handshake.setServerAddress(server.getServerInfo().getAddress().getHostString()); + handshake.setPort(server.getServerInfo().getAddress().getPort()); + handshake.setProtocolVersion(ProtocolConstants.MINIMUM_GENERIC_VERSION); + connection.write(handshake); + + connection.setState(StateRegistry.STATUS); + connection.write(new StatusRequest()); + } + + @Override + public void handle(MinecraftPacket packet) { + Preconditions.checkState(packet instanceof StatusResponse, "Did not get status response back from connection"); + + // All good! + completed = true; + connection.close(); + + ServerPing ping = VelocityServer.GSON.fromJson(((StatusResponse) packet).getStatus(), ServerPing.class); + result.complete(ping); + } + + @Override + public void disconnected() { + if (!completed) { + result.completeExceptionally(new IOException("Unexpectedly disconnected from remote server")); + } + } + + @Override + public void exception(Throwable throwable) { + result.completeExceptionally(throwable); + } +}