-
-
Notifications
You must be signed in to change notification settings - Fork 209
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Move reusable methods to a separate helper class (#863) * Move reusable methods to a separate helper class This way we allow other apps such as Geyser LocalSession to use these currently private methods without needing to copy over the code. * Remove unused field * Improve MCPL ticking data and behaviour (#865) * Move packets to proper packages & implement game thread metadata for every packet * Add new ticking info for 1.21.3 Notable info: ServerboundSelectBundleItemPacket is not ticked, it's instant ClientboundSetHeldSlotPacket is already ticked as it is just renamed ClientboundSetCarriedItemPacket * Expose packet handler executor * Do not tick delimiter packet * Allow server to define packet handler executor factory * Update protocol/src/main/java/org/geysermc/mcprotocollib/network/packet/Packet.java Co-authored-by: chris <github@onechris.mozmail.com> --------- Co-authored-by: chris <github@onechris.mozmail.com> * Implement missing ticking --------- Co-authored-by: chris <github@onechris.mozmail.com>
- Loading branch information
1 parent
25df601
commit 8806d1e
Showing
201 changed files
with
1,127 additions
and
242 deletions.
There are no files selected for viewing
149 changes: 149 additions & 0 deletions
149
protocol/src/main/java/org/geysermc/mcprotocollib/network/helper/NettyHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package org.geysermc.mcprotocollib.network.helper; | ||
|
||
import io.netty.buffer.ByteBuf; | ||
import io.netty.channel.AddressedEnvelope; | ||
import io.netty.channel.Channel; | ||
import io.netty.channel.ChannelHandlerContext; | ||
import io.netty.channel.ChannelInboundHandlerAdapter; | ||
import io.netty.channel.ChannelPipeline; | ||
import io.netty.channel.EventLoop; | ||
import io.netty.handler.codec.dns.DefaultDnsQuestion; | ||
import io.netty.handler.codec.dns.DefaultDnsRawRecord; | ||
import io.netty.handler.codec.dns.DefaultDnsRecordDecoder; | ||
import io.netty.handler.codec.dns.DnsRecordType; | ||
import io.netty.handler.codec.dns.DnsResponse; | ||
import io.netty.handler.codec.dns.DnsSection; | ||
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.proxy.HttpProxyHandler; | ||
import io.netty.handler.proxy.Socks4ProxyHandler; | ||
import io.netty.handler.proxy.Socks5ProxyHandler; | ||
import io.netty.resolver.dns.DnsNameResolver; | ||
import io.netty.resolver.dns.DnsNameResolverBuilder; | ||
import org.geysermc.mcprotocollib.network.BuiltinFlags; | ||
import org.geysermc.mcprotocollib.network.ProxyInfo; | ||
import org.geysermc.mcprotocollib.network.Session; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.net.Inet4Address; | ||
import java.net.InetAddress; | ||
import java.net.InetSocketAddress; | ||
import java.net.UnknownHostException; | ||
|
||
public class NettyHelper { | ||
private static final Logger log = LoggerFactory.getLogger(NettyHelper.class); | ||
private static final String IP_REGEX = "\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b"; | ||
|
||
public static InetSocketAddress resolveAddress(Session session, EventLoop eventLoop, String host, int port) { | ||
String name = session.getPacketProtocol().getSRVRecordPrefix() + "._tcp." + host; | ||
log.debug("Attempting SRV lookup for \"{}\".", name); | ||
|
||
if (session.getFlag(BuiltinFlags.ATTEMPT_SRV_RESOLVE, true) && (!host.matches(IP_REGEX) && !host.equalsIgnoreCase("localhost"))) { | ||
try (DnsNameResolver resolver = new DnsNameResolverBuilder(eventLoop) | ||
.channelFactory(TransportHelper.TRANSPORT_TYPE.datagramChannelFactory()) | ||
.build()) { | ||
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope = resolver.query(new DefaultDnsQuestion(name, DnsRecordType.SRV)).get(); | ||
try { | ||
DnsResponse response = envelope.content(); | ||
if (response.count(DnsSection.ANSWER) > 0) { | ||
DefaultDnsRawRecord record = response.recordAt(DnsSection.ANSWER, 0); | ||
if (record.type() == DnsRecordType.SRV) { | ||
ByteBuf buf = record.content(); | ||
buf.skipBytes(4); // Skip priority and weight. | ||
|
||
int tempPort = buf.readUnsignedShort(); | ||
String tempHost = DefaultDnsRecordDecoder.decodeName(buf); | ||
if (tempHost.endsWith(".")) { | ||
tempHost = tempHost.substring(0, tempHost.length() - 1); | ||
} | ||
|
||
log.debug("Found SRV record containing \"{}:{}\".", tempHost, tempPort); | ||
|
||
host = tempHost; | ||
port = tempPort; | ||
} else { | ||
log.debug("Received non-SRV record in response."); | ||
} | ||
} else { | ||
log.debug("No SRV record found."); | ||
} | ||
} finally { | ||
envelope.release(); | ||
} | ||
} catch (Exception e) { | ||
log.debug("Failed to resolve SRV record.", e); | ||
} | ||
} else { | ||
log.debug("Not resolving SRV record for {}", host); | ||
} | ||
|
||
// Resolve host here | ||
try { | ||
InetAddress resolved = InetAddress.getByName(host); | ||
log.debug("Resolved {} -> {}", host, resolved.getHostAddress()); | ||
return new InetSocketAddress(resolved, port); | ||
} catch (UnknownHostException e) { | ||
log.debug("Failed to resolve host, letting Netty do it instead.", e); | ||
return InetSocketAddress.createUnresolved(host, port); | ||
} | ||
} | ||
|
||
public static void initializeHAProxySupport(Session session, Channel channel) { | ||
InetSocketAddress clientAddress = session.getFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS); | ||
if (clientAddress == null) { | ||
return; | ||
} | ||
|
||
channel.pipeline().addLast("proxy-protocol-encoder", HAProxyMessageEncoder.INSTANCE); | ||
channel.pipeline().addLast("proxy-protocol-packet-sender", new ChannelInboundHandlerAdapter() { | ||
@Override | ||
public void channelActive(ChannelHandlerContext ctx) throws Exception { | ||
InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress(); | ||
HAProxyProxiedProtocol proxiedProtocol = clientAddress.getAddress() instanceof Inet4Address ? HAProxyProxiedProtocol.TCP4 : HAProxyProxiedProtocol.TCP6; | ||
ctx.channel().writeAndFlush(new HAProxyMessage( | ||
HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, proxiedProtocol, | ||
clientAddress.getAddress().getHostAddress(), remoteAddress.getAddress().getHostAddress(), | ||
clientAddress.getPort(), remoteAddress.getPort() | ||
)).addListener(future -> channel.pipeline().remove("proxy-protocol-encoder")); | ||
ctx.pipeline().remove(this); | ||
|
||
super.channelActive(ctx); | ||
} | ||
}); | ||
} | ||
|
||
public static void addProxy(ProxyInfo proxy, ChannelPipeline pipeline) { | ||
if (proxy == null) { | ||
return; | ||
} | ||
|
||
switch (proxy.type()) { | ||
case HTTP -> { | ||
if (proxy.username() != null && proxy.password() != null) { | ||
pipeline.addLast("proxy", new HttpProxyHandler(proxy.address(), proxy.username(), proxy.password())); | ||
} else { | ||
pipeline.addLast("proxy", new HttpProxyHandler(proxy.address())); | ||
} | ||
} | ||
case SOCKS4 -> { | ||
if (proxy.username() != null) { | ||
pipeline.addLast("proxy", new Socks4ProxyHandler(proxy.address(), proxy.username())); | ||
} else { | ||
pipeline.addLast("proxy", new Socks4ProxyHandler(proxy.address())); | ||
} | ||
} | ||
case SOCKS5 -> { | ||
if (proxy.username() != null && proxy.password() != null) { | ||
pipeline.addLast("proxy", new Socks5ProxyHandler(proxy.address(), proxy.username(), proxy.password())); | ||
} else { | ||
pipeline.addLast("proxy", new Socks5ProxyHandler(proxy.address())); | ||
} | ||
} | ||
default -> throw new UnsupportedOperationException("Unsupported proxy type: " + proxy.type()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
...ol/src/main/java/org/geysermc/mcprotocollib/network/tcp/DefaultPacketHandlerExecutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package org.geysermc.mcprotocollib.network.tcp; | ||
|
||
import io.netty.channel.DefaultEventLoopGroup; | ||
import io.netty.channel.EventLoopGroup; | ||
import io.netty.util.concurrent.DefaultThreadFactory; | ||
|
||
import java.util.concurrent.Executor; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
public class DefaultPacketHandlerExecutor { | ||
/** | ||
* Controls whether non-priority packets are handled in a separate event loop | ||
*/ | ||
public static boolean USE_EVENT_LOOP_FOR_PACKETS = true; | ||
private static EventLoopGroup PACKET_EVENT_LOOP; | ||
private static final int SHUTDOWN_QUIET_PERIOD_MS = 100; | ||
private static final int SHUTDOWN_TIMEOUT_MS = 500; | ||
|
||
public static Executor createExecutor() { | ||
if (!USE_EVENT_LOOP_FOR_PACKETS) { | ||
return Runnable::run; | ||
} | ||
|
||
if (PACKET_EVENT_LOOP == null) { | ||
// See TcpClientSession.newThreadFactory() for details on | ||
// daemon threads and their interaction with the runtime. | ||
PACKET_EVENT_LOOP = new DefaultEventLoopGroup(new DefaultThreadFactory(DefaultPacketHandlerExecutor.class, true)); | ||
Runtime.getRuntime().addShutdownHook(new Thread( | ||
() -> PACKET_EVENT_LOOP.shutdownGracefully(SHUTDOWN_QUIET_PERIOD_MS, SHUTDOWN_TIMEOUT_MS, TimeUnit.MILLISECONDS))); | ||
} | ||
|
||
return PACKET_EVENT_LOOP.next(); | ||
} | ||
} |
Oops, something went wrong.