Support remote Geyser Bedrock pack URLs
Build and publish release / Build and publish RemotePackSync (push) Successful in 59s
Build and publish release / Build and publish RemotePackSync (push) Successful in 59s
This commit is contained in:
@@ -40,7 +40,12 @@ sha1Url: "https://cdn.example.com/server-pack.zip.sha1"
|
||||
# Seconds between Java SHA-1 and Bedrock pack checks. Minimum: 30.
|
||||
refreshInterval: 300
|
||||
|
||||
# Optional local Bedrock Edition pack for GeyserMC players. Accepts .mcpack or .zip.
|
||||
# Preferred: remote Bedrock pack URL for GeyserMC players.
|
||||
# Must be a direct .mcpack/.zip download with Content-Type: application/zip
|
||||
# and an exact Content-Length.
|
||||
bedrockResourcePackUrl: "https://example.com/packs/server-pack.mcpack"
|
||||
|
||||
# Optional local alternative; use only when bedrockResourcePackUrl is blank.
|
||||
# Relative paths are resolved from plugins/RemotePackSync/.
|
||||
bedrockResourcePack: "bedrock/server-pack.mcpack"
|
||||
```
|
||||
@@ -67,11 +72,12 @@ The SHA-1 response must contain a 40-character hexadecimal SHA-1. Both of these
|
||||
|
||||
### Bedrock Edition through GeyserMC
|
||||
|
||||
- Set `bedrockResourcePack` to a local `.mcpack` or `.zip` file. `.mcpack` is fully supported.
|
||||
- The source pack is checked asynchronously at `refreshInterval`. When its SHA-256 changes, RemotePackSync creates a managed copy under `plugins/RemotePackSync/bedrock-managed/`.
|
||||
- The managed copy receives a deterministic new header UUID derived from the updated source SHA-256. This makes Bedrock clients treat a changed pack as a new pack, while Geyser calculates and supplies the required final pack hash and size.
|
||||
- The original configured pack is never modified.
|
||||
- Every new Geyser Bedrock connection receives the latest successfully processed pack through Geyser's public `SessionLoadResourcePacksEvent` API.
|
||||
- Preferred remote mode: set `bedrockResourcePackUrl` to a direct `.mcpack` or `.zip` download URL. RemotePackSync creates a Geyser `UrlPackCodec` and registers it through the public `SessionLoadResourcePacksEvent` API, so Geyser supplies the URL to connecting Bedrock clients.
|
||||
- The remote server must return `Content-Type: application/zip` and an exact `Content-Length`. Redirects are allowed only when they ultimately resolve to a valid direct download. The URL should be stable and should expose a changed ETag or URL when the pack changes.
|
||||
- Geyser validates/downloads remote URL packs during its lifecycle. Use `/remotepacksync reload` after changing the URL or after replacing a pack at the same URL; players already connected must reconnect.
|
||||
- Local alternative: set `bedrockResourcePack` to a `.mcpack` or `.zip` file. The source is checked asynchronously at `refreshInterval`; changes receive a deterministic manifest UUID and are managed under `plugins/RemotePackSync/bedrock-managed/`.
|
||||
- The original configured local pack is never modified.
|
||||
- Every new Geyser Bedrock connection receives the latest successfully registered pack.
|
||||
- The pack must be a valid Bedrock pack containing `manifest.json` at the archive root. Geyser does **not** convert Java packs into Bedrock packs.
|
||||
- Bedrock's protocol only negotiates packs while a client connects. Players already connected when the file changes must reconnect to receive the new version; RemotePackSync cannot hot-push a replacement to an active Bedrock session.
|
||||
- If Geyser is absent, Java Edition resource-pack operation continues normally and Bedrock delivery stays disabled.
|
||||
@@ -83,7 +89,7 @@ The SHA-1 response must contain a 40-character hexadecimal SHA-1. Both of these
|
||||
|
||||
## Compatibility
|
||||
|
||||
RemotePackSync compiles against `org.spigotmc:spigot-api:26.2-R0.1-SNAPSHOT` and Geyser API `2.11.0-SNAPSHOT`, targeting Java 25 as required by Minecraft/Spigot 26.2. Java packs are sent through Bukkit's stable `Player#setResourcePack(String, byte[])` API; Bedrock packs use Geyser's public pack/session API. Geyser is a soft dependency, so Java-only servers remain supported.
|
||||
RemotePackSync compiles against `org.spigotmc:spigot-api:26.2-R0.1-SNAPSHOT` and Geyser API `2.11.0-SNAPSHOT`, targeting Java 25 as required by Minecraft/Spigot 26.2. Java packs are sent through Bukkit's stable `Player#setResourcePack(String, byte[])` API; Bedrock packs use Geyser's public `UrlPackCodec`/pack-session API. Geyser is a soft dependency, so Java-only servers remain supported.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -57,10 +57,18 @@ public final class BedrockPackManager {
|
||||
}
|
||||
|
||||
subscribeToGeyser();
|
||||
refreshTask = Bukkit.getScheduler().runTaskTimerAsynchronously(
|
||||
plugin, this::refreshPack, 0L, newSettings.refreshIntervalTicks());
|
||||
plugin.getLogger().info("Bedrock resource-pack refresh enabled every "
|
||||
+ (newSettings.refreshIntervalTicks() / 20L) + " seconds.");
|
||||
if (newSettings.isRemote()) {
|
||||
// Geyser's URL codec validates the remote download and obtains its metadata.
|
||||
// Geyser itself downloads remote packs once during its lifecycle, so do not
|
||||
// repeatedly recreate the URL codec on a timer.
|
||||
refreshTask = Bukkit.getScheduler().runTaskAsynchronously(plugin, this::refreshPack);
|
||||
plugin.getLogger().info("Bedrock remote resource-pack URL configured. Geyser will fetch it for new connections.");
|
||||
} else {
|
||||
refreshTask = Bukkit.getScheduler().runTaskTimerAsynchronously(
|
||||
plugin, this::refreshPack, 0L, newSettings.refreshIntervalTicks());
|
||||
plugin.getLogger().info("Bedrock local resource-pack refresh enabled every "
|
||||
+ (newSettings.refreshIntervalTicks() / 20L) + " seconds.");
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@@ -111,6 +119,17 @@ public final class BedrockPackManager {
|
||||
}
|
||||
|
||||
try {
|
||||
if (activeSettings.isRemote()) {
|
||||
ResourcePack resourcePack = ResourcePack.create(PackCodec.url(activeSettings.remoteUrl()));
|
||||
if (activeSettings != settings) {
|
||||
return;
|
||||
}
|
||||
currentPack.set(new Snapshot(null, resourcePack));
|
||||
plugin.getLogger().info("Bedrock remote resource-pack URL validated and registered: "
|
||||
+ activeSettings.remoteUrl());
|
||||
return;
|
||||
}
|
||||
|
||||
Path source = activeSettings.sourceFile();
|
||||
if (!Files.isRegularFile(source)) {
|
||||
throw new IOException("Configured Bedrock pack does not exist or is not a regular file: " + source);
|
||||
|
||||
@@ -6,30 +6,46 @@ import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Validated configuration for an optional local Bedrock resource pack. */
|
||||
public record BedrockPackSettings(Path sourceFile, long refreshIntervalTicks) {
|
||||
/** Validated configuration for an optional local or remote Bedrock resource pack. */
|
||||
public record BedrockPackSettings(Path sourceFile, String remoteUrl, long refreshIntervalTicks) {
|
||||
private static final long MINIMUM_REFRESH_SECONDS = 30L;
|
||||
|
||||
public static Optional<BedrockPackSettings> from(FileConfiguration config, Path dataDirectory) {
|
||||
String configuredUrl = config.getString("bedrockResourcePackUrl", "").trim();
|
||||
String configuredPath = config.getString("bedrockResourcePack", "").trim();
|
||||
if (configuredPath.isEmpty()) {
|
||||
if (!configuredUrl.isEmpty() && !configuredPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("Configure only one of bedrockResourcePackUrl or bedrockResourcePack.");
|
||||
}
|
||||
if (configuredUrl.isEmpty() && configuredPath.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Path source = Path.of(configuredPath);
|
||||
if (!source.isAbsolute()) {
|
||||
source = dataDirectory.resolve(source);
|
||||
Path source = null;
|
||||
if (!configuredPath.isEmpty()) {
|
||||
source = Path.of(configuredPath);
|
||||
if (!source.isAbsolute()) {
|
||||
source = dataDirectory.resolve(source);
|
||||
}
|
||||
source = source.normalize();
|
||||
String fileName = source.getFileName().toString().toLowerCase(Locale.ROOT);
|
||||
if (!fileName.endsWith(".mcpack") && !fileName.endsWith(".zip")) {
|
||||
throw new IllegalArgumentException("bedrockResourcePack must reference a .mcpack or .zip file.");
|
||||
}
|
||||
} else {
|
||||
if (!configuredUrl.startsWith("https://") && !configuredUrl.startsWith("http://")) {
|
||||
throw new IllegalArgumentException("bedrockResourcePackUrl must be an HTTP(S) direct download URL.");
|
||||
}
|
||||
}
|
||||
source = source.normalize();
|
||||
|
||||
String fileName = source.getFileName().toString().toLowerCase(Locale.ROOT);
|
||||
if (!fileName.endsWith(".mcpack") && !fileName.endsWith(".zip")) {
|
||||
throw new IllegalArgumentException("bedrockResourcePack must reference a .mcpack or .zip file.");
|
||||
}
|
||||
long refreshSeconds = config.getLong("refreshInterval", 300L);
|
||||
if (refreshSeconds < MINIMUM_REFRESH_SECONDS) {
|
||||
throw new IllegalArgumentException("refreshInterval must be at least " + MINIMUM_REFRESH_SECONDS + " seconds.");
|
||||
}
|
||||
return Optional.of(new BedrockPackSettings(source, Math.multiplyExact(refreshSeconds, 20L)));
|
||||
return Optional.of(new BedrockPackSettings(source, configuredUrl.isEmpty() ? null : configuredUrl,
|
||||
Math.multiplyExact(refreshSeconds, 20L)));
|
||||
}
|
||||
|
||||
public boolean isRemote() {
|
||||
return remoteUrl != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public final class RemotePackSyncPlugin extends JavaPlugin implements Listener,
|
||||
@Override
|
||||
public void onEnable() {
|
||||
saveDefaultConfig();
|
||||
getConfig().addDefault("bedrockResourcePackUrl", "");
|
||||
getConfig().addDefault("bedrockResourcePack", "");
|
||||
getConfig().options().copyDefaults(true);
|
||||
saveConfig();
|
||||
|
||||
@@ -9,7 +9,12 @@ sha1Url: ""
|
||||
# Seconds between remote SHA-1 and local Bedrock pack checks. Minimum: 30 seconds.
|
||||
refreshInterval: 300
|
||||
|
||||
# Optional local Bedrock Edition resource pack for players joining through GeyserMC.
|
||||
# The path may point to a .mcpack or .zip file. Relative paths are resolved from
|
||||
# plugins/RemotePackSync/. Leave blank to disable Bedrock pack delivery.
|
||||
# Optional remote Bedrock Edition resource-pack URL for players connecting through GeyserMC.
|
||||
# This must be a direct HTTP(S) download URL to a .mcpack or .zip pack. The server
|
||||
# must return Content-Type: application/zip and an exact Content-Length.
|
||||
# Geyser passes this URL to Bedrock clients using its URL pack codec.
|
||||
bedrockResourcePackUrl: ""
|
||||
|
||||
# Optional local alternative. Configure this only when bedrockResourcePackUrl is blank.
|
||||
# Relative paths are resolved from plugins/RemotePackSync/.
|
||||
bedrockResourcePack: ""
|
||||
|
||||
Reference in New Issue
Block a user