1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-27 18:59:17 +00:00

Merge branch 'master' into feature/1.21.6

This commit is contained in:
chris
2025-06-23 16:18:03 +02:00
committed by GitHub
10 changed files with 120 additions and 16 deletions

View File

@@ -42,6 +42,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.util.EncryptionUtils;
import org.geysermc.api.Geyser;
import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormBuilder;
@@ -215,6 +216,9 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
}
public void initialize() {
// Setup encryption early so we don't start if we can't auth
EncryptionUtils.getMojangPublicKey();
long startupTime = System.currentTimeMillis();
GeyserLogger logger = bootstrap.getGeyserLogger();

View File

@@ -36,6 +36,7 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.event.bedrock.SessionLoadResourcePacksEvent;
import org.geysermc.geyser.api.pack.ResourcePack;
import org.geysermc.geyser.api.pack.ResourcePackManifest;
import org.geysermc.geyser.api.pack.UrlPackCodec;
import org.geysermc.geyser.api.pack.exception.ResourcePackException;
import org.geysermc.geyser.api.pack.option.PriorityOption;
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
@@ -205,7 +206,7 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE
ResourcePackManifest.Header header = pack.manifest().header();
entries.add(new ResourcePacksInfoPacket.Entry(
header.uuid(), header.version().toString(), pack.codec().size(), pack.contentKey(),
subpackName(pack), header.uuid().toString(), false, false, false, subpackName(pack))
subpackName(pack), header.uuid().toString(), false, false, false, cdnUrl(pack))
);
}
@@ -229,4 +230,11 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE
private String subpackName(GeyserResourcePack pack) {
return value(pack.uuid(), ResourcePackOption.Type.SUBPACK, "");
}
private String cdnUrl(GeyserResourcePack pack) {
if (pack.codec() instanceof UrlPackCodec urlPackCodec) {
return urlPackCodec.url();
}
return "";
}
}

View File

@@ -52,6 +52,7 @@ import org.cloudburstmc.protocol.bedrock.packet.ResourcePacksInfoPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket;
import org.cloudburstmc.protocol.common.PacketSignal;
import org.cloudburstmc.protocol.common.util.Zlib;
import org.geysermc.api.util.BedrockPlatform;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent;
@@ -80,7 +81,10 @@ import java.nio.channels.SeekableByteChannel;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.OptionalInt;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
public class UpstreamPacketHandler extends LoggingPacketHandler {
@@ -88,6 +92,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
private final Deque<String> packsToSend = new ArrayDeque<>();
private final CompressionStrategy compressionStrategy;
// Avoid overloading consoles when downloading larger resource packs
private static final int PACKET_SEND_DELAY = 4 * 50;
private final Queue<ResourcePackChunkRequestPacket> chunkRequestQueue = new ConcurrentLinkedQueue<>();
private boolean currentlySendingChunks = false;
private SessionLoadResourcePacksEventImpl resourcePackLoadEvent;
public UpstreamPacketHandler(GeyserImpl geyser, GeyserSession session) {
@@ -304,13 +313,35 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override
public PacketSignal handle(ResourcePackChunkRequestPacket packet) {
// Resolve some console pack downloading issues.
// See <https://github.com/PowerNukkitX/PowerNukkitX/pull/1997> for reference
chunkRequestQueue.add(packet);
if (isConsole()) {
if (!currentlySendingChunks) {
currentlySendingChunks = true;
processNextChunk();
}
} else {
processNextChunk();
}
return PacketSignal.HANDLED;
}
public void processNextChunk() {
ResourcePackChunkRequestPacket packet = chunkRequestQueue.poll();
if (packet == null) {
currentlySendingChunks = false;
return;
}
ResourcePackHolder holder = this.resourcePackLoadEvent.getPacks().get(packet.getPackId());
if (holder == null) {
GeyserImpl.getInstance().getLogger().debug("Client {0} tried to request pack id {1} not sent to it!",
session.bedrockUsername(), packet.getPackId());
currentlySendingChunks = false;
session.disconnect("disconnectionScreen.resourcePack");
return PacketSignal.HANDLED;
return;
}
ResourcePack pack = holder.pack();
@@ -323,7 +354,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
if (!resourcePackLoadEvent.value(pack.uuid(), ResourcePackOption.Type.FALLBACK, true)) {
session.disconnect("Unable to provide downloaded resource pack. Contact an administrator!");
return PacketSignal.HANDLED;
currentlySendingChunks = false;
return;
}
}
@@ -346,14 +378,19 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
data.setData(Unpooled.wrappedBuffer(packData));
session.sendUpstreamPacket(data);
if (isConsole()) {
// Also flushes packets
// Avoids bursting slower / delayed clients
session.sendUpstreamPacketImmediately(data);
GeyserImpl.getInstance().getScheduledThread().schedule(this::processNextChunk, PACKET_SEND_DELAY, TimeUnit.MILLISECONDS);
} else {
session.sendUpstreamPacket(data);
}
// Check if it is the last chunk and send next pack in queue when available.
if (remainingSize <= GeyserResourcePack.CHUNK_SIZE && !packsToSend.isEmpty()) {
sendPackDataInfo(packsToSend.pop());
}
return PacketSignal.HANDLED;
}
private void sendPackDataInfo(String id) {
@@ -401,4 +438,9 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
session.sendUpstreamPacket(data);
}
private boolean isConsole() {
BedrockPlatform platform = session.platform();
return platform == BedrockPlatform.PS4 || platform == BedrockPlatform.XBOX || platform == BedrockPlatform.NX;
}
}

View File

@@ -41,7 +41,7 @@ public record GeyserResourcePack(
/**
* The size of each chunk to use when sending the resource packs to clients in bytes
*/
public static final int CHUNK_SIZE = 102400;
public static final int CHUNK_SIZE = 1024 * 256;
public static class Builder implements ResourcePack.Builder {

View File

@@ -115,8 +115,11 @@ public class SkinManager {
String xuid = "";
GeyserSession playerSession = GeyserImpl.getInstance().connectionByUuid(uuid);
// Prefer looking up xuid using the session to catch linked players
if (playerSession != null) {
xuid = playerSession.getAuthData().xuid();
} else if (uuid.version() == 0) {
xuid = Long.toString(uuid.getLeastSignificantBits());
}
PlayerListPacket.Entry entry;

View File

@@ -36,6 +36,6 @@ public class JavaDisguisedChatTranslator extends PacketTranslator<ClientboundDis
@Override
public void translate(GeyserSession session, ClientboundDisguisedChatPacket packet) {
MessageTranslator.handleChatPacket(session, packet.getMessage(), packet.getChatType(), packet.getTargetName(), packet.getName());
MessageTranslator.handleChatPacket(session, packet.getMessage(), packet.getChatType(), packet.getTargetName(), packet.getName(), null);
}
}

View File

@@ -38,6 +38,6 @@ public class JavaPlayerChatTranslator extends PacketTranslator<ClientboundPlayer
@Override
public void translate(GeyserSession session, ClientboundPlayerChatPacket packet) {
Component message = packet.getUnsignedContent() == null ? Component.text(packet.getContent()) : packet.getUnsignedContent();
MessageTranslator.handleChatPacket(session, message, packet.getChatType(), packet.getTargetName(), packet.getName());
MessageTranslator.handleChatPacket(session, message, packet.getChatType(), packet.getTargetName(), packet.getName(), packet.getSender());
}
}

View File

@@ -58,6 +58,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatTypeDecoration;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
public class MessageTranslator {
@@ -367,11 +368,25 @@ public class MessageTranslator {
return PlainTextComponentSerializer.plainText().serialize(messageComponent);
}
public static void handleChatPacket(GeyserSession session, Component message, Holder<ChatType> chatTypeHolder, Component targetName, Component sender) {
public static void handleChatPacket(GeyserSession session, Component message, Holder<ChatType> chatTypeHolder, Component targetName, Component sender, @Nullable UUID senderUuid) {
TextPacket textPacket = new TextPacket();
textPacket.setPlatformChatId("");
textPacket.setSourceName("");
textPacket.setXuid(session.getAuthData().xuid());
if (senderUuid == null) {
textPacket.setXuid(session.getAuthData().xuid());
} else {
String xuid = "";
GeyserSession playerSession = GeyserImpl.getInstance().connectionByUuid(senderUuid);
// Prefer looking up xuid using the session to catch linked players
if (playerSession != null) {
xuid = playerSession.getAuthData().xuid();
} else if (senderUuid.version() == 0) {
xuid = Long.toString(senderUuid.getLeastSignificantBits());
}
textPacket.setXuid(xuid);
}
textPacket.setType(TextPacket.Type.CHAT);
textPacket.setNeedsTranslation(false);

View File

@@ -33,8 +33,18 @@ import org.geysermc.geyser.GeyserLogger;
import javax.naming.directory.Attribute;
import javax.naming.directory.InitialDirContext;
import java.io.*;
import java.net.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -44,6 +54,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import java.util.zip.ZipFile;
public class WebUtils {
@@ -183,6 +194,27 @@ public class WebUtils {
.formatted(url, downloadSize, size));
}
try {
boolean shouldDeleteEnclosing = false;
var originalZip = downloadLocation;
try (ZipFile zip = new ZipFile(downloadLocation.toFile())) {
// This can (or should???) contain a zip
if (zip.stream().allMatch(name -> name.getName().endsWith(".zip"))) {
// Unzip the pack, as that's what we're after
downloadLocation = REMOTE_PACK_CACHE.resolve(url.hashCode() + "_" + System.currentTimeMillis() + "_unzipped.zip");
Files.copy(zip.getInputStream(zip.entries().nextElement()), downloadLocation, StandardCopyOption.REPLACE_EXISTING);
shouldDeleteEnclosing = true;
}
} finally {
if (shouldDeleteEnclosing) {
// We don't need the original zip anymore
Files.delete(originalZip);
}
}
} catch (IOException e) {
throw new IllegalArgumentException("Encountered exception while reading downloaded resource pack at url: %s".formatted(url), e);
}
try {
Files.write(
packMetadata,