Skip to content

Commit c093141

Browse files
committed
Add full support for Java ping pong packets
1 parent ae130cf commit c093141

File tree

7 files changed

+63
-52
lines changed

7 files changed

+63
-52
lines changed

Diff for: core/src/main/java/org/geysermc/geyser/session/GeyserSession.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -648,10 +648,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
648648
private ScheduledFuture<?> mountVehicleScheduledFuture = null;
649649

650650
/**
651-
* A cache of IDs from ClientboundKeepAlivePackets that have been sent to the Bedrock client, but haven't been returned to the server.
651+
* A cache of IDs from ClientboundKeepAlivePackets or ClientboundPingPacket that have been sent to the Bedrock client, but haven't been returned to the server.
652652
* Only used if {@link GeyserConfiguration#isForwardPlayerPing()} is enabled.
653653
*/
654-
private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>();
654+
private final Queue<Runnable> latencyPingCache = new ConcurrentLinkedQueue<>();
655655

656656
/**
657657
* Stores the book that is currently being read. Used in {@link org.geysermc.geyser.translator.protocol.java.inventory.JavaOpenBookTranslator}

Diff for: core/src/main/java/org/geysermc/geyser/session/cache/FormCache.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,23 @@
3232
import it.unimi.dsi.fastutil.ints.IntArrayList;
3333
import it.unimi.dsi.fastutil.ints.IntList;
3434
import java.util.ArrayList;
35+
import java.util.Collections;
3536
import java.util.List;
3637
import java.util.concurrent.TimeUnit;
3738
import java.util.concurrent.atomic.AtomicInteger;
3839
import lombok.RequiredArgsConstructor;
40+
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
3941
import org.cloudburstmc.protocol.bedrock.packet.ModalFormRequestPacket;
4042
import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
4143
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
44+
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
4245
import org.geysermc.cumulus.component.util.ComponentType;
4346
import org.geysermc.cumulus.form.CustomForm;
4447
import org.geysermc.cumulus.form.Form;
4548
import org.geysermc.cumulus.form.SimpleForm;
4649
import org.geysermc.cumulus.form.impl.FormDefinitions;
4750
import org.geysermc.geyser.GeyserImpl;
51+
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
4852
import org.geysermc.geyser.network.GameProtocol;
4953
import org.geysermc.geyser.session.GeyserSession;
5054

@@ -91,7 +95,23 @@ private void sendForm(int formId, Form form) {
9195
latencyPacket.setFromServer(true);
9296
latencyPacket.setTimestamp(MAGIC_FORM_IMAGE_HACK_TIMESTAMP);
9397
session.scheduleInEventLoop(
94-
() -> session.sendUpstreamPacket(latencyPacket),
98+
() -> {
99+
session.getLatencyPingCache().add(() -> session.scheduleInEventLoop(() -> {
100+
// Hack to fix the url image loading bug
101+
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
102+
attributesPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
103+
104+
AttributeData attribute = session.getPlayerEntity().getAttributes().get(GeyserAttributeType.EXPERIENCE_LEVEL);
105+
if (attribute != null) {
106+
attributesPacket.setAttributes(Collections.singletonList(attribute));
107+
} else {
108+
attributesPacket.setAttributes(Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0)));
109+
}
110+
111+
session.sendUpstreamPacket(attributesPacket);
112+
}, 500, TimeUnit.MILLISECONDS));
113+
session.sendUpstreamPacket(latencyPacket);
114+
},
95115
500, TimeUnit.MILLISECONDS
96116
);
97117
}

Diff for: core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockContainerCloseTranslator.java

+5
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ public void translate(GeyserSession session, ContainerClosePacket packet) {
7070
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
7171
latencyPacket.setFromServer(true);
7272
latencyPacket.setTimestamp(MAGIC_VIRTUAL_INVENTORY_HACK);
73+
session.getLatencyPingCache().add(() -> {
74+
if (session.getPendingOrCurrentBedrockInventoryId() != -1) {
75+
InventoryUtils.openPendingInventory(session);
76+
}
77+
});
7378
session.sendUpstreamPacket(latencyPacket);
7479
GeyserImpl.getInstance().getLogger().debug(session, "Unable to open a virtual inventory, sent another latency packet!");
7580
}, 100, TimeUnit.MILLISECONDS);

Diff for: core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockNetworkStackLatencyTranslator.java

+6-43
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,10 @@
2525

2626
package org.geysermc.geyser.translator.protocol.bedrock;
2727

28-
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
2928
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
30-
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
31-
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
3229
import org.geysermc.geyser.session.GeyserSession;
3330
import org.geysermc.geyser.translator.protocol.PacketTranslator;
3431
import org.geysermc.geyser.translator.protocol.Translator;
35-
import org.geysermc.geyser.util.InventoryUtils;
36-
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundKeepAlivePacket;
37-
38-
import java.util.Collections;
39-
import java.util.concurrent.TimeUnit;
4032

4133
/**
4234
* Used to send the forwarded keep alive packet back to the server
@@ -46,47 +38,18 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<Netwo
4638

4739
@Override
4840
public void translate(GeyserSession session, NetworkStackLatencyPacket packet) {
49-
// negative timestamps are used as hack to fix the url image loading bug
50-
if (packet.getTimestamp() >= 0) {
51-
if (session.getGeyser().getConfig().isForwardPlayerPing()) {
52-
// use our cached value because
53-
// a) bedrock can be inaccurate with the value returned
54-
// b) playstation replies with a different magnitude than other platforms
55-
// c) 1.20.10 and later reply with a different magnitude
56-
Long keepAliveId = session.getKeepAliveCache().poll();
57-
if (keepAliveId == null) {
58-
session.getGeyser().getLogger().debug("Received a latency packet that we don't have a KeepAlive for: " + packet);
59-
return;
60-
}
61-
62-
ServerboundKeepAlivePacket keepAlivePacket = new ServerboundKeepAlivePacket(keepAliveId);
63-
session.sendDownstreamPacket(keepAlivePacket);
64-
}
41+
// We should receive these packets in the same order they were sent
42+
final Runnable latencyPing = session.getLatencyPingCache().poll();
43+
if (latencyPing == null) {
44+
session.getGeyser().getLogger().debug("Received a latency packet that we don't have a ping for: " + packet);
6545
return;
6646
}
6747

68-
if (session.getPendingOrCurrentBedrockInventoryId() != -1) {
69-
InventoryUtils.openPendingInventory(session);
70-
} else {
71-
session.scheduleInEventLoop(() -> {
72-
// Hack to fix the url image loading bug
73-
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
74-
attributesPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
75-
76-
AttributeData attribute = session.getPlayerEntity().getAttributes().get(GeyserAttributeType.EXPERIENCE_LEVEL);
77-
if (attribute != null) {
78-
attributesPacket.setAttributes(Collections.singletonList(attribute));
79-
} else {
80-
attributesPacket.setAttributes(Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0)));
81-
}
82-
83-
session.sendUpstreamPacket(attributesPacket);
84-
}, 500, TimeUnit.MILLISECONDS);
85-
}
48+
latencyPing.run();
8649
}
8750

8851
@Override
8952
public boolean shouldExecuteInEventLoop() {
90-
return false;
53+
return true;
9154
}
9255
}

Diff for: core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaKeepAliveTranslator.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.geysermc.geyser.session.GeyserSession;
3131
import org.geysermc.geyser.translator.protocol.PacketTranslator;
3232
import org.geysermc.geyser.translator.protocol.Translator;
33+
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundKeepAlivePacket;
3334

3435
/**
3536
* Used to forward the keep alive packet to the client in order to get back a reliable ping.
@@ -42,10 +43,11 @@ public void translate(GeyserSession session, ClientboundKeepAlivePacket packet)
4243
if (!session.getGeyser().getConfig().isForwardPlayerPing()) {
4344
return;
4445
}
45-
// We use this once the client replies (see BedrockNetworkStackLatencyTranslator)
46-
session.getKeepAliveCache().add(packet.getPingId());
4746

48-
long timestamp = packet.getPingId();
47+
// We use this once the client replies
48+
final long javaId = packet.getPingId();
49+
50+
long timestamp = javaId;
4951

5052
// We take the abs because we rely on the client responding with a negative value ONLY when we send
5153
// a negative timestamp in the form-image-hack performed in FormCache.
@@ -62,9 +64,17 @@ public void translate(GeyserSession session, ClientboundKeepAlivePacket packet)
6264
timestamp /= 10;
6365
}
6466

67+
session.getLatencyPingCache().add(() -> {
68+
// use our cached value because
69+
// a) bedrock can be inaccurate with the value returned
70+
// b) playstation replies with a different magnitude than other platforms
71+
// c) 1.20.10 and later reply with a different magnitude
72+
session.sendDownstreamPacket(new ServerboundKeepAlivePacket(javaId));
73+
});
74+
6575
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
6676
latencyPacket.setFromServer(true);
6777
latencyPacket.setTimestamp(timestamp);
68-
session.sendUpstreamPacketImmediately(latencyPacket);
78+
session.sendUpstreamPacket(latencyPacket);
6979
}
7080
}

Diff for: core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPingTranslator.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,26 @@
2525

2626
package org.geysermc.geyser.translator.protocol.java;
2727

28+
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
2829
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundPingPacket;
2930
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundPongPacket;
3031
import org.geysermc.geyser.session.GeyserSession;
3132
import org.geysermc.geyser.translator.protocol.PacketTranslator;
3233
import org.geysermc.geyser.translator.protocol.Translator;
3334

34-
// Why does this packet exist? Whatever, we better implement it
35+
// This packet is the same as keep alive, except it runs on the client's main thread.
3536
@Translator(packet = ClientboundPingPacket.class)
3637
public class JavaPingTranslator extends PacketTranslator<ClientboundPingPacket> {
3738

3839
@Override
3940
public void translate(GeyserSession session, ClientboundPingPacket packet) {
40-
session.sendDownstreamPacket(new ServerboundPongPacket(packet.getId()));
41+
// We use this once the client replies
42+
final int id = packet.getId();
43+
44+
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
45+
latencyPacket.setFromServer(true);
46+
latencyPacket.setTimestamp(id);
47+
session.getLatencyPingCache().add(() -> session.sendDownstreamPacket(new ServerboundPongPacket(id)));
48+
session.sendUpstreamPacket(latencyPacket);
4149
}
4250
}

Diff for: core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java

+5
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ public static void displayInventory(GeyserSession session, Inventory inventory)
149149
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
150150
latencyPacket.setFromServer(true);
151151
latencyPacket.setTimestamp(MAGIC_VIRTUAL_INVENTORY_HACK);
152+
session.getLatencyPingCache().add(() -> {
153+
if (session.getPendingOrCurrentBedrockInventoryId() != -1) {
154+
InventoryUtils.openPendingInventory(session);
155+
}
156+
});
152157
session.sendUpstreamPacket(latencyPacket);
153158

154159
GeyserImpl.getInstance().getLogger().debug(session, "Queuing virtual inventory (%s)", debugInventory(inventory));

0 commit comments

Comments
 (0)