mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-04-17 19:12:14 +02:00
Add full support for Java ping pong packets
This commit is contained in:
parent
ae130cf5a5
commit
c09314160d
7 changed files with 63 additions and 52 deletions
core/src/main/java/org/geysermc/geyser
session
translator/protocol
bedrock
java
util
|
@ -648,10 +648,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
private ScheduledFuture<?> mountVehicleScheduledFuture = null;
|
||||
|
||||
/**
|
||||
* A cache of IDs from ClientboundKeepAlivePackets that have been sent to the Bedrock client, but haven't been returned to the server.
|
||||
* A cache of IDs from ClientboundKeepAlivePackets or ClientboundPingPacket that have been sent to the Bedrock client, but haven't been returned to the server.
|
||||
* Only used if {@link GeyserConfiguration#isForwardPlayerPing()} is enabled.
|
||||
*/
|
||||
private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>();
|
||||
private final Queue<Runnable> latencyPingCache = new ConcurrentLinkedQueue<>();
|
||||
|
||||
/**
|
||||
* Stores the book that is currently being read. Used in {@link org.geysermc.geyser.translator.protocol.java.inventory.JavaOpenBookTranslator}
|
||||
|
|
|
@ -32,19 +32,23 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ModalFormRequestPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.cumulus.component.util.ComponentType;
|
||||
import org.geysermc.cumulus.form.CustomForm;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.SimpleForm;
|
||||
import org.geysermc.cumulus.form.impl.FormDefinitions;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
|
@ -91,7 +95,23 @@ public class FormCache {
|
|||
latencyPacket.setFromServer(true);
|
||||
latencyPacket.setTimestamp(MAGIC_FORM_IMAGE_HACK_TIMESTAMP);
|
||||
session.scheduleInEventLoop(
|
||||
() -> session.sendUpstreamPacket(latencyPacket),
|
||||
() -> {
|
||||
session.getLatencyPingCache().add(() -> session.scheduleInEventLoop(() -> {
|
||||
// Hack to fix the url image loading bug
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
|
||||
AttributeData attribute = session.getPlayerEntity().getAttributes().get(GeyserAttributeType.EXPERIENCE_LEVEL);
|
||||
if (attribute != null) {
|
||||
attributesPacket.setAttributes(Collections.singletonList(attribute));
|
||||
} else {
|
||||
attributesPacket.setAttributes(Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0)));
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
}, 500, TimeUnit.MILLISECONDS));
|
||||
session.sendUpstreamPacket(latencyPacket);
|
||||
},
|
||||
500, TimeUnit.MILLISECONDS
|
||||
);
|
||||
}
|
||||
|
|
|
@ -70,6 +70,11 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
|
|||
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
|
||||
latencyPacket.setFromServer(true);
|
||||
latencyPacket.setTimestamp(MAGIC_VIRTUAL_INVENTORY_HACK);
|
||||
session.getLatencyPingCache().add(() -> {
|
||||
if (session.getPendingOrCurrentBedrockInventoryId() != -1) {
|
||||
InventoryUtils.openPendingInventory(session);
|
||||
}
|
||||
});
|
||||
session.sendUpstreamPacket(latencyPacket);
|
||||
GeyserImpl.getInstance().getLogger().debug(session, "Unable to open a virtual inventory, sent another latency packet!");
|
||||
}, 100, TimeUnit.MILLISECONDS);
|
||||
|
|
|
@ -25,18 +25,10 @@
|
|||
|
||||
package org.geysermc.geyser.translator.protocol.bedrock;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundKeepAlivePacket;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Used to send the forwarded keep alive packet back to the server
|
||||
|
@ -46,47 +38,18 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<Netwo
|
|||
|
||||
@Override
|
||||
public void translate(GeyserSession session, NetworkStackLatencyPacket packet) {
|
||||
// negative timestamps are used as hack to fix the url image loading bug
|
||||
if (packet.getTimestamp() >= 0) {
|
||||
if (session.getGeyser().getConfig().isForwardPlayerPing()) {
|
||||
// use our cached value because
|
||||
// a) bedrock can be inaccurate with the value returned
|
||||
// b) playstation replies with a different magnitude than other platforms
|
||||
// c) 1.20.10 and later reply with a different magnitude
|
||||
Long keepAliveId = session.getKeepAliveCache().poll();
|
||||
if (keepAliveId == null) {
|
||||
session.getGeyser().getLogger().debug("Received a latency packet that we don't have a KeepAlive for: " + packet);
|
||||
return;
|
||||
}
|
||||
|
||||
ServerboundKeepAlivePacket keepAlivePacket = new ServerboundKeepAlivePacket(keepAliveId);
|
||||
session.sendDownstreamPacket(keepAlivePacket);
|
||||
}
|
||||
// We should receive these packets in the same order they were sent
|
||||
final Runnable latencyPing = session.getLatencyPingCache().poll();
|
||||
if (latencyPing == null) {
|
||||
session.getGeyser().getLogger().debug("Received a latency packet that we don't have a ping for: " + packet);
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.getPendingOrCurrentBedrockInventoryId() != -1) {
|
||||
InventoryUtils.openPendingInventory(session);
|
||||
} else {
|
||||
session.scheduleInEventLoop(() -> {
|
||||
// Hack to fix the url image loading bug
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
|
||||
AttributeData attribute = session.getPlayerEntity().getAttributes().get(GeyserAttributeType.EXPERIENCE_LEVEL);
|
||||
if (attribute != null) {
|
||||
attributesPacket.setAttributes(Collections.singletonList(attribute));
|
||||
} else {
|
||||
attributesPacket.setAttributes(Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0)));
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
}, 500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
latencyPing.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExecuteInEventLoop() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
|
|||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundKeepAlivePacket;
|
||||
|
||||
/**
|
||||
* Used to forward the keep alive packet to the client in order to get back a reliable ping.
|
||||
|
@ -42,10 +43,11 @@ public class JavaKeepAliveTranslator extends PacketTranslator<ClientboundKeepAli
|
|||
if (!session.getGeyser().getConfig().isForwardPlayerPing()) {
|
||||
return;
|
||||
}
|
||||
// We use this once the client replies (see BedrockNetworkStackLatencyTranslator)
|
||||
session.getKeepAliveCache().add(packet.getPingId());
|
||||
|
||||
long timestamp = packet.getPingId();
|
||||
// We use this once the client replies
|
||||
final long javaId = packet.getPingId();
|
||||
|
||||
long timestamp = javaId;
|
||||
|
||||
// We take the abs because we rely on the client responding with a negative value ONLY when we send
|
||||
// a negative timestamp in the form-image-hack performed in FormCache.
|
||||
|
@ -62,9 +64,17 @@ public class JavaKeepAliveTranslator extends PacketTranslator<ClientboundKeepAli
|
|||
timestamp /= 10;
|
||||
}
|
||||
|
||||
session.getLatencyPingCache().add(() -> {
|
||||
// use our cached value because
|
||||
// a) bedrock can be inaccurate with the value returned
|
||||
// b) playstation replies with a different magnitude than other platforms
|
||||
// c) 1.20.10 and later reply with a different magnitude
|
||||
session.sendDownstreamPacket(new ServerboundKeepAlivePacket(javaId));
|
||||
});
|
||||
|
||||
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
|
||||
latencyPacket.setFromServer(true);
|
||||
latencyPacket.setTimestamp(timestamp);
|
||||
session.sendUpstreamPacketImmediately(latencyPacket);
|
||||
session.sendUpstreamPacket(latencyPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,18 +25,26 @@
|
|||
|
||||
package org.geysermc.geyser.translator.protocol.java;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundPingPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundPongPacket;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
||||
// Why does this packet exist? Whatever, we better implement it
|
||||
// This packet is the same as keep alive, except it runs on the client's main thread.
|
||||
@Translator(packet = ClientboundPingPacket.class)
|
||||
public class JavaPingTranslator extends PacketTranslator<ClientboundPingPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundPingPacket packet) {
|
||||
session.sendDownstreamPacket(new ServerboundPongPacket(packet.getId()));
|
||||
// We use this once the client replies
|
||||
final int id = packet.getId();
|
||||
|
||||
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
|
||||
latencyPacket.setFromServer(true);
|
||||
latencyPacket.setTimestamp(id);
|
||||
session.getLatencyPingCache().add(() -> session.sendDownstreamPacket(new ServerboundPongPacket(id)));
|
||||
session.sendUpstreamPacket(latencyPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,6 +149,11 @@ public class InventoryUtils {
|
|||
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
|
||||
latencyPacket.setFromServer(true);
|
||||
latencyPacket.setTimestamp(MAGIC_VIRTUAL_INVENTORY_HACK);
|
||||
session.getLatencyPingCache().add(() -> {
|
||||
if (session.getPendingOrCurrentBedrockInventoryId() != -1) {
|
||||
InventoryUtils.openPendingInventory(session);
|
||||
}
|
||||
});
|
||||
session.sendUpstreamPacket(latencyPacket);
|
||||
|
||||
GeyserImpl.getInstance().getLogger().debug(session, "Queuing virtual inventory (%s)", debugInventory(inventory));
|
||||
|
|
Loading…
Add table
Reference in a new issue