This commit is contained in:
OurLobanov 2024-08-10 23:50:32 +01:00 committed by GitHub
commit c85b1efc20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 273 additions and 99 deletions

View file

@ -28,6 +28,7 @@ package org.geysermc.geyser.entity;
import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.EmoteFlag;
import org.cloudburstmc.protocol.bedrock.packet.EmotePacket; import org.cloudburstmc.protocol.bedrock.packet.EmotePacket;
import org.geysermc.geyser.api.entity.EntityData; import org.geysermc.geyser.api.entity.EntityData;
import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.api.entity.type.GeyserEntity;
@ -71,6 +72,8 @@ public class GeyserEntityData implements EntityData {
packet.setXuid(""); packet.setXuid("");
packet.setPlatformId(""); // BDS sends empty packet.setPlatformId(""); // BDS sends empty
packet.setEmoteId(emoteId); packet.setEmoteId(emoteId);
packet.getFlags().add(EmoteFlag.SERVER_SIDE);
packet.getFlags().add(EmoteFlag.MUTE_EMOTE_CHAT);
session.sendUpstreamPacket(packet); session.sendUpstreamPacket(packet);
} }

View file

@ -33,13 +33,20 @@ import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobArmorEquipment
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobEquipmentSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobEquipmentSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.PlayerHotbarSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.PlayerHotbarSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityLinkSerializer_v291; import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityLinkSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityMotionSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v390.serializer.PlayerSkinSerializer_v390; import org.cloudburstmc.protocol.bedrock.codec.v390.serializer.PlayerSkinSerializer_v390;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventoryContentSerializer_v407; import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventoryContentSerializer_v407;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventorySlotSerializer_v407; import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventorySlotSerializer_v407;
import org.cloudburstmc.protocol.bedrock.codec.v422.serializer.FilterTextSerializer_v422;
import org.cloudburstmc.protocol.bedrock.codec.v486.serializer.BossEventSerializer_v486; import org.cloudburstmc.protocol.bedrock.codec.v486.serializer.BossEventSerializer_v486;
import org.cloudburstmc.protocol.bedrock.codec.v554.serializer.TextSerializer_v554;
import org.cloudburstmc.protocol.bedrock.codec.v557.serializer.SetEntityDataSerializer_v557; import org.cloudburstmc.protocol.bedrock.codec.v557.serializer.SetEntityDataSerializer_v557;
import org.cloudburstmc.protocol.bedrock.codec.v567.serializer.CommandRequestSerializer_v567;
import org.cloudburstmc.protocol.bedrock.codec.v630.serializer.SetPlayerInventoryOptionsSerializer_v360;
import org.cloudburstmc.protocol.bedrock.codec.v662.serializer.SetEntityMotionSerializer_v662; import org.cloudburstmc.protocol.bedrock.codec.v662.serializer.SetEntityMotionSerializer_v662;
import org.cloudburstmc.protocol.bedrock.codec.v685.serializer.TextSerializer_v685;
import org.cloudburstmc.protocol.bedrock.data.inventory.InventoryLayout;
import org.cloudburstmc.protocol.bedrock.data.inventory.InventoryTabLeft;
import org.cloudburstmc.protocol.bedrock.data.inventory.InventoryTabRight;
import org.cloudburstmc.protocol.bedrock.packet.AnvilDamagePacket; import org.cloudburstmc.protocol.bedrock.packet.AnvilDamagePacket;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.BossEventPacket; import org.cloudburstmc.protocol.bedrock.packet.BossEventPacket;
@ -48,11 +55,14 @@ import org.cloudburstmc.protocol.bedrock.packet.ClientCacheStatusPacket;
import org.cloudburstmc.protocol.bedrock.packet.ClientCheatAbilityPacket; import org.cloudburstmc.protocol.bedrock.packet.ClientCheatAbilityPacket;
import org.cloudburstmc.protocol.bedrock.packet.ClientToServerHandshakePacket; import org.cloudburstmc.protocol.bedrock.packet.ClientToServerHandshakePacket;
import org.cloudburstmc.protocol.bedrock.packet.CodeBuilderSourcePacket; import org.cloudburstmc.protocol.bedrock.packet.CodeBuilderSourcePacket;
import org.cloudburstmc.protocol.bedrock.packet.CommandRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.CraftingEventPacket; import org.cloudburstmc.protocol.bedrock.packet.CraftingEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.CreatePhotoPacket; import org.cloudburstmc.protocol.bedrock.packet.CreatePhotoPacket;
import org.cloudburstmc.protocol.bedrock.packet.DebugInfoPacket; import org.cloudburstmc.protocol.bedrock.packet.DebugInfoPacket;
import org.cloudburstmc.protocol.bedrock.packet.EditorNetworkPacket; import org.cloudburstmc.protocol.bedrock.packet.EditorNetworkPacket;
import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket;
import org.cloudburstmc.protocol.bedrock.packet.EntityFallPacket; import org.cloudburstmc.protocol.bedrock.packet.EntityFallPacket;
import org.cloudburstmc.protocol.bedrock.packet.FilterTextPacket;
import org.cloudburstmc.protocol.bedrock.packet.GameTestRequestPacket; import org.cloudburstmc.protocol.bedrock.packet.GameTestRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket; import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
@ -74,10 +84,12 @@ import org.cloudburstmc.protocol.bedrock.packet.ScriptMessagePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetPlayerInventoryOptionsPacket;
import org.cloudburstmc.protocol.bedrock.packet.SettingsCommandPacket; import org.cloudburstmc.protocol.bedrock.packet.SettingsCommandPacket;
import org.cloudburstmc.protocol.bedrock.packet.SimpleEventPacket; import org.cloudburstmc.protocol.bedrock.packet.SimpleEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.SubChunkRequestPacket; import org.cloudburstmc.protocol.bedrock.packet.SubChunkRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.SubClientLoginPacket; import org.cloudburstmc.protocol.bedrock.packet.SubClientLoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.cloudburstmc.protocol.bedrock.packet.TickSyncPacket; import org.cloudburstmc.protocol.bedrock.packet.TickSyncPacket;
import org.cloudburstmc.protocol.common.util.VarInts; import org.cloudburstmc.protocol.common.util.VarInts;
@ -136,6 +148,84 @@ class CodecProcessor {
} }
}; };
private static final BedrockPacketSerializer<SetPlayerInventoryOptionsPacket> SET_PLAYER_INVENTORY_OPTIONS_SERIALIZER = new SetPlayerInventoryOptionsSerializer_v360() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, SetPlayerInventoryOptionsPacket packet) {
int leftTabIndex = VarInts.readInt(buffer);
int rightTabIndex = VarInts.readInt(buffer);
packet.setLeftTab(leftTabIndex >= 0 && leftTabIndex < InventoryTabLeft.VALUES.length ? InventoryTabLeft.VALUES[leftTabIndex] : InventoryTabLeft.NONE);
packet.setRightTab(rightTabIndex >= 0 && rightTabIndex < InventoryTabRight.VALUES.length ? InventoryTabRight.VALUES[rightTabIndex] : InventoryTabRight.NONE);
packet.setFiltering(buffer.readBoolean());
int layoutIndex = VarInts.readInt(buffer);
packet.setLayout(layoutIndex >= 0 && layoutIndex < InventoryLayout.VALUES.length ? InventoryLayout.VALUES[layoutIndex] : InventoryLayout.NONE);
int craftingLayoutIndex = VarInts.readInt(buffer);
packet.setCraftingLayout(craftingLayoutIndex >= 0 && craftingLayoutIndex < InventoryLayout.VALUES.length ? InventoryLayout.VALUES[craftingLayoutIndex] : InventoryLayout.NONE);
}
};
private static final BedrockPacketSerializer<FilterTextPacket> FILTER_TEXT = new FilterTextSerializer_v422() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, FilterTextPacket packet) {
packet.setText(helper.readStringMaxLen(buffer, 513));
packet.setFromServer(buffer.readBoolean());
}
};
private static final BedrockPacketSerializer<CommandRequestPacket> COMMAND_REQUEST_SERIALIZER = new CommandRequestSerializer_v567() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, CommandRequestPacket packet) {
packet.setCommand(helper.readStringMaxLen(buffer, 513));
packet.setCommandOriginData(helper.readCommandOrigin(buffer));
packet.setInternal(buffer.readBoolean());
packet.setVersion(VarInts.readInt(buffer));
}
};
private static final BedrockPacketSerializer<TextPacket> TEXT_SERIALIZER_V554 = new TextSerializer_v554() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, TextPacket packet) {
TextPacket.Type type = TextPacket.Type.values()[buffer.readUnsignedByte()];
packet.setType(type);
packet.setNeedsTranslation(buffer.readBoolean());
if (type == TextPacket.Type.CHAT) {
packet.setSourceName(helper.readString(buffer));
//The client does not send more than 512 characters, and we do not need to decode other TextPacket.Type
packet.setMessage(helper.readStringMaxLen(buffer, 513));
} else {
throw new IllegalArgumentException("Unsupported TextType " + type);
}
packet.setXuid(helper.readString(buffer));
packet.setPlatformChatId(helper.readString(buffer));
}
};
private static final BedrockPacketSerializer<TextPacket> TEXT_SERIALIZER = new TextSerializer_v685() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, TextPacket packet) {
TextPacket.Type type = TextPacket.Type.values()[buffer.readUnsignedByte()];
packet.setType(type);
packet.setNeedsTranslation(buffer.readBoolean());
if (type == TextPacket.Type.CHAT) {
packet.setSourceName(helper.readString(buffer));
//The client does not send more than 512 characters, and we do not need to decode other TextPacket.Type
packet.setMessage(helper.readStringMaxLen(buffer, 513));
} else {
throw new IllegalArgumentException("Unsupported TextType " + type);
}
packet.setXuid(helper.readString(buffer));
packet.setPlatformChatId(helper.readString(buffer));
packet.setFilteredMessage(helper.readString(buffer));
}
};
/** /**
* Serializer that does nothing when trying to deserialize BossEventPacket since it is not used from the client. * Serializer that does nothing when trying to deserialize BossEventPacket since it is not used from the client.
*/ */
@ -181,15 +271,6 @@ class CodecProcessor {
} }
}; };
/**
* Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client for codec v291.
*/
private static final BedrockPacketSerializer<SetEntityMotionPacket> SET_ENTITY_MOTION_SERIALIZER_V291 = new SetEntityMotionSerializer_v291() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, SetEntityMotionPacket packet) {
}
};
/** /**
* Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client for codec v662. * Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client for codec v662.
*/ */
@ -251,6 +332,7 @@ class CodecProcessor {
.updateSerializer(SettingsCommandPacket.class, IGNORED_SERIALIZER) .updateSerializer(SettingsCommandPacket.class, IGNORED_SERIALIZER)
.updateSerializer(AnvilDamagePacket.class, IGNORED_SERIALIZER) .updateSerializer(AnvilDamagePacket.class, IGNORED_SERIALIZER)
.updateSerializer(RefreshEntitlementsPacket.class, IGNORED_SERIALIZER) .updateSerializer(RefreshEntitlementsPacket.class, IGNORED_SERIALIZER)
.updateSerializer(EmoteListPacket.class, IGNORED_SERIALIZER)
// Illegal when serverbound due to Geyser specific setup // Illegal when serverbound due to Geyser specific setup
.updateSerializer(InventoryContentPacket.class, INVENTORY_CONTENT_SERIALIZER) .updateSerializer(InventoryContentPacket.class, INVENTORY_CONTENT_SERIALIZER)
.updateSerializer(InventorySlotPacket.class, INVENTORY_SLOT_SERIALIZER) .updateSerializer(InventorySlotPacket.class, INVENTORY_SLOT_SERIALIZER)
@ -273,10 +355,19 @@ class CodecProcessor {
.updateSerializer(SimpleEventPacket.class, IGNORED_SERIALIZER) .updateSerializer(SimpleEventPacket.class, IGNORED_SERIALIZER)
.updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER); .updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER);
if (codec.getProtocolVersion() < 685) { if (codec.getProtocolVersion() < 685) {
// Ignored bidirectional packets // Ignored bidirectional packets
codecBuilder.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER); codecBuilder.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER);
} }
codecBuilder.updateSerializer(FilterTextPacket.class, FILTER_TEXT);
codecBuilder.updateSerializer(CommandRequestPacket.class, COMMAND_REQUEST_SERIALIZER);
codecBuilder.updateSerializer(SetPlayerInventoryOptionsPacket.class, SET_PLAYER_INVENTORY_OPTIONS_SERIALIZER);
if (codec.getProtocolVersion() >= 685) {
codecBuilder.updateSerializer(TextPacket.class, TEXT_SERIALIZER);
} else {
codecBuilder.updateSerializer(TextPacket.class, TEXT_SERIALIZER_V554);
}
return codecBuilder.build(); return codecBuilder.build();
} }

View file

@ -39,13 +39,16 @@ import org.geysermc.geyser.session.GeyserSession;
public class LoggingPacketHandler implements BedrockPacketHandler { public class LoggingPacketHandler implements BedrockPacketHandler {
protected final GeyserImpl geyser; protected final GeyserImpl geyser;
protected final GeyserSession session; protected final GeyserSession session;
protected final PacketCooldownManager cooldownHandler;
LoggingPacketHandler(GeyserImpl geyser, GeyserSession session) { LoggingPacketHandler(GeyserImpl geyser, GeyserSession session) {
this.geyser = geyser; this.geyser = geyser;
this.session = session; this.session = session;
this.cooldownHandler = new PacketCooldownManager(session);
} }
PacketSignal defaultHandler(BedrockPacket packet) { PacketSignal defaultHandler(BedrockPacket packet) {
this.cooldownHandler.handle(packet);
geyser.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName()); geyser.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName());
return PacketSignal.HANDLED; return PacketSignal.HANDLED;
} }

View file

@ -0,0 +1,106 @@
package org.geysermc.geyser.network;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.BlockPickRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.BookEditPacket;
import org.cloudburstmc.protocol.bedrock.packet.CommandRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.EntityPickRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.FilterTextPacket;
import org.cloudburstmc.protocol.bedrock.packet.LecternUpdatePacket;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.RequestNetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackChunkRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackClientResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.geysermc.geyser.session.GeyserSession;
import java.util.Map;
public class PacketCooldownManager {
private final GeyserSession session;
private final Map<String, CooldownSettings> packetCooldownSettings = new Object2ObjectOpenHashMap<>();
private final Map<String, CooldownTracker> activeCooldowns = new Object2ObjectOpenHashMap<>();
public PacketCooldownManager(GeyserSession session) {
this.session = session;
setPacketCooldown(LoginPacket.class, -1, 2);
setPacketCooldown(ResourcePackClientResponsePacket.class, -1, 4);
setPacketCooldown(ResourcePackChunkRequestPacket.class, -1, 0);
setPacketCooldown(RequestNetworkSettingsPacket.class, -1, 2);
setPacketCooldown(TextPacket.class, 1000, 50);
setPacketCooldown(CommandRequestPacket.class, 1000, 50);
setPacketCooldown(ModalFormResponsePacket.class, 1000, 50);
setPacketCooldown(BlockEntityDataPacket.class, 1000, 40);
setPacketCooldown(SetLocalPlayerAsInitializedPacket.class, 1000, 40);
setPacketCooldown(BlockPickRequestPacket.class, 1000, 40);
setPacketCooldown(EntityPickRequestPacket.class, 1000, 40);
setPacketCooldown(BookEditPacket.class, 1000, 40);
setPacketCooldown(FilterTextPacket.class, 1000, 40);
setPacketCooldown(LecternUpdatePacket.class, 1000, 10);
}
public void setPacketCooldown(Class<? extends BedrockPacket> packetClass, int cooldownMillis, int maxCount) {
packetCooldownSettings.put(packetClass.getSimpleName(), new CooldownSettings(cooldownMillis, maxCount));
}
private boolean isCooldown(BedrockPacket packet) {
String packetName = packet.getClass().getSimpleName();
CooldownSettings settings = packetCooldownSettings.get(packetName);
if (settings == null) return false;
CooldownTracker tracker = activeCooldowns.computeIfAbsent(packetName, k -> {
CooldownTracker newTracker = new CooldownTracker();
long cooldownMillis = settings.cooldownMillis();
if (cooldownMillis == -1) {
newTracker.setExpiryTime(-1);
} else {
newTracker.setExpiryTime(System.currentTimeMillis() + cooldownMillis);
}
return newTracker;
});
tracker.incrementCount();
if (tracker.getExpiryTime() != -1 && tracker.getExpiryTime() <= System.currentTimeMillis()) {
activeCooldowns.remove(packetName);
return false;
}
return tracker.getCount() >= settings.maxCount();
}
public void handle(BedrockPacket packet) {
String packetName = packet.getClass().getSimpleName();
if (!isCooldown(packet)) return;
if (session.getGeyser().getConfig().isDebugMode()) {
CooldownTracker tracker = activeCooldowns.get(packetName);
String message = session.getSocketAddress().getAddress().toString() + " -> Attempted to send too many packets " + packetName + " count " + tracker.getCount();
if (session.isLoggedIn()) {
message += " by user " + session.bedrockUsername();
}
session.getGeyser().getLogger().debug(message);
}
session.disconnect("many Packets " + packetName);
}
private record CooldownSettings(int cooldownMillis, int maxCount) {
}
@Getter
private class CooldownTracker {
private long count;
@Setter
private long expiryTime;
public void incrementCount() {
this.count++;
}
}
}

View file

@ -37,6 +37,7 @@ import org.cloudburstmc.protocol.bedrock.netty.codec.compression.CompressionStra
import org.cloudburstmc.protocol.bedrock.netty.codec.compression.SimpleCompressionStrategy; import org.cloudburstmc.protocol.bedrock.netty.codec.compression.SimpleCompressionStrategy;
import org.cloudburstmc.protocol.bedrock.netty.codec.compression.ZlibCompression; import org.cloudburstmc.protocol.bedrock.netty.codec.compression.ZlibCompression;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.LecternUpdatePacket;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket; import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket; import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
@ -73,15 +74,12 @@ import org.geysermc.geyser.util.VersionCheckUtils;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel; import java.nio.channels.SeekableByteChannel;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.OptionalInt; import java.util.OptionalInt;
public class UpstreamPacketHandler extends LoggingPacketHandler { public class UpstreamPacketHandler extends LoggingPacketHandler {
private boolean networkSettingsRequested = false; private boolean networkSettingsRequested = false;
private final Deque<String> packsToSent = new ArrayDeque<>();
private final CompressionStrategy compressionStrategy; private final CompressionStrategy compressionStrategy;
private SessionLoadResourcePacksEventImpl resourcePackLoadEvent; private SessionLoadResourcePacksEventImpl resourcePackLoadEvent;
@ -101,6 +99,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override @Override
PacketSignal defaultHandler(BedrockPacket packet) { PacketSignal defaultHandler(BedrockPacket packet) {
this.cooldownHandler.handle(packet);
return translateAndDefault(packet); return translateAndDefault(packet);
} }
@ -150,6 +149,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override @Override
public PacketSignal handle(RequestNetworkSettingsPacket packet) { public PacketSignal handle(RequestNetworkSettingsPacket packet) {
this.cooldownHandler.handle(packet);
if (!setCorrectCodec(packet.getProtocolVersion())) { if (!setCorrectCodec(packet.getProtocolVersion())) {
return PacketSignal.HANDLED; return PacketSignal.HANDLED;
} }
@ -169,6 +169,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override @Override
public PacketSignal handle(LoginPacket loginPacket) { public PacketSignal handle(LoginPacket loginPacket) {
this.cooldownHandler.handle(loginPacket);
if (geyser.isShuttingDown() || geyser.isReloading()) { if (geyser.isShuttingDown() || geyser.isReloading()) {
// Don't allow new players in if we're no longer operating // Don't allow new players in if we're no longer operating
session.disconnect(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.kick.message")); session.disconnect(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.kick.message"));
@ -220,6 +221,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override @Override
public PacketSignal handle(ResourcePackClientResponsePacket packet) { public PacketSignal handle(ResourcePackClientResponsePacket packet) {
this.cooldownHandler.handle(packet);
if (packet.getPackIds().size() > this.resourcePackLoadEvent.getPacks().size()) {
session.disconnect("Packet " + packet.getClass().getSimpleName() + " PackIds max count");
return PacketSignal.HANDLED;
}
switch (packet.getStatus()) { switch (packet.getStatus()) {
case COMPLETED: case COMPLETED:
if (geyser.getConfig().getRemote().authType() != AuthType.ONLINE) { if (geyser.getConfig().getRemote().authType() != AuthType.ONLINE) {
@ -232,8 +238,24 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
break; break;
case SEND_PACKS: case SEND_PACKS:
packsToSent.addAll(packet.getPackIds()); int chunkIndex = 1;
sendPackDataInfo(packsToSent.pop()); for (String id : packet.getPackIds()) {
String[] packID = id.split("_", 2);
if (packID.length != 2) {
session.disconnect("Invalid packID id: " + id);
break;
}
ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packID[0]);
if (pack == null) {
session.disconnect("Invalid request unknown pack " + packID[0] + ", available packs: " + this.resourcePackLoadEvent.getPacks().keySet());
break;
}
sendPackDataInfo(id);
chunkIndex += (int) ((this.resourcePackLoadEvent.getPacks().get(packID[0]).codec().size() + GeyserResourcePack.CHUNK_SIZE) / GeyserResourcePack.CHUNK_SIZE);
}
cooldownHandler.setPacketCooldown(ResourcePackChunkRequestPacket.class, -1, chunkIndex);
break; break;
case HAVE_ALL_PACKS: case HAVE_ALL_PACKS:
@ -268,6 +290,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override @Override
public PacketSignal handle(ModalFormResponsePacket packet) { public PacketSignal handle(ModalFormResponsePacket packet) {
this.cooldownHandler.handle(packet);
session.executeInEventLoop(() -> session.getFormCache().handleResponse(packet)); session.executeInEventLoop(() -> session.getFormCache().handleResponse(packet));
return PacketSignal.HANDLED; return PacketSignal.HANDLED;
} }
@ -308,8 +331,13 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override @Override
public PacketSignal handle(ResourcePackChunkRequestPacket packet) { public PacketSignal handle(ResourcePackChunkRequestPacket packet) {
this.cooldownHandler.handle(packet);
ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket(); ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket();
ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packet.getPackId().toString()); ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packet.getPackId().toString());
if (pack == null) {
session.disconnect("Invalid request for chunk " + packet.getChunkIndex() + " of unknown pack " + packet.getPackId() + ", available packs: " + this.resourcePackLoadEvent.getPacks().keySet());
return PacketSignal.HANDLED;
}
PackCodec codec = pack.codec(); PackCodec codec = pack.codec();
data.setChunkIndex(packet.getChunkIndex()); data.setChunkIndex(packet.getChunkIndex());
@ -318,6 +346,10 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
data.setPackId(packet.getPackId()); data.setPackId(packet.getPackId());
int offset = packet.getChunkIndex() * GeyserResourcePack.CHUNK_SIZE; int offset = packet.getChunkIndex() * GeyserResourcePack.CHUNK_SIZE;
if (offset < 0 || offset >= codec.size()) {
session.disconnect("Invalid out-of-bounds request for chunk " + packet.getChunkIndex() + " of " + packet.getPackId() + " offset " + offset + ", file size " + codec.size());
return PacketSignal.HANDLED;
}
long remainingSize = codec.size() - offset; long remainingSize = codec.size() - offset;
byte[] packData = new byte[(int) MathUtils.constrain(remainingSize, 0, GeyserResourcePack.CHUNK_SIZE)]; byte[] packData = new byte[(int) MathUtils.constrain(remainingSize, 0, GeyserResourcePack.CHUNK_SIZE)];
@ -332,17 +364,12 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
session.sendUpstreamPacket(data); session.sendUpstreamPacket(data);
// Check if it is the last chunk and send next pack in queue when available.
if (remainingSize <= GeyserResourcePack.CHUNK_SIZE && !packsToSent.isEmpty()) {
sendPackDataInfo(packsToSent.pop());
}
return PacketSignal.HANDLED; return PacketSignal.HANDLED;
} }
private void sendPackDataInfo(String id) { private void sendPackDataInfo(String id) {
ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket(); ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket();
String[] packID = id.split("_"); String[] packID = id.split("_", 2);
ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packID[0]); ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packID[0]);
PackCodec codec = pack.codec(); PackCodec codec = pack.codec();
ResourcePackManifest.Header header = pack.manifest().header(); ResourcePackManifest.Header header = pack.manifest().header();

View file

@ -236,7 +236,7 @@ public final class GeyserServer {
.group(group, childGroup) .group(group, childGroup)
.option(RakChannelOption.RAK_HANDLE_PING, true) .option(RakChannelOption.RAK_HANDLE_PING, true)
.option(RakChannelOption.RAK_MAX_MTU, this.geyser.getConfig().getMtu()) .option(RakChannelOption.RAK_MAX_MTU, this.geyser.getConfig().getMtu())
.option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit) .option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit) // FIXME When using Velocity, the proxy server gets kicked for exceeding the limit, even if this value is increased tenfold. With an online count of 1000, this happens about 40 times a day at a value of 450. It is unclear why it detects the server's IP with Velocity, rather than the players' IPs.
.option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit) .option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit)
.option(RakChannelOption.RAK_SEND_COOKIE, rakSendCookie) .option(RakChannelOption.RAK_SEND_COOKIE, rakSendCookie)
.childHandler(serverInitializer); .childHandler(serverInitializer);

View file

@ -118,7 +118,6 @@ import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.api.network.RemoteServer;
import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.GeyserEntityData; import org.geysermc.geyser.entity.GeyserEntityData;
@ -346,7 +345,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
// Exposed for GeyserConnect usage // Exposed for GeyserConnect usage
protected boolean sentSpawnPacket; protected boolean sentSpawnPacket;
@Getter
private boolean loggedIn; private boolean loggedIn;
@Getter
private boolean loggingIn; private boolean loggingIn;
@Setter @Setter
@ -587,8 +588,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter @Setter
private boolean waitingForStatistics = false; private boolean waitingForStatistics = false;
private final Set<UUID> emotes;
/** /**
* Whether advanced tooltips will be added to the player's items. * Whether advanced tooltips will be added to the player's items.
*/ */
@ -676,13 +675,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.spawned = false; this.spawned = false;
this.loggedIn = false; this.loggedIn = false;
if (geyser.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) {
this.emotes = new HashSet<>();
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
} else {
this.emotes = null;
}
this.remoteServer = geyser.defaultRemoteServer(); this.remoteServer = geyser.defaultRemoteServer();
} }
@ -1938,23 +1930,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.statistics.putAll(statistics); this.statistics.putAll(statistics);
} }
public void refreshEmotes(List<UUID> emotes) {
this.emotes.addAll(emotes);
for (GeyserSession player : geyser.getSessionManager().getSessions().values()) {
List<UUID> pieces = new ArrayList<>();
for (UUID piece : emotes) {
if (!player.getEmotes().contains(piece)) {
pieces.add(piece);
}
player.getEmotes().add(piece);
}
EmoteListPacket emoteList = new EmoteListPacket();
emoteList.setRuntimeEntityId(player.getPlayerEntity().getGeyserId());
emoteList.getPieceIds().addAll(pieces);
player.sendUpstreamPacket(emoteList);
}
}
public boolean canUseCommandBlocks() { public boolean canUseCommandBlocks() {
return instabuild && opPermissionLevel >= 2; return instabuild && opPermissionLevel >= 2;
} }

View file

@ -25,18 +25,21 @@
package org.geysermc.geyser.session.cache; package org.geysermc.geyser.session.cache;
import org.cloudburstmc.protocol.bedrock.packet.ModalFormRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.cloudburstmc.protocol.bedrock.data.ModalFormCancelReason;
import org.cloudburstmc.protocol.bedrock.packet.ModalFormRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.SimpleForm; import org.geysermc.cumulus.form.SimpleForm;
import org.geysermc.cumulus.form.impl.FormDefinitions; import org.geysermc.cumulus.form.impl.FormDefinitions;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import java.util.LinkedList;
import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -52,11 +55,22 @@ public class FormCache {
private final FormDefinitions formDefinitions = FormDefinitions.instance(); private final FormDefinitions formDefinitions = FormDefinitions.instance();
private final AtomicInteger formIdCounter = new AtomicInteger(0); private final AtomicInteger formIdCounter = new AtomicInteger(0);
private final Int2ObjectMap<Form> forms = new Int2ObjectOpenHashMap<>(); private final Int2ObjectMap<Form> forms = new Int2ObjectOpenHashMap<>();
LinkedList<Integer> formsOrderList = new LinkedList<>();
private final GeyserSession session; private final GeyserSession session;
public int addForm(Form form) { public int addForm(Form form) {
int formId = formIdCounter.getAndIncrement(); int formId = formIdCounter.getAndIncrement();
forms.put(formId, form); forms.put(formId, form);
formsOrderList.add(formId);
if (formsOrderList.size() > 50) {
int removeFormId = formsOrderList.getFirst();
ModalFormResponsePacket packet = new ModalFormResponsePacket();
packet.setFormId(removeFormId);
packet.setCancelReason(Optional.of(ModalFormCancelReason.USER_CLOSED));
packet.setFormData(null);
this.handleResponse(packet);
}
return formId; return formId;
} }
@ -96,6 +110,7 @@ public class FormCache {
public void handleResponse(ModalFormResponsePacket response) { public void handleResponse(ModalFormResponsePacket response) {
Form form = forms.remove(response.getFormId()); Form form = forms.remove(response.getFormId());
formsOrderList.remove((Integer) response.getFormId());
if (form == null) { if (form == null) {
return; return;
} }

View file

@ -39,8 +39,7 @@ public class BedrockCommandRequestTranslator extends PacketTranslator<CommandReq
@Override @Override
public void translate(GeyserSession session, CommandRequestPacket packet) { public void translate(GeyserSession session, CommandRequestPacket packet) {
String command = MessageTranslator.convertToPlainText(packet.getCommand()); handleCommand(session, MessageTranslator.normalizeSpace(MessageTranslator.convertToPlainText(packet.getCommand())).substring(1));
handleCommand(session, MessageTranslator.normalizeSpace(command).substring(1));
} }
static void handleCommand(GeyserSession session, String command) { static void handleCommand(GeyserSession session, String command) {

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.protocol.bedrock;
import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@Translator(packet = EmoteListPacket.class)
public class BedrockEmoteListTranslator extends PacketTranslator<EmoteListPacket> {
@Override
public void translate(GeyserSession session, EmoteListPacket packet) {
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) {
return;
}
session.refreshEmotes(packet.getPieceIds());
}
}