-
-
Notifications
You must be signed in to change notification settings - Fork 658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
full support for UnixDomainSocket backend #1173
base: dev/3.0.0
Are you sure you want to change the base?
Changes from 6 commits
c0efc35
01bbc8b
18be4ad
50d7771
bd7d87a
a78d248
47fdc4f
125a10a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,16 +42,22 @@ | |
import com.velocitypowered.proxy.protocol.packet.PluginMessage; | ||
import com.velocitypowered.proxy.protocol.packet.ServerLogin; | ||
import com.velocitypowered.proxy.server.VelocityRegisteredServer; | ||
import io.netty.bootstrap.Bootstrap; | ||
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.nio.charset.StandardCharsets; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.function.UnaryOperator; | ||
|
||
import io.netty.channel.unix.DomainSocketAddress; | ||
import net.kyori.adventure.nbt.CompoundBinaryTag; | ||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; | ||
import org.checkerframework.checker.nullness.qual.Nullable; | ||
|
@@ -99,34 +105,40 @@ 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()) | ||
.handler(server.getBackendChannelInitializer()) | ||
.connect(registeredServer.getServerInfo().getAddress()) | ||
.addListener((ChannelFutureListener) future -> { | ||
if (future.isSuccess()) { | ||
connection = new MinecraftConnection(future.channel(), server); | ||
connection.setAssociation(VelocityServerConnection.this); | ||
future.channel().pipeline().addLast(HANDLER, connection); | ||
|
||
// Kick off the connection process | ||
if (!connection.setActiveSessionHandler(StateRegistry.HANDSHAKE)) { | ||
MinecraftSessionHandler handler = | ||
new LoginSessionHandler(server, VelocityServerConnection.this, result); | ||
connection.setActiveSessionHandler(StateRegistry.HANDSHAKE, handler); | ||
connection.addSessionHandler(StateRegistry.LOGIN, handler); | ||
} | ||
|
||
// Set the connection phase, which may, for future forge (or whatever), be | ||
// determined | ||
// at this point already | ||
connectionPhase = connection.getType().getInitialBackendPhase(); | ||
startHandshake(); | ||
} else { | ||
// Complete the result immediately. ConnectedPlayer will reset the in-flight | ||
// connection. | ||
result.completeExceptionally(future.cause()); | ||
} | ||
}); | ||
SocketAddress address = registeredServer.getServerInfo().getSocketAddress(); | ||
Bootstrap bootstrap; | ||
if (address instanceof DomainSocketAddress) { | ||
bootstrap = server.createDomainBootstrap(proxyPlayer.getConnection().eventLoop()); | ||
} else { | ||
bootstrap = server.createBootstrap(proxyPlayer.getConnection().eventLoop()); | ||
} | ||
bootstrap.handler(server.getBackendChannelInitializer()) | ||
.connect(address) | ||
.addListener((ChannelFutureListener) future -> { | ||
if (future.isSuccess()) { | ||
connection = new MinecraftConnection(future.channel(), server); | ||
connection.setAssociation(VelocityServerConnection.this); | ||
future.channel().pipeline().addLast(HANDLER, connection); | ||
|
||
// Kick off the connection process | ||
if (!connection.setActiveSessionHandler(StateRegistry.HANDSHAKE)) { | ||
MinecraftSessionHandler handler = | ||
new LoginSessionHandler(server, VelocityServerConnection.this, result); | ||
connection.setActiveSessionHandler(StateRegistry.HANDSHAKE, handler); | ||
connection.addSessionHandler(StateRegistry.LOGIN, handler); | ||
} | ||
|
||
// Set the connection phase, which may, for future forge (or whatever), be | ||
// determined | ||
// at this point already | ||
connectionPhase = connection.getType().getInitialBackendPhase(); | ||
startHandshake(); | ||
} else { | ||
// Complete the result immediately. ConnectedPlayer will reset the in-flight | ||
// connection. | ||
result.completeExceptionally(future.cause()); | ||
} | ||
}); | ||
return result; | ||
} | ||
|
||
|
@@ -140,13 +152,26 @@ String getPlayerRemoteAddressAsString() { | |
} | ||
} | ||
|
||
private String getRemoteAddress() { | ||
var op = proxyPlayer.getVirtualHost(); | ||
if (op.isPresent()) { | ||
return op.get().getHostString(); | ||
} | ||
SocketAddress address = registeredServer.getServerInfo().getSocketAddress(); | ||
if (address instanceof InetSocketAddress) { | ||
InetSocketAddress address1 = (InetSocketAddress) address; | ||
return address1.getHostString(); | ||
} else { | ||
DomainSocketAddress address1 = (DomainSocketAddress) address; | ||
return address1.toString(); | ||
} | ||
} | ||
|
||
private String createLegacyForwardingAddress(UnaryOperator<List<Property>> propertiesTransform) { | ||
// BungeeCord IP forwarding is simply a special injection after the "address" in the handshake, | ||
// separated by \0 (the null byte). In order, you send the original host, the player's IP, their | ||
// UUID (undashed), and if you are in online-mode, their login properties (from Mojang). | ||
StringBuilder data = new StringBuilder().append(proxyPlayer.getVirtualHost().orElseGet(() -> | ||
registeredServer.getServerInfo().getAddress()).getHostString()) | ||
.append('\0') | ||
StringBuilder data = new StringBuilder().append(getRemoteAddress()) | ||
.append(getPlayerRemoteAddressAsString()) | ||
.append('\0') | ||
.append(proxyPlayer.getGameProfile().getUndashedId()) | ||
|
@@ -174,9 +199,10 @@ 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(() -> registeredServer.getServerInfo().getAddress()) | ||
// .getHostString(); | ||
String playerVhost = getRemoteAddress(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no |
||
|
||
Handshake handshake = new Handshake(); | ||
handshake.setNextStatus(StateRegistry.LOGIN_ID); | ||
|
@@ -192,13 +218,19 @@ private void startHandshake() { | |
handshake.setServerAddress(playerVhost); | ||
} | ||
|
||
handshake.setPort(registeredServer.getServerInfo().getAddress().getPort()); | ||
SocketAddress address = registeredServer.getServerInfo().getSocketAddress(); | ||
if (address instanceof InetSocketAddress) { | ||
InetSocketAddress address1 = (InetSocketAddress) address; | ||
handshake.setPort(address1.getPort()); | ||
} else { | ||
handshake.setPort(-1); | ||
} | ||
mc.delayedWrite(handshake); | ||
|
||
mc.setProtocolVersion(protocolVersion); | ||
mc.setActiveSessionHandler(StateRegistry.LOGIN); | ||
if (proxyPlayer.getIdentifiedKey() == null | ||
&& proxyPlayer.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_19_3) >= 0) { | ||
&& proxyPlayer.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_19_3) >= 0) { | ||
mc.delayedWrite(new ServerLogin(proxyPlayer.getUsername(), proxyPlayer.getUniqueId())); | ||
} else { | ||
mc.delayedWrite(new ServerLogin(proxyPlayer.getUsername(), proxyPlayer.getIdentifiedKey())); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,8 @@ | |
import io.netty.channel.epoll.EpollEventLoopGroup; | ||
import io.netty.channel.epoll.EpollServerSocketChannel; | ||
import io.netty.channel.epoll.EpollSocketChannel; | ||
import io.netty.channel.epoll.EpollDomainSocketChannel; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. epoll is epoll, we can't just slap it on every single connection type, that is not how that works There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so i patch the paper There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so i patch the paper |
||
import io.netty.channel.epoll.EpollServerDomainSocketChannel; | ||
import io.netty.channel.kqueue.KQueue; | ||
import io.netty.channel.kqueue.KQueueDatagramChannel; | ||
import io.netty.channel.kqueue.KQueueEventLoopGroup; | ||
|
@@ -37,6 +39,9 @@ | |
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.DomainSocketChannel; | ||
import io.netty.channel.unix.ServerDomainSocketChannel; | ||
|
||
import java.util.concurrent.ThreadFactory; | ||
import java.util.function.BiFunction; | ||
|
||
|
@@ -47,31 +52,43 @@ public enum TransportType { | |
NIO("NIO", NioServerSocketChannel::new, | ||
NioSocketChannel::new, | ||
NioDatagramChannel::new, | ||
EpollServerDomainSocketChannel::new, | ||
EpollDomainSocketChannel::new, | ||
(name, type) -> new NioEventLoopGroup(0, createThreadFactory(name, type))), | ||
EPOLL("epoll", EpollServerSocketChannel::new, | ||
EpollSocketChannel::new, | ||
EpollDatagramChannel::new, | ||
EpollServerDomainSocketChannel::new, | ||
EpollDomainSocketChannel::new, | ||
(name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type))), | ||
KQUEUE("kqueue", KQueueServerSocketChannel::new, | ||
KQueueSocketChannel::new, | ||
KQueueDatagramChannel::new, | ||
EpollServerDomainSocketChannel::new, | ||
EpollDomainSocketChannel::new, | ||
(name, type) -> new KQueueEventLoopGroup(0, createThreadFactory(name, type))); | ||
|
||
final String name; | ||
final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory; | ||
final ChannelFactory<? extends SocketChannel> socketChannelFactory; | ||
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory; | ||
final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory; | ||
final ChannelFactory<? extends ServerDomainSocketChannel> domainServerSocketChannelFactory; | ||
final ChannelFactory<? extends DomainSocketChannel> domainSocketChannelFactory; | ||
|
||
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 BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory) { | ||
this.name = name; | ||
this.serverSocketChannelFactory = serverSocketChannelFactory; | ||
this.socketChannelFactory = socketChannelFactory; | ||
this.datagramChannelFactory = datagramChannelFactory; | ||
this.domainServerSocketChannelFactory = domainServerSocketChannelFactory; | ||
this.domainSocketChannelFactory = domainSocketChannelFactory; | ||
this.eventLoopGroupFactory = eventLoopGroupFactory; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this changes the intended behavior here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unix socket hoststring like
/xxx/xxx
is a filename, but inetsocketaddress.getHostString is a ipaddress, because of these two differences, resulting in different behaviors?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh i missing a
.append('\0')