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:
@@ -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();
|
||||
|
||||
@@ -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 "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user