diff --git a/pom.xml b/pom.xml
index 3147ad5..08afce6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,7 +39,7 @@
dev.waterdog.waterdogpe
waterdog
- 2.0.2-SNAPSHOT
+ 2.0.3-20240423.203337-2
provided
diff --git a/src/main/java/org/nethergames/proxytransport/impl/TransportChannelInitializer.java b/src/main/java/org/nethergames/proxytransport/impl/TransportChannelInitializer.java
index 988e989..f525937 100644
--- a/src/main/java/org/nethergames/proxytransport/impl/TransportChannelInitializer.java
+++ b/src/main/java/org/nethergames/proxytransport/impl/TransportChannelInitializer.java
@@ -1,7 +1,6 @@
package org.nethergames.proxytransport.impl;
import dev.waterdog.waterdogpe.network.NetworkMetrics;
-import dev.waterdog.waterdogpe.network.PacketDirection;
import dev.waterdog.waterdogpe.network.connection.client.ClientConnection;
import dev.waterdog.waterdogpe.network.connection.codec.batch.BedrockBatchDecoder;
import dev.waterdog.waterdogpe.network.connection.codec.batch.BedrockBatchEncoder;
@@ -16,8 +15,9 @@
import io.netty.util.concurrent.Promise;
import lombok.RequiredArgsConstructor;
import org.cloudburstmc.netty.channel.raknet.RakChannel;
+import org.cloudburstmc.netty.channel.raknet.config.RakChannelMetrics;
import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption;
-import org.cloudburstmc.netty.channel.raknet.config.RakMetrics;
+import org.cloudburstmc.protocol.bedrock.PacketDirection;
import org.cloudburstmc.protocol.bedrock.netty.codec.compression.CompressionCodec;
import org.nethergames.proxytransport.compression.FrameIdCodec;
import org.nethergames.proxytransport.compression.ProxyTransportCompressionCodec;
@@ -44,14 +44,14 @@ protected void initChannel(Channel channel) {
int rakVersion = this.player.getProtocol().getRaknetVersion();
CompressionType compression = this.player.getProxy().getConfiguration().getCompression();
- channel.attr(PacketDirection.ATTRIBUTE).set(PacketDirection.FROM_SERVER);
+ channel.attr(PacketDirection.ATTRIBUTE).set(PacketDirection.SERVER_BOUND);
NetworkMetrics metrics = this.player.getProxy().getNetworkMetrics();
if (metrics != null) {
channel.attr(NetworkMetrics.ATTRIBUTE).set(metrics);
}
- if (metrics instanceof RakMetrics rakMetrics && channel instanceof RakChannel) {
+ if (metrics instanceof RakChannelMetrics rakMetrics && channel instanceof RakChannel) {
channel.config().setOption(RakChannelOption.RAK_METRICS, rakMetrics);
}
@@ -72,6 +72,10 @@ protected void initChannel(Channel channel) {
channel.pipeline().addLast(ClientConnection.NAME, handler);
}
+ if (connection.getPacketDirection() != PacketDirection.SERVER_BOUND) {
+ throw new IllegalStateException("Client connection must have a server-bound packet direction");
+ }
+
channel.pipeline()
.addLast(CustomClientEventHandler.NAME, new CustomClientEventHandler(this.player, connection))
.addLast(new TransportChannelInitializer.ChannelActiveHandler(connection, this.promise));
diff --git a/src/main/java/org/nethergames/proxytransport/impl/TransportClientConnection.java b/src/main/java/org/nethergames/proxytransport/impl/TransportClientConnection.java
index 0ad7e29..abc9e3b 100644
--- a/src/main/java/org/nethergames/proxytransport/impl/TransportClientConnection.java
+++ b/src/main/java/org/nethergames/proxytransport/impl/TransportClientConnection.java
@@ -13,6 +13,7 @@
import io.netty.incubator.codec.quic.QuicConnectionPathStats;
import io.netty.incubator.codec.quic.QuicStreamChannel;
import io.netty.util.concurrent.Future;
+import lombok.Getter;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
@@ -51,6 +52,14 @@ public class TransportClientConnection extends BedrockClientConnection {
private long lastPingTimestamp = -1;
private long latency = 0; // Latency in microseconds
+ private long lastSentPackets = 0;
+ private long lastLostPackets = 0;
+ /**
+ * Get the lost percentage of packets sent to the server as a 3 decimal integer.
+ */
+ @Getter
+ private Integer lostPercentage = null;
+
private final List> scheduledTasks = new ArrayList<>();
public TransportClientConnection(ProxiedPlayer player, ServerInfo serverInfo, Channel channel) {
@@ -175,13 +184,37 @@ public long getMicroSecondsPing() {
return this.latency;
}
+ private void setLostPercentage(long lostPackets, long sentPackets) {
+ long lost = lostPackets - this.lastLostPackets;
+ long sent = sentPackets - this.lastSentPackets;
+ this.lastLostPackets = lostPackets;
+ this.lastSentPackets = sentPackets;
+
+ if (sent == 0) {
+ this.lostPercentage = 0;
+ return;
+ }
+
+ long lostPercentage = Math.round(((double) (lost * 100) / sent) * 1000);
+
+ if (lostPercentage > 65535) {
+ this.lostPercentage = 65535;
+ return;
+ }
+
+ this.lostPercentage = (int) lostPercentage;
+ }
+
public void collectStats() {
var connection = getPlayer().getDownstreamConnection();
if (connection instanceof TransportClientConnection && connection.getServerInfo().getServerName().equalsIgnoreCase(getServerInfo().getServerName())) {
if (this.channel instanceof QuicStreamChannel quicChannel) {
quicChannel.parent().collectPathStats(0).addListener((Future quicChannelFuture) -> {
if (quicChannelFuture.isSuccess()) {
- this.latency = quicChannelFuture.getNow().rtt() / 1000; // convert to nanoseconds to microseconds
+ QuicConnectionPathStats quicStats = quicChannelFuture.getNow();
+
+ this.latency = quicStats.rtt() / 1000; // convert to nanoseconds to microsecond
+ this.setLostPercentage(quicStats.lost(), quicStats.sent());
this.broadcastPing();
}
});
diff --git a/src/main/java/org/nethergames/proxytransport/utils/CodecUpdater.java b/src/main/java/org/nethergames/proxytransport/utils/CodecUpdater.java
index 0edefe3..aac1a4e 100644
--- a/src/main/java/org/nethergames/proxytransport/utils/CodecUpdater.java
+++ b/src/main/java/org/nethergames/proxytransport/utils/CodecUpdater.java
@@ -3,13 +3,14 @@
import dev.waterdog.waterdogpe.network.protocol.updaters.ProtocolCodecUpdater;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.codec.v388.serializer.TickSyncSerializer_v388;
+import org.cloudburstmc.protocol.bedrock.data.PacketRecipient;
import org.cloudburstmc.protocol.bedrock.packet.TickSyncPacket;
public class CodecUpdater implements ProtocolCodecUpdater {
@Override
public BedrockCodec.Builder updateCodec(BedrockCodec.Builder builder, BedrockCodec baseCodec) {
- builder.registerPacket(TickSyncPacket::new, TickSyncSerializer_v388.INSTANCE, 23);
+ builder.registerPacket(TickSyncPacket::new, TickSyncSerializer_v388.INSTANCE, 23, PacketRecipient.SERVER);
return builder;
}