From 65b96208fb3359765f668dc10205b70c0035c454 Mon Sep 17 00:00:00 2001 From: Aurora Date: Mon, 3 Nov 2025 16:51:34 +0000 Subject: [PATCH 1/2] Add automatic downloading of the GeyserOptionalPack --- .../java/org/geysermc/geyser/GeyserImpl.java | 4 + .../geyser/pack/GeyserOptionalPackUtils.java | 97 +++++++++++++++++++ .../org/geysermc/geyser/util/WebUtils.java | 14 ++- 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/pack/GeyserOptionalPackUtils.java diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 55c15297d..1aaaae061 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -57,6 +57,7 @@ import org.geysermc.floodgate.news.NewsItemAction; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.command.CommandSource; import org.geysermc.geyser.api.event.EventRegistrar; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPostReloadEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; @@ -81,6 +82,7 @@ import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.netty.GeyserServer; +import org.geysermc.geyser.pack.GeyserOptionalPackUtils; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.loader.ResourcePackLoader; @@ -274,6 +276,8 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { eventBus.subscribe(this, GeyserRegisterPermissionsEvent.class, Permissions::register); // Replace disconnect messages whenever necessary eventBus.subscribe(this, SessionDisconnectEventImpl.class, SessionDisconnectListener::onSessionDisconnect); + // Apply the GeyserOptionalPack if enabled + eventBus.subscribe(this, GeyserDefineResourcePacksEvent.class, GeyserOptionalPackUtils::register); startInstance(); diff --git a/core/src/main/java/org/geysermc/geyser/pack/GeyserOptionalPackUtils.java b/core/src/main/java/org/geysermc/geyser/pack/GeyserOptionalPackUtils.java new file mode 100644 index 000000000..6a3904afc --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/pack/GeyserOptionalPackUtils.java @@ -0,0 +1,97 @@ +package org.geysermc.geyser.pack; + +import com.google.gson.JsonObject; +import lombok.Getter; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent; +import org.geysermc.geyser.api.pack.PackCodec; +import org.geysermc.geyser.api.pack.ResourcePack; +import org.geysermc.geyser.api.pack.option.PriorityOption; +import org.geysermc.geyser.util.WebUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class GeyserOptionalPackUtils { + + private static final Path CACHE = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache"); + private static final Path PACK_PATH = CACHE.resolve("GeyserOptionalPack.mcpack"); + + private static final String METADATA_URL = "https://download.geysermc.org/v2/projects/geyseroptionalpack/versions/latest/builds/latest"; + private static final String DOWNLOAD_URL = METADATA_URL + "/downloads/geyseroptionalpack"; + + @Getter + private static boolean optionalPackLoaded = false; + + public static void register(GeyserDefineResourcePacksEvent event) { + GeyserImpl geyser = GeyserImpl.getInstance(); + + if (!geyser.config().gameplay().enableOptionalPack()) return; + + if (ensurePackIsPresent(geyser, PackCodec.path(PACK_PATH))) { + event.register(ResourcePack.create(PackCodec.path(PACK_PATH)), PriorityOption.HIGH); + optionalPackLoaded = true; + } else { + geyser.getLogger().error("The GeyserOptionalPack was not found! The pack will not be downloaded when players join."); + optionalPackLoaded = false; + } + } + + public static boolean ensurePackIsPresent(GeyserImpl geyser, PackCodec packCodec) { + boolean downloadRequired = false; + + if (Files.notExists(PACK_PATH)) downloadRequired = true; + else { + try { + IllegalStateException invalidMetadataException = new IllegalStateException("Metadata is missing or has invalid required properties which are required in order to check for updates."); + + JsonObject metadata = WebUtils.getJson(METADATA_URL); + if (!metadata.has("downloads") || !metadata.get("downloads").isJsonObject()) { + throw invalidMetadataException; + } + + JsonObject downloads = metadata.getAsJsonObject("downloads"); + if (!downloads.has("geyseroptionalpack") || !downloads.get("geyseroptionalpack").isJsonObject()) { + throw invalidMetadataException; + } + + JsonObject downloadMetadata = downloads.getAsJsonObject("geyseroptionalpack"); + if (!downloadMetadata.has("sha256") || !downloadMetadata.get("sha256").isJsonPrimitive()) { + throw invalidMetadataException; + } + + String remoteSha256 = downloadMetadata.get("sha256").getAsString(); + + byte[] hash = packCodec.sha256(); + StringBuilder sha256Builder = new StringBuilder(2 * hash.length); + + for (byte hashPart : hash) { + String hex = Integer.toHexString(0xFF & hashPart); + if (hex.length() == 1) sha256Builder.append('0'); + sha256Builder.append(hex); + } + String localSha256 = sha256Builder.toString(); + + downloadRequired = !remoteSha256.equals(localSha256); + if (downloadRequired) { + geyser.getLogger().info("New GeyserOptionalPack update has been found."); + } + } catch (IOException | IllegalStateException e) { + geyser.getLogger().error("Unable to check GeyserOptionalPack metadata! Pack will not update.", e); + } + } + + if (downloadRequired) { + geyser.getLogger().info("Downloading the GeyserOptionalPack..."); + try { + WebUtils.downloadFile(DOWNLOAD_URL, PACK_PATH); + geyser.getLogger().info("GeyserOptionalPack download successful!"); + } catch (RuntimeException e) { + geyser.getLogger().error("Failed to download the latest GeyserOptionalPack!", e); + } + } + + return Files.exists(PACK_PATH) && Files.isRegularFile(PACK_PATH); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java index 8843abf01..5b4cb2706 100644 --- a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java @@ -109,14 +109,24 @@ public class WebUtils { * @param fileLocation Location to save on disk */ public static void downloadFile(String reqURL, String fileLocation) { + downloadFile(reqURL, Paths.get(fileLocation)); + } + + /** + * Downloads a file from the given URL and saves it to disk + * + * @param reqURL File to fetch + * @param path Location to save on disk as a Path + */ + public static void downloadFile(String reqURL, Path path) { try { HttpURLConnection con = (HttpURLConnection) new URL(reqURL).openConnection(); con.setRequestProperty("User-Agent", getUserAgent()); checkResponseCode(con); InputStream in = con.getInputStream(); - Files.copy(in, Paths.get(fileLocation), StandardCopyOption.REPLACE_EXISTING); + Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING); } catch (Exception e) { - throw new RuntimeException("Unable to download and save file: " + fileLocation + " (" + reqURL + ")", e); + throw new RuntimeException("Unable to download and save file: " + path.toAbsolutePath() + " (" + reqURL + ")", e); } } From 82025edb9c14398b5cda03c26f92b669ad9db6bb Mon Sep 17 00:00:00 2001 From: Aurora Date: Mon, 3 Nov 2025 16:58:28 +0000 Subject: [PATCH 2/2] Revert "Add automatic downloading of the GeyserOptionalPack" This reverts commit 65b96208fb3359765f668dc10205b70c0035c454. --- .../java/org/geysermc/geyser/GeyserImpl.java | 4 - .../geyser/pack/GeyserOptionalPackUtils.java | 97 ------------------- .../org/geysermc/geyser/util/WebUtils.java | 14 +-- 3 files changed, 2 insertions(+), 113 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/pack/GeyserOptionalPackUtils.java diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 1aaaae061..55c15297d 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -57,7 +57,6 @@ import org.geysermc.floodgate.news.NewsItemAction; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.command.CommandSource; import org.geysermc.geyser.api.event.EventRegistrar; -import org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPostReloadEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; @@ -82,7 +81,6 @@ import org.geysermc.geyser.level.BedrockDimension; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.netty.GeyserServer; -import org.geysermc.geyser.pack.GeyserOptionalPackUtils; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.loader.ResourcePackLoader; @@ -276,8 +274,6 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { eventBus.subscribe(this, GeyserRegisterPermissionsEvent.class, Permissions::register); // Replace disconnect messages whenever necessary eventBus.subscribe(this, SessionDisconnectEventImpl.class, SessionDisconnectListener::onSessionDisconnect); - // Apply the GeyserOptionalPack if enabled - eventBus.subscribe(this, GeyserDefineResourcePacksEvent.class, GeyserOptionalPackUtils::register); startInstance(); diff --git a/core/src/main/java/org/geysermc/geyser/pack/GeyserOptionalPackUtils.java b/core/src/main/java/org/geysermc/geyser/pack/GeyserOptionalPackUtils.java deleted file mode 100644 index 6a3904afc..000000000 --- a/core/src/main/java/org/geysermc/geyser/pack/GeyserOptionalPackUtils.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.geysermc.geyser.pack; - -import com.google.gson.JsonObject; -import lombok.Getter; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent; -import org.geysermc.geyser.api.pack.PackCodec; -import org.geysermc.geyser.api.pack.ResourcePack; -import org.geysermc.geyser.api.pack.option.PriorityOption; -import org.geysermc.geyser.util.WebUtils; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -public class GeyserOptionalPackUtils { - - private static final Path CACHE = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache"); - private static final Path PACK_PATH = CACHE.resolve("GeyserOptionalPack.mcpack"); - - private static final String METADATA_URL = "https://download.geysermc.org/v2/projects/geyseroptionalpack/versions/latest/builds/latest"; - private static final String DOWNLOAD_URL = METADATA_URL + "/downloads/geyseroptionalpack"; - - @Getter - private static boolean optionalPackLoaded = false; - - public static void register(GeyserDefineResourcePacksEvent event) { - GeyserImpl geyser = GeyserImpl.getInstance(); - - if (!geyser.config().gameplay().enableOptionalPack()) return; - - if (ensurePackIsPresent(geyser, PackCodec.path(PACK_PATH))) { - event.register(ResourcePack.create(PackCodec.path(PACK_PATH)), PriorityOption.HIGH); - optionalPackLoaded = true; - } else { - geyser.getLogger().error("The GeyserOptionalPack was not found! The pack will not be downloaded when players join."); - optionalPackLoaded = false; - } - } - - public static boolean ensurePackIsPresent(GeyserImpl geyser, PackCodec packCodec) { - boolean downloadRequired = false; - - if (Files.notExists(PACK_PATH)) downloadRequired = true; - else { - try { - IllegalStateException invalidMetadataException = new IllegalStateException("Metadata is missing or has invalid required properties which are required in order to check for updates."); - - JsonObject metadata = WebUtils.getJson(METADATA_URL); - if (!metadata.has("downloads") || !metadata.get("downloads").isJsonObject()) { - throw invalidMetadataException; - } - - JsonObject downloads = metadata.getAsJsonObject("downloads"); - if (!downloads.has("geyseroptionalpack") || !downloads.get("geyseroptionalpack").isJsonObject()) { - throw invalidMetadataException; - } - - JsonObject downloadMetadata = downloads.getAsJsonObject("geyseroptionalpack"); - if (!downloadMetadata.has("sha256") || !downloadMetadata.get("sha256").isJsonPrimitive()) { - throw invalidMetadataException; - } - - String remoteSha256 = downloadMetadata.get("sha256").getAsString(); - - byte[] hash = packCodec.sha256(); - StringBuilder sha256Builder = new StringBuilder(2 * hash.length); - - for (byte hashPart : hash) { - String hex = Integer.toHexString(0xFF & hashPart); - if (hex.length() == 1) sha256Builder.append('0'); - sha256Builder.append(hex); - } - String localSha256 = sha256Builder.toString(); - - downloadRequired = !remoteSha256.equals(localSha256); - if (downloadRequired) { - geyser.getLogger().info("New GeyserOptionalPack update has been found."); - } - } catch (IOException | IllegalStateException e) { - geyser.getLogger().error("Unable to check GeyserOptionalPack metadata! Pack will not update.", e); - } - } - - if (downloadRequired) { - geyser.getLogger().info("Downloading the GeyserOptionalPack..."); - try { - WebUtils.downloadFile(DOWNLOAD_URL, PACK_PATH); - geyser.getLogger().info("GeyserOptionalPack download successful!"); - } catch (RuntimeException e) { - geyser.getLogger().error("Failed to download the latest GeyserOptionalPack!", e); - } - } - - return Files.exists(PACK_PATH) && Files.isRegularFile(PACK_PATH); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java index 5b4cb2706..8843abf01 100644 --- a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java @@ -109,24 +109,14 @@ public class WebUtils { * @param fileLocation Location to save on disk */ public static void downloadFile(String reqURL, String fileLocation) { - downloadFile(reqURL, Paths.get(fileLocation)); - } - - /** - * Downloads a file from the given URL and saves it to disk - * - * @param reqURL File to fetch - * @param path Location to save on disk as a Path - */ - public static void downloadFile(String reqURL, Path path) { try { HttpURLConnection con = (HttpURLConnection) new URL(reqURL).openConnection(); con.setRequestProperty("User-Agent", getUserAgent()); checkResponseCode(con); InputStream in = con.getInputStream(); - Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING); + Files.copy(in, Paths.get(fileLocation), StandardCopyOption.REPLACE_EXISTING); } catch (Exception e) { - throw new RuntimeException("Unable to download and save file: " + path.toAbsolutePath() + " (" + reqURL + ")", e); + throw new RuntimeException("Unable to download and save file: " + fileLocation + " (" + reqURL + ")", e); } }