Skip to content

Commit

Permalink
Implement session restructure mcpl PR, fix disconnect messages in con…
Browse files Browse the repository at this point in the history
…figuration stage (#5196)

* Implement session restructure mcpl PR

* Bump mcpl

* Update mcpl api usage

* Update MCPL to release, fix #5281 (GeyserMC/MCProtocolLib@1daf036)

* Use correct mcpl dependency

* apparently 1.21.4-1 doesn't exist? back to snapshots then

---------

Co-authored-by: onebeastchris <[email protected]>
  • Loading branch information
AlexProgrammerDE and onebeastchris authored Jan 18, 2025
1 parent a7d475e commit 613718a
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

package org.geysermc.geyser.platform.spigot;

import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer;
import io.netty.bootstrap.ServerBootstrap;
Expand Down Expand Up @@ -176,9 +177,9 @@ private ChannelInitializer<Channel> getChildHandler(GeyserBootstrap bootstrap, C
*/
private void workAroundWeirdBug(GeyserBootstrap bootstrap) {
MinecraftProtocol protocol = new MinecraftProtocol();
LocalSession session = new LocalSession(bootstrap.getGeyserConfig().getRemote().address(),
bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress,
InetAddress.getLoopbackAddress().getHostAddress(), protocol, Runnable::run);
LocalSession session = new LocalSession(this.serverSocketAddress, InetAddress.getLoopbackAddress().getHostAddress(), protocol, Runnable::run);
session.setFlag(MinecraftConstants.CLIENT_HOST, bootstrap.getGeyserConfig().getRemote().address());
session.setFlag(MinecraftConstants.CLIENT_PORT, bootstrap.getGeyserConfig().getRemote().port());
session.connect();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class SessionDisconnectEventImpl extends SessionDisconnectEvent {
private final Component reasonComponent;

public SessionDisconnectEventImpl(@NonNull GeyserSession session, Component reason) {
super(session, MessageTranslator.convertToPlainText(reason, session.locale()));
super(session, MessageTranslator.convertMessageRaw(reason, session.locale()));
this.reasonComponent = reason;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Unbreakable;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;

import java.util.List;
Expand Down Expand Up @@ -424,7 +425,7 @@ private int getRepairCost(GeyserItemStack itemStack) {

private boolean hasDurability(GeyserItemStack itemStack) {
if (itemStack.asItem().defaultMaxDamage() > 0) {
return itemStack.getComponentOrFallback(DataComponentType.UNBREAKABLE, false);
return itemStack.getComponent(DataComponentType.UNBREAKABLE) != null;
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,19 @@
import io.netty.channel.local.LocalChannel;

import java.net.InetSocketAddress;
import java.net.SocketAddress;

/**
* Client -> server storing the spoofed remote address.
*/
public class LocalChannelWithRemoteAddress extends LocalChannel {
private InetSocketAddress spoofedAddress;
private SocketAddress spoofedAddress;

public InetSocketAddress spoofedRemoteAddress() {
public SocketAddress spoofedRemoteAddress() {
return spoofedAddress;
}

public void spoofedRemoteAddress(InetSocketAddress socketAddress) {
public void spoofedRemoteAddress(SocketAddress socketAddress) {
this.spoofedAddress = socketAddress;
}
}
149 changes: 32 additions & 117 deletions core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,151 +27,66 @@

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.ReflectiveChannelFactory;
import io.netty.channel.unix.PreferredDirectByteBufAllocator;
import io.netty.handler.codec.haproxy.HAProxyCommand;
import io.netty.handler.codec.haproxy.HAProxyMessage;
import io.netty.handler.codec.haproxy.HAProxyMessageEncoder;
import io.netty.handler.codec.haproxy.HAProxyProtocolVersion;
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.mcprotocollib.network.BuiltinFlags;
import org.geysermc.mcprotocollib.network.codec.PacketCodecHelper;
import org.geysermc.mcprotocollib.network.helper.NettyHelper;
import org.geysermc.mcprotocollib.network.netty.MinecraftChannelInitializer;
import org.geysermc.mcprotocollib.network.packet.PacketProtocol;
import org.geysermc.mcprotocollib.network.tcp.FlushHandler;
import org.geysermc.mcprotocollib.network.tcp.TcpFlowControlHandler;
import org.geysermc.mcprotocollib.network.tcp.TcpPacketCodec;
import org.geysermc.mcprotocollib.network.tcp.TcpPacketCompression;
import org.geysermc.mcprotocollib.network.tcp.TcpPacketEncryptor;
import org.geysermc.mcprotocollib.network.tcp.TcpPacketSizer;
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodecHelper;
import org.geysermc.mcprotocollib.network.session.ClientNetworkSession;

import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

/**
* Manages a Minecraft Java session over our LocalChannel implementations.
*/
public final class LocalSession extends TcpSession {
private static DefaultEventLoopGroup DEFAULT_EVENT_LOOP_GROUP;
public final class LocalSession extends ClientNetworkSession {
private static PreferredDirectByteBufAllocator PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR = null;

private final SocketAddress targetAddress;
private final String clientIp;
private final PacketCodecHelper codecHelper;
private final SocketAddress spoofedRemoteAddress;

public LocalSession(String host, int port, SocketAddress targetAddress, String clientIp, PacketProtocol protocol, Executor packetHandlerExecutor) {
super(host, port, protocol, packetHandlerExecutor);
this.targetAddress = targetAddress;
this.clientIp = clientIp;
this.codecHelper = protocol.createHelper();
public LocalSession(SocketAddress targetAddress, String clientIp, PacketProtocol protocol, Executor packetHandlerExecutor) {
super(targetAddress, protocol, packetHandlerExecutor, null, null);
this.spoofedRemoteAddress = new InetSocketAddress(clientIp, 0);
}

@Override
public void connect(boolean wait, boolean transferring) {
if (this.disconnected) {
throw new IllegalStateException("Connection has already been disconnected.");
}

if (DEFAULT_EVENT_LOOP_GROUP == null) {
DEFAULT_EVENT_LOOP_GROUP = new DefaultEventLoopGroup(new DefaultThreadFactory(this.getClass(), true));
Runtime.getRuntime().addShutdownHook(new Thread(
() -> DEFAULT_EVENT_LOOP_GROUP.shutdownGracefully(100, 500, TimeUnit.MILLISECONDS)));
}

final Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(LocalChannelWithRemoteAddress.class);
bootstrap.handler(new ChannelInitializer<LocalChannelWithRemoteAddress>() {
@Override
public void initChannel(@NonNull LocalChannelWithRemoteAddress channel) {
channel.spoofedRemoteAddress(new InetSocketAddress(clientIp, 0));
PacketProtocol protocol = getPacketProtocol();
protocol.newClientSession(LocalSession.this, transferring);

ChannelPipeline pipeline = channel.pipeline();

addHAProxySupport(pipeline);

pipeline.addLast("read-timeout", new ReadTimeoutHandler(getFlag(BuiltinFlags.READ_TIMEOUT, 30)));
pipeline.addLast("write-timeout", new WriteTimeoutHandler(getFlag(BuiltinFlags.WRITE_TIMEOUT, 0)));

pipeline.addLast("encryption", new TcpPacketEncryptor());
pipeline.addLast("sizer", new TcpPacketSizer(protocol.getPacketHeader(), getCodecHelper()));
pipeline.addLast("compression", new TcpPacketCompression(getCodecHelper()));

pipeline.addLast("flow-control", new TcpFlowControlHandler());
pipeline.addLast("codec", new TcpPacketCodec(LocalSession.this, true));
pipeline.addLast("flush-handler", new FlushHandler());
pipeline.addLast("manager", LocalSession.this);
}
}).group(DEFAULT_EVENT_LOOP_GROUP).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getFlag(BuiltinFlags.CLIENT_CONNECT_TIMEOUT, 30) * 1000);
protected ChannelFactory<? extends Channel> getChannelFactory() {
return new ReflectiveChannelFactory<>(LocalChannelWithRemoteAddress.class);
}

@Override
protected void setOptions(Bootstrap bootstrap) {
if (PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR != null) {
bootstrap.option(ChannelOption.ALLOCATOR, PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR);
}
}

bootstrap.remoteAddress(targetAddress);

CompletableFuture<Void> handleFuture = new CompletableFuture<>();
bootstrap.connect().addListener((futureListener) -> {
if (!futureListener.isSuccess()) {
exceptionCaught(null, futureListener.cause());
}
@Override
protected ChannelHandler getChannelHandler() {
return new MinecraftChannelInitializer<>(channel -> {
PacketProtocol protocol = getPacketProtocol();
protocol.newClientSession(LocalSession.this);

handleFuture.complete(null);
});
return LocalSession.this;
}, true) {
@Override
public void initChannel(@NonNull Channel channel) throws Exception {
((LocalChannelWithRemoteAddress) channel).spoofedRemoteAddress(spoofedRemoteAddress);

if (wait) {
handleFuture.join();
}
}
NettyHelper.initializeHAProxySupport(LocalSession.this, channel);

@Override
public MinecraftCodecHelper getCodecHelper() {
return (MinecraftCodecHelper) this.codecHelper;
super.initChannel(channel);
}
};
}

// TODO duplicate code
private void addHAProxySupport(ChannelPipeline pipeline) {
InetSocketAddress clientAddress = getFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS);
if (clientAddress != null) {
pipeline.addFirst("proxy-protocol-packet-sender", new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(@NonNull ChannelHandlerContext ctx) throws Exception {
HAProxyProxiedProtocol proxiedProtocol = clientAddress.getAddress() instanceof Inet4Address ? HAProxyProxiedProtocol.TCP4 : HAProxyProxiedProtocol.TCP6;
InetSocketAddress remoteAddress;
if (ctx.channel().remoteAddress() instanceof InetSocketAddress) {
remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
} else {
remoteAddress = new InetSocketAddress(host, port);
}
ctx.channel().writeAndFlush(new HAProxyMessage(
HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, proxiedProtocol,
clientAddress.getAddress().getHostAddress(), remoteAddress.getAddress().getHostAddress(),
clientAddress.getPort(), remoteAddress.getPort()
));
ctx.pipeline().remove(this);
ctx.pipeline().remove("proxy-protocol-encoder");
super.channelActive(ctx);
}
});
pipeline.addFirst("proxy-protocol-encoder", HAProxyMessageEncoder.INSTANCE);
}
}

/**
* Should only be called when direct ByteBufs should be preferred. At this moment, this should only be called on BungeeCord.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.mcprotocollib.network.ClientSession;
import org.geysermc.mcprotocollib.network.packet.Packet;
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodecHelper;

@Getter
@RequiredArgsConstructor
public class DownstreamSession {
private final TcpSession session;
private final ClientSession session;

public void sendPacket(@NonNull Packet packet) {
this.session.send(packet);
Expand Down
Loading

0 comments on commit 613718a

Please sign in to comment.