Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import com.google.common.base.Preconditions;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;

Expand All @@ -19,7 +20,7 @@
public final class ServerInfo implements Comparable<ServerInfo> {

private final String name;
private final InetSocketAddress address;
private final SocketAddress address;

/**
* Creates a new ServerInfo object.
Expand All @@ -32,11 +33,44 @@ public ServerInfo(String name, InetSocketAddress address) {
this.address = Preconditions.checkNotNull(address, "address");
}

/**
* Creates a new ServerInfo object.
*
* @param name the name for the server
* @param address the address of the server to connect to
*/
public ServerInfo(String name, SocketAddress address) {
this.name = Preconditions.checkNotNull(name, "name");
this.address = Preconditions.checkNotNull(address, "address");
}

public final String getName() {
return name;
}

/**
* Returns the address of this server as an {@link InetSocketAddress}.
*
* @return the address
* @deprecated Use {@link #getSocketAddress()} instead, which supports both TCP and Unix socket
* addresses.
*/
@Deprecated
public final InetSocketAddress getAddress() {
if (address instanceof InetSocketAddress inet) {
return inet;
}
throw new UnsupportedOperationException(
"This server is not configured with an InetSocketAddress. Use getSocketAddress() instead.");
}

/**
* Returns the address of this server as a {@link SocketAddress}. This may be an
* {@link InetSocketAddress} for TCP connections or a Unix domain socket address.
*
* @return the address
*/
public final SocketAddress getSocketAddress() {
return address;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.proxy.util.AddressUtil;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.List;
import joptsimple.OptionParser;
Expand Down Expand Up @@ -109,7 +109,7 @@ public ServerInfo convert(String s) {
if (split.length < 2) {
throw new ValueConversionException("Invalid server format. Use <name>:<address>");
}
InetSocketAddress address;
SocketAddress address;
try {
address = AddressUtil.parseAddress(split[1]);
} catch (IllegalStateException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.http.HttpClient;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -455,8 +456,8 @@ private void loadPlugins() {
logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
}

public Bootstrap createBootstrap(@Nullable EventLoopGroup group) {
return this.cm.createWorker(group);
public Bootstrap createBootstrap(@Nullable EventLoopGroup group, SocketAddress target) {
return this.cm.createWorker(group, target);
}

public ChannelInitializer<Channel> getBackendChannelInitializer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ private void loadFavicon() {
}

public InetSocketAddress getBind() {
return AddressUtil.parseAndResolveAddress(bind);
return (InetSocketAddress) AddressUtil.parseAndResolveAddress(bind);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.unix.DomainSocketAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Optional;
import java.util.StringJoiner;
import net.kyori.adventure.text.Component;
Expand Down Expand Up @@ -247,8 +250,14 @@ private void processServerIp(ByteBufDataInput in) {

out.writeUTF("ServerIP");
out.writeUTF(info.getServerInfo().getName());
out.writeUTF(info.getServerInfo().getAddress().getHostString());
out.writeShort(info.getServerInfo().getAddress().getPort());
SocketAddress address = info.getServerInfo().getSocketAddress();
if (address instanceof InetSocketAddress inetAddr) {
out.writeUTF(inetAddr.getHostString());
out.writeShort(inetAddr.getPort());
} else {
out.writeUTF("unix://" + ((DomainSocketAddress) address).path());
out.writeShort(0);
}

sendResponseOnConnection(buf);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -100,9 +102,10 @@ public CompletableFuture<Impl> connect() {
CompletableFuture<Impl> result = new CompletableFuture<>();
// Note: we use the event loop for the connection the player is on. This reduces context
// switches.
server.createBootstrap(proxyPlayer.getConnection().eventLoop())
SocketAddress destinationAddress = registeredServer.getServerInfo().getSocketAddress();
server.createBootstrap(proxyPlayer.getConnection().eventLoop(), destinationAddress)
.handler(server.getBackendChannelInitializer())
.connect(registeredServer.getServerInfo().getAddress())
.connect(destinationAddress)
.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
connection = new MinecraftConnection(future.channel(), server);
Expand Down Expand Up @@ -143,17 +146,27 @@ String getPlayerRemoteAddressAsString() {

private String createLegacyForwardingAddress() {
return PlayerDataForwarding.createLegacyForwardingAddress(
proxyPlayer.getVirtualHost().orElseGet(() ->
registeredServer.getServerInfo().getAddress()).getHostString(),
proxyPlayer.getVirtualHost().orElseGet(() -> {
SocketAddress address = registeredServer.getServerInfo().getSocketAddress();
if (address instanceof InetSocketAddress inetAddr) {
return inetAddr;
}
return InetSocketAddress.createUnresolved("::", 0);
}).getHostString(),
getPlayerRemoteAddressAsString(),
proxyPlayer.getGameProfile()
);
}

private String createBungeeGuardForwardingAddress(byte[] forwardingSecret) {
return PlayerDataForwarding.createBungeeGuardForwardingAddress(
proxyPlayer.getVirtualHost().orElseGet(() ->
registeredServer.getServerInfo().getAddress()).getHostString(),
proxyPlayer.getVirtualHost().orElseGet(() -> {
SocketAddress address = registeredServer.getServerInfo().getSocketAddress();
if (address instanceof InetSocketAddress inetAddr) {
return inetAddr;
}
return InetSocketAddress.createUnresolved("::", 0);
}).getHostString(),
getPlayerRemoteAddressAsString(),
proxyPlayer.getGameProfile(),
forwardingSecret
Expand All @@ -166,9 +179,13 @@ private void startHandshake() {

// Initiate the handshake.
ProtocolVersion protocolVersion = proxyPlayer.getConnection().getProtocolVersion();
String playerVhost = proxyPlayer.getVirtualHost()
.orElseGet(() -> registeredServer.getServerInfo().getAddress())
.getHostString();
String playerVhost = proxyPlayer.getVirtualHost().orElseGet(() -> {
SocketAddress address = registeredServer.getServerInfo().getSocketAddress();
if (address instanceof InetSocketAddress inetAddr) {
return inetAddr;
}
return InetSocketAddress.createUnresolved("::", 0);
}).getHostString();

HandshakePacket handshake = new HandshakePacket();
handshake.setIntent(HandshakeIntent.LOGIN);
Expand All @@ -186,9 +203,10 @@ private void startHandshake() {
handshake.setServerAddress(playerVhost);
}

handshake.setPort(proxyPlayer.getVirtualHost()
.orElseGet(() -> registeredServer.getServerInfo().getAddress())
.getPort());
SocketAddress destinationAddr = registeredServer.getServerInfo().getSocketAddress();
if (destinationAddr instanceof InetSocketAddress) {
handshake.setPort(((InetSocketAddress) destinationAddr).getPort());
}
mc.delayedWrite(handshake);

mc.setProtocolVersion(protocolVersion);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.UnixChannelOption;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.MultithreadEventExecutorGroup;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.http.HttpClient;
import java.util.Collection;
import java.util.Map;
Expand Down Expand Up @@ -184,21 +186,24 @@ public void queryBind(final String hostname, final int port) {
}

/**
* Creates a TCP {@link Bootstrap} using Velocity's event loops.
* Creates a {@link Bootstrap} using Velocity's event loops.
*
* @param group the event loop group to use. Use {@code null} for the default worker group.
* @param target the address the client will connect to
* @return a new {@link Bootstrap}
*/
public Bootstrap createWorker(@Nullable EventLoopGroup group) {
public Bootstrap createWorker(@Nullable EventLoopGroup group, SocketAddress target) {
Bootstrap bootstrap = new Bootstrap()
.channelFactory(this.transportType.socketChannelFactory)
.option(ChannelOption.TCP_NODELAY, true)
.channelFactory(this.transportType.getClientChannelFactory(target))
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
this.server.getConfiguration().getConnectTimeout())
.group(group == null ? this.workerGroup : group)
.resolver(this.resolver.asGroup());
if (server.getConfiguration().useTcpFastOpen()) {
bootstrap.option(ChannelOption.TCP_FASTOPEN_CONNECT, true);
if (!(target instanceof DomainSocketAddress)) {
bootstrap.option(ChannelOption.TCP_NODELAY, true);
if (server.getConfiguration().useTcpFastOpen()) {
bootstrap.option(ChannelOption.TCP_FASTOPEN_CONNECT, true);
}
}
return bootstrap;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,24 @@
package com.velocitypowered.proxy.network;

import com.velocitypowered.proxy.util.concurrent.VelocityNettyThreadFactory;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.IoHandlerFactory;
import io.netty.channel.MultiThreadIoEventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollDomainSocketChannel;
import io.netty.channel.epoll.EpollIoHandler;
import io.netty.channel.epoll.EpollServerDomainSocketChannel;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.kqueue.KQueue;
import io.netty.channel.kqueue.KQueueDatagramChannel;
import io.netty.channel.kqueue.KQueueDomainSocketChannel;
import io.netty.channel.kqueue.KQueueIoHandler;
import io.netty.channel.kqueue.KQueueServerDomainSocketChannel;
import io.netty.channel.kqueue.KQueueServerSocketChannel;
import io.netty.channel.kqueue.KQueueSocketChannel;
import io.netty.channel.nio.NioIoHandler;
Expand All @@ -39,11 +45,17 @@
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.DomainSocketChannel;
import io.netty.channel.unix.ServerDomainSocketChannel;
import io.netty.channel.uring.IoUring;
import io.netty.channel.uring.IoUringDatagramChannel;
import io.netty.channel.uring.IoUringDomainSocketChannel;
import io.netty.channel.uring.IoUringIoHandler;
import io.netty.channel.uring.IoUringServerDomainSocketChannel;
import io.netty.channel.uring.IoUringServerSocketChannel;
import io.netty.channel.uring.IoUringSocketChannel;
import java.net.SocketAddress;
import java.util.concurrent.ThreadFactory;
import java.util.function.Supplier;

Expand All @@ -54,35 +66,49 @@ public enum TransportType {
NIO("NIO", NioServerSocketChannel::new,
NioSocketChannel::new,
NioDatagramChannel::new,
null,
null,
NioIoHandler::newFactory),
EPOLL("epoll", EpollServerSocketChannel::new,
EpollSocketChannel::new,
EpollDatagramChannel::new,
EpollServerDomainSocketChannel::new,
EpollDomainSocketChannel::new,
EpollIoHandler::newFactory),
KQUEUE("kqueue", KQueueServerSocketChannel::new,
KQueueSocketChannel::new,
KQueueDatagramChannel::new,
KQueueServerDomainSocketChannel::new,
KQueueDomainSocketChannel::new,
KQueueIoHandler::newFactory),
IO_URING("io_uring", IoUringServerSocketChannel::new,
IoUringSocketChannel::new,
IoUringDatagramChannel::new,
IoUringServerDomainSocketChannel::new,
IoUringDomainSocketChannel::new,
IoUringIoHandler::newFactory);

final String name;
final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory;
final ChannelFactory<? extends SocketChannel> socketChannelFactory;
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory;
final ChannelFactory<? extends ServerDomainSocketChannel> domainServerSocketChannelFactory;
final ChannelFactory<? extends DomainSocketChannel> domainSocketChannelFactory;
final Supplier<IoHandlerFactory> ioHandlerFactorySupplier;

TransportType(final String name,
final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory,
final ChannelFactory<? extends SocketChannel> socketChannelFactory,
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory,
final ChannelFactory<? extends ServerDomainSocketChannel> domainServerSocketChannelFactory,
final ChannelFactory<? extends DomainSocketChannel> domainSocketChannelFactory,
final Supplier<IoHandlerFactory> ioHandlerFactorySupplier) {
this.name = name;
this.serverSocketChannelFactory = serverSocketChannelFactory;
this.socketChannelFactory = socketChannelFactory;
this.datagramChannelFactory = datagramChannelFactory;
this.domainServerSocketChannelFactory = domainServerSocketChannelFactory;
this.domainSocketChannelFactory = domainSocketChannelFactory;
this.ioHandlerFactorySupplier = ioHandlerFactorySupplier;
}

Expand All @@ -91,6 +117,40 @@ public String toString() {
return this.name;
}

/**
* Returns the channel factory to use to listen on the specified socket address.
*
* @param address the address we want to listen on
* @return the channel factory
*/
public ChannelFactory<? extends ServerChannel> getServerChannelFactory(SocketAddress address) {
if (address instanceof DomainSocketAddress) {
if (this.domainServerSocketChannelFactory == null) {
throw new IllegalArgumentException(
"Domain sockets are not available for non-Linux platforms");
}
return this.domainServerSocketChannelFactory;
}
return this.serverSocketChannelFactory;
}

/**
* Returns the channel factory to use to connect on the specified socket address.
*
* @param address the address we want to connect to
* @return the channel factory
*/
public ChannelFactory<? extends Channel> getClientChannelFactory(SocketAddress address) {
if (address instanceof DomainSocketAddress) {
if (this.domainSocketChannelFactory == null) {
throw new IllegalArgumentException(
"Domain sockets are not available for non-Linux platforms");
}
return this.domainSocketChannelFactory;
}
return this.socketChannelFactory;
}

/**
* Creates a new event loop group for the given type.
*
Expand Down
Loading