This commit is contained in:
Brian Neumann-Fopiano
2026-08-12 16:31:27 -04:00
parent c59592e448
commit 13e02ecd2f
41 changed files with 352 additions and 449 deletions
@@ -151,7 +151,6 @@ public class IrisSettings {
public static class IrisSettingsAutoconfiguration {
public boolean configureSpigotTimeoutTime = true;
public boolean configurePaperWatchdogDelay = true;
public boolean autoRestartOnCustomBiomeInstall = true;
}
@Data
@@ -7,16 +7,13 @@ import art.arcane.iris.engine.data.cache.AtomicCache;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.IrisServices;
import art.arcane.iris.util.common.misc.ServerProperties;
import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.volmlib.util.bukkit.WorldIdentity;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.io.IO;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -231,12 +228,8 @@ public class IrisWorlds {
+ " but its dimension failed to load; not redownloading. Fix or delete the pack folder.");
return null;
}
IrisLogging.warn("Unable to find dimension type " + id + " Looking for online packs...");
IrisServices.get(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, false);
dimension = IrisData.loadAnyDimension(id, null);
if (dimension != null) {
IrisLogging.info("Resolved missing dimension, proceeding.");
}
IrisLogging.warn("Unable to find dimension type " + id + ". Install it with /iris download "
+ id + " and restart the server.");
}
return dimension;
}
@@ -114,8 +114,8 @@ public class ServerConfigurator {
loadedDatapackRuntimeReady = result.succeeded()
&& !result.restartRequired()
&& pinLoadedDatapackCompilerInputs();
if (result.restartRequired() && IrisSettings.get().getAutoConfiguration().isAutoRestartOnCustomBiomeInstall()) {
restart();
if (result.restartRequired()) {
IrisLogging.warn("Iris datapack changes require another server restart before worlds can use them.");
}
}
}
@@ -295,10 +295,6 @@ public final class BukkitCommandMessagesExtended {
"iris.bukkit.commandiris.set_debug",
C.GREEN + "Set debug to: " + "{to}"
);
public static final TextKey COMMAND_IRIS_DOWNLOADING_PACK_BETA_RELEASE = TextKey.of(
"iris.bukkit.commandiris.downloading_pack_beta_release",
C.GREEN + "Downloading pack: " + "{pack}" + " (beta release)" + "{value}"
);
public static final TextKey COMMAND_IRIS_DOWNLOADING_PACK = TextKey.of(
"iris.bukkit.commandiris.downloading_pack",
C.GREEN + "Downloading pack: " + "{pack}" + "/" + "{branch}" + "{value}"
@@ -916,7 +912,6 @@ public final class BukkitCommandMessagesExtended {
COMMAND_IRIS_LOOKS_LIKE_WORLD_WAS_ALREADY_REMOVED_FROM_BUKKIT_YML,
COMMAND_IRIS_FAILED_SAVE_BUKKIT_YML_BECAUSE,
COMMAND_IRIS_SET_DEBUG,
COMMAND_IRIS_DOWNLOADING_PACK_BETA_RELEASE,
COMMAND_IRIS_DOWNLOADING_PACK,
COMMAND_IRIS_YOU_MUST_BE_IRIS_WORLD,
COMMAND_IRIS_SENDING_METRICS,
@@ -446,10 +446,6 @@ public final class BukkitRuntimeMessages {
"iris.bukkit.runtime.studiosvc.failed_download",
"Failed to download '" + "{key}" + "'."
);
public static final TextKey STUDIO_S_V_C_FAILED_DOWNLOAD_IRISDIMENSIONS_OVERWORLD_BETA_RELEASE = TextKey.of(
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release",
"Failed to download the IrisDimensions/overworld beta release."
);
public static final TextKey STUDIO_S_V_C_FAILED_DOWNLOAD_BRANCH = TextKey.of(
"iris.bukkit.runtime.studiosvc.failed_download_branch",
"Failed to download '" + "{repo}" + "' (branch " + "{branch}" + ")."
@@ -730,7 +726,6 @@ public final class BukkitRuntimeMessages {
STUDIO_S_V_C_PACK_WAS_NOT_FOUND_PACK_LISTING,
STUDIO_S_V_C_USE_IRIS_DOWNLOAD_PACK_BRANCH_BRANCH_DOWNLOAD_MANUALLY,
STUDIO_S_V_C_FAILED_DOWNLOAD,
STUDIO_S_V_C_FAILED_DOWNLOAD_IRISDIMENSIONS_OVERWORLD_BETA_RELEASE,
STUDIO_S_V_C_FAILED_DOWNLOAD_BRANCH,
STUDIO_S_V_C_FAILED_OPEN_STUDIO_WORLD,
STUDIO_S_V_C_CANNOT_OPEN_STUDIO_PACK_HAS_BLOCKING_ERRORS,
@@ -43,20 +43,9 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public final class DefaultPackBootstrapProvisioner {
private static final List<PackSpec> DEFAULT_PACKS = List.of(
new PackSpec(
"overworld",
URI.create("https://github.com/IrisDimensions/overworld/releases/download/beta/overworld.zip"),
"overworld"
),
new PackSpec(
"underworld",
URI.create("https://github.com/IrisDimensions/underworld/releases/download/beta/underworld.zip"),
"underworld"
)
);
private static final List<PackSpec> DEFAULT_PACKS = List.of();
private static final String WORLD_DATAPACK_DIRECTORY = "iris";
private static final int MARKER_SCHEMA = 4;
private static final int MARKER_SCHEMA = 5;
private static final int MAX_ARCHIVE_ENTRIES = 100_000;
private static final long MAX_ARCHIVE_BYTES = 512L * 1024L * 1024L;
private static final long MAX_EXPANDED_BYTES = 2L * 1024L * 1024L * 1024L;
@@ -239,9 +228,6 @@ public final class DefaultPackBootstrapProvisioner {
}
List<File> packRoots = IrisDatapackCompiler.collectPackRoots(normalizedData, options.levelRoot());
if (packRoots.isEmpty()) {
throw new IOException("No Iris pack roots were available for bootstrap datapack compilation");
}
IDataFixer fixer = DataVersion.getLatest().get();
if (fixer == null) {
throw new IOException("Latest Iris datapack fixer is unavailable during bootstrap");
@@ -260,7 +246,7 @@ public final class DefaultPackBootstrapProvisioner {
Files.createDirectories(compileContainer);
KList<File> outputFolders = new KList<File>().qadd(compileContainer.toFile());
IrisDatapackCompiler.compile(packRoots, outputFolders, fixer, false);
if (!isDatapackRoot(compileContainer)) {
if (!isDatapackRoot(compileContainer, !packRoots.isEmpty())) {
throw new IOException("Canonical Iris datapack compiler produced incomplete output at " + compileContainer);
}
datapackBackup = replaceWithBackup(compileContainer, datapackRoot);
@@ -271,12 +257,12 @@ public final class DefaultPackBootstrapProvisioner {
for (PackPlan plan : plans) {
validatePackRoot(plan.root(), plan.spec());
}
if (!isDatapackRoot(datapackRoot)) {
throw new IOException("Bootstrap datapack output is incomplete at " + datapackRoot);
}
List<File> finalPackRoots = IrisDatapackCompiler.collectPackRoots(
normalizedData,
options.levelRoot());
if (!isDatapackRoot(datapackRoot, !finalPackRoots.isEmpty())) {
throw new IOException("Bootstrap datapack output is incomplete at " + datapackRoot);
}
String finalAggregateFingerprint = packRootsFingerprint(finalPackRoots);
String finalCompilerInputFingerprint = IrisDatapackCompiler.computeInputFingerprint(
finalPackRoots,
@@ -316,7 +302,6 @@ public final class DefaultPackBootstrapProvisioner {
} else {
status = ProvisionStatus.UNCHANGED;
}
feedback.accept("Iris bootstrap packs are " + status.name().toLowerCase() + ".");
Map<String, Path> provisionedPacks = new LinkedHashMap<>();
for (PackPlan plan : plans) {
provisionedPacks.put(plan.spec().key(), plan.root());
@@ -605,9 +590,13 @@ public final class DefaultPackBootstrapProvisioner {
}
private static boolean isDatapackRoot(Path path) {
return isDatapackRoot(path, false);
}
private static boolean isDatapackRoot(Path path, boolean requiresGeneratedTypes) {
return path != null
&& Files.isRegularFile(path.resolve("pack.mcmeta"))
&& Files.isDirectory(path.resolve("data/iris/dimension_type"));
&& (!requiresGeneratedTypes || Files.isDirectory(path.resolve("data/iris/dimension_type")));
}
static Path worldDatapackRoot(Path levelRoot) {
@@ -908,7 +897,7 @@ public final class DefaultPackBootstrapProvisioner {
Objects.requireNonNull(requestTimeout, "requestTimeout");
Objects.requireNonNull(retryDelay, "retryDelay");
Objects.requireNonNull(levelRoot, "levelRoot");
if (packs.isEmpty() || attempts < 1 || maxArchiveBytes < 1L) {
if (attempts < 1 || maxArchiveBytes < 1L) {
throw new IllegalArgumentException("Invalid bootstrap provisioning options");
}
Set<String> keys = new HashSet<>();
@@ -32,6 +32,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
@@ -56,14 +57,14 @@ import java.util.zip.ZipInputStream;
public final class PackDownloader {
private static final String DEFAULT_OVERWORLD_PACK = "overworld";
private static final String DEFAULT_OVERWORLD_REPOSITORY = "IrisDimensions/overworld";
private static final String DEFAULT_OVERWORLD_RELEASE_URL = "https://github.com/IrisDimensions/overworld/releases/download/beta/overworld.zip";
private static final String DEFAULT_OVERWORLD_REF = "master";
private static final String UNDERWORLD_PACK = "underworld";
private static final String UNDERWORLD_REPOSITORY = "IrisDimensions/underworld";
private static final String UNDERWORLD_RELEASE_URL = "https://github.com/IrisDimensions/underworld/releases/download/beta/underworld.zip";
private static final List<String> MANAGED_BETA_PACK_KEYS = List.of(DEFAULT_OVERWORLD_PACK, UNDERWORLD_PACK);
private static final Map<String, ManagedBetaPack> MANAGED_BETA_PACKS = Map.of(
DEFAULT_OVERWORLD_PACK, new ManagedBetaPack(DEFAULT_OVERWORLD_REPOSITORY, DEFAULT_OVERWORLD_RELEASE_URL),
UNDERWORLD_PACK, new ManagedBetaPack(UNDERWORLD_REPOSITORY, UNDERWORLD_RELEASE_URL)
private static final String UNDERWORLD_REF = "main";
private static final List<String> MANAGED_PACK_KEYS = List.of(DEFAULT_OVERWORLD_PACK, UNDERWORLD_PACK);
private static final Map<String, ManagedPack> MANAGED_PACKS = Map.of(
DEFAULT_OVERWORLD_PACK, new ManagedPack(DEFAULT_OVERWORLD_REPOSITORY, DEFAULT_OVERWORLD_REF),
UNDERWORLD_PACK, new ManagedPack(UNDERWORLD_REPOSITORY, UNDERWORLD_REF)
);
private static final Pattern GITHUB_REPOSITORY = Pattern.compile("[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+");
private static final Pattern GITHUB_REF = Pattern.compile("[A-Za-z0-9][A-Za-z0-9._/-]*");
@@ -84,12 +85,30 @@ public final class PackDownloader {
return DEFAULT_OVERWORLD_PACK.equals(pack);
}
public static boolean isManagedBetaPack(String pack) {
return pack != null && MANAGED_BETA_PACKS.containsKey(pack);
public static boolean isManagedPack(String pack) {
return pack != null && MANAGED_PACKS.containsKey(pack);
}
public static List<String> managedBetaPacks() {
return MANAGED_BETA_PACK_KEYS;
public static List<String> managedPacks() {
return MANAGED_PACK_KEYS;
}
public static boolean isDirectZipUrl(String value) {
if (value == null || value.isBlank()) {
return false;
}
try {
URI uri = URI.create(value.trim());
String scheme = uri.getScheme();
String path = uri.getPath();
return ("https".equalsIgnoreCase(scheme) || "http".equalsIgnoreCase(scheme))
&& uri.getHost() != null
&& !uri.getHost().isBlank()
&& path != null
&& path.toLowerCase(Locale.ROOT).endsWith(".zip");
} catch (IllegalArgumentException exception) {
return false;
}
}
public static String defaultOverworldPack() {
@@ -133,8 +152,8 @@ public final class PackDownloader {
}
}
public static boolean isManagedBetaPackPresent(File packsFolder, String key) {
if (!isManagedBetaPack(key) || !isPackPresent(packsFolder, key)) {
public static boolean isManagedPackPresent(File packsFolder, String key) {
if (!isManagedPack(key) || !isPackPresent(packsFolder, key)) {
return false;
}
File resolvedPack = PackDirectoryResolver.resolveExisting(packsFolder, key);
@@ -149,32 +168,46 @@ public final class PackDownloader {
}
public static PackInstallResult downloadDefaultOverworld(File packsFolder, boolean forceOverwrite, Consumer<String> feedback) throws IOException {
return downloadManagedBeta(packsFolder, DEFAULT_OVERWORLD_PACK, forceOverwrite, feedback);
return downloadManaged(packsFolder, DEFAULT_OVERWORLD_PACK, forceOverwrite, feedback);
}
public static PackInstallResult downloadManagedBeta(File packsFolder, String pack, boolean forceOverwrite,
Consumer<String> feedback) throws IOException {
ManagedBetaPack managed = pack == null ? null : MANAGED_BETA_PACKS.get(pack);
public static PackInstallResult downloadManaged(File packsFolder, String pack, boolean forceOverwrite,
Consumer<String> feedback) throws IOException {
ManagedPack managed = pack == null ? null : MANAGED_PACKS.get(pack);
if (managed == null) {
throw new IllegalArgumentException("Pack '" + pack + "' has no managed beta release");
throw new IllegalArgumentException("Pack '" + pack + "' has no managed Git source");
}
return download(
packsFolder,
managed.repository(),
managed.releaseUrl(),
managed.ref(),
forceOverwrite,
false,
pack,
feedback
);
}
public static PackInstallResult downloadUrl(File packsFolder, String url, boolean forceOverwrite,
Consumer<String> feedback) throws IOException {
if (!isDirectZipUrl(url)) {
throw new IllegalArgumentException("Pack URL must be an HTTP or HTTPS .zip link");
}
return download(
packsFolder,
"direct-url",
url.trim(),
forceOverwrite,
true,
pack,
null,
feedback
);
}
/**
* Downloads and imports a pack. {@code expectedKey} is the pack key the caller is trying to
* obtain (null when unknown, e.g. arbitrary repo/branch downloads); when the key is already
* present on disk and {@code forceOverwrite} is false, the network is never touched. The
* per-repo lock keeps concurrent startup triggers (async default-pack install racing world
* resolution) from downloading the same archive twice.
* obtain (null when unknown, e.g. arbitrary repo, branch, or URL downloads); when the key is
* already present on disk and {@code forceOverwrite} is false, the network is never touched.
*/
public static PackInstallResult download(File packsFolder, String repo, String ref, boolean forceOverwrite, boolean directUrl, String expectedKey, Consumer<String> feedback) throws IOException {
Objects.requireNonNull(packsFolder, "packsFolder");
@@ -185,8 +218,8 @@ public final class PackDownloader {
}
String lockKey = expectedKey != null && !expectedKey.isBlank() ? "key:" + expectedKey : "ref:" + repo + "|" + ref;
return withDownloadLock(lockKey, () -> {
boolean present = isManagedBetaPack(expectedKey)
? isManagedBetaPackPresent(packsFolder, expectedKey)
boolean present = isManagedPack(expectedKey)
? isManagedPackPresent(packsFolder, expectedKey)
: isPackPresent(packsFolder, expectedKey);
if (!forceOverwrite && present) {
sendFeedback(output, IrisLanguage.plain(PackDownloadMessages.ALREADY_INSTALLED, MessageArgument.untrusted("key", expectedKey)));
@@ -224,14 +257,7 @@ public final class PackDownloader {
sendFeedback(feedback, IrisLanguage.plain(PackDownloadMessages.NO_EXTRACTED_FILES));
return null;
}
File directory;
try {
directory = zipFiles.length > 1 ? work : zipFiles[0].isDirectory() ? zipFiles[0] : null;
} catch (NullPointerException exception) {
IrisLogging.reportError(exception);
sendFeedback(feedback, IrisLanguage.plain(PackDownloadMessages.HOME_DIRECTORY_ERROR));
return null;
}
File directory = findExtractedPackDirectory(work, zipFiles);
if (directory == null) {
sendFeedback(feedback, IrisLanguage.plain(PackDownloadMessages.INVALID_ARCHIVE_FORMAT));
return null;
@@ -242,6 +268,26 @@ public final class PackDownloader {
}
}
private static File findExtractedPackDirectory(File work, File[] extractedFiles) {
if (new File(work, "dimensions").isDirectory()) {
return work;
}
File candidate = null;
for (File extracted : extractedFiles) {
if (!extracted.isDirectory() || !new File(extracted, "dimensions").isDirectory()) {
continue;
}
if (candidate != null) {
return work;
}
candidate = extracted;
}
if (candidate != null) {
return candidate;
}
return extractedFiles.length == 1 && extractedFiles[0].isDirectory() ? extractedFiles[0] : work;
}
static PackInstallResult installExtractedPack(File packsFolder, File extractedPack, boolean forceOverwrite,
String expectedKey, Consumer<String> feedback) throws IOException {
Objects.requireNonNull(packsFolder, "packsFolder");
@@ -311,7 +357,7 @@ public final class PackDownloader {
PackValidationResult stagedValidation;
try {
stagedValidation = PackValidator.validate(staging);
stagedValidation = PackValidator.validateForDatapackBootstrap(staging);
} catch (RuntimeException exception) {
throw new IOException("Pack validation failed before publication for '" + key + "'", exception);
}
@@ -375,8 +421,8 @@ public final class PackDownloader {
));
return null;
}
boolean present = isManagedBetaPack(prepared.key())
? isManagedBetaPackPresent(packsRoot.toFile(), prepared.key())
boolean present = isManagedPack(prepared.key())
? isManagedPackPresent(packsRoot.toFile(), prepared.key())
: isPackPresent(packsRoot.toFile(), prepared.key());
if (!forceOverwrite && present) {
sendFeedback(feedback, IrisLanguage.plain(
@@ -418,7 +464,7 @@ public final class PackDownloader {
PackDownloadMessages.ACQUIRED,
MessageArgument.untrusted("name", prepared.name())
));
return new PackInstallResult(prepared.key(), true, false);
return new PackInstallResult(prepared.key(), true, true);
}
private static Path findConflictingPack(Path packsRoot, Path staging, Path target, String key) throws IOException {
@@ -648,12 +694,20 @@ public final class PackDownloader {
return "https://codeload.github.com/" + repo + "/zip/" + qualifiedRef;
}
static String defaultOverworldReleaseUrl() {
return DEFAULT_OVERWORLD_RELEASE_URL;
static String defaultOverworldRepository() {
return DEFAULT_OVERWORLD_REPOSITORY;
}
static String underworldReleaseUrl() {
return UNDERWORLD_RELEASE_URL;
static String defaultOverworldRef() {
return DEFAULT_OVERWORLD_REF;
}
static String underworldRepository() {
return UNDERWORLD_REPOSITORY;
}
static String underworldRef() {
return UNDERWORLD_REF;
}
private static void validateGithubRef(String qualifiedRef) {
@@ -681,7 +735,7 @@ public final class PackDownloader {
private record PreparedPack(String key, String name, PackValidationResult validation) {
}
private record ManagedBetaPack(String repository, String releaseUrl) {
private record ManagedPack(String repository, String ref) {
}
public record PackInstallResult(String key, boolean changed, boolean restartRequired) {
@@ -98,14 +98,8 @@ public class StudioSVC implements IrisService {
@Override
public void onEnable() {
VolmitSender console = BukkitPlatform.console();
runPackMutation(console, LifecycleOperationCoordinator.OperationKind.PACK_DOWNLOAD,
"managed-beta-packs", () -> installMissingManagedBetaPacks(console),
"Failed to install Iris managed beta packs at startup.");
String configuredPack = IrisSettings.get().getGenerator().getDefaultWorldType();
if (!PackDownloader.isManagedBetaPack(configuredPack)
&& !PackDownloader.isPackPresent(getWorkspaceFolder(), configuredPack)) {
if (!PackDownloader.isPackPresent(getWorkspaceFolder(), configuredPack)) {
IrisLogging.warn("Default pack '" + configuredPack
+ "' is not installed. Please download it manually with /iris download " + configuredPack);
}
@@ -342,25 +336,41 @@ public class StudioSVC implements IrisService {
public void downloadSearch(VolmitSender sender, String key, boolean forceOverwrite) {
runPackMutation(sender, LifecycleOperationCoordinator.OperationKind.PACK_DOWNLOAD, key, () -> {
DownloadOutcome outcome = downloadSearchLocked(sender, key, forceOverwrite);
return finishStandalonePackMutation(sender, outcome);
return finishStandalonePackMutation(outcome);
}, IrisLanguage.text(BukkitRuntimeMessages.STUDIO_S_V_C_FAILED_DOWNLOAD, MessageArgument.untrusted("key", String.valueOf(key))));
}
public void downloadManagedBeta(VolmitSender sender, String key, boolean forceOverwrite) {
if (!PackDownloader.isManagedBetaPack(key)) {
sender.sendMessage("Iris pack '" + key + "' does not have a managed beta release.");
public void downloadManaged(VolmitSender sender, String key, boolean forceOverwrite) {
if (!PackDownloader.isManagedPack(key)) {
sender.sendMessage("Iris pack '" + key + "' does not have a managed Git source.");
return;
}
runPackMutation(sender, LifecycleOperationCoordinator.OperationKind.PACK_DOWNLOAD, key, () -> {
DownloadOutcome outcome = downloadManagedBetaLocked(sender, key, forceOverwrite);
return finishStandalonePackMutation(sender, outcome);
}, "Failed to download IrisDimensions/" + key + " beta release.");
DownloadOutcome outcome = downloadManagedLocked(sender, key, forceOverwrite);
return finishStandalonePackMutation(outcome);
}, "Failed to download managed Iris pack '" + key + "'.");
}
public void downloadUrl(VolmitSender sender, String url, boolean forceOverwrite) {
if (!PackDownloader.isDirectZipUrl(url)) {
sender.sendMessage("Iris requires a valid HTTP or HTTPS .zip URL.");
return;
}
runPackMutation(sender, LifecycleOperationCoordinator.OperationKind.PACK_DOWNLOAD, url, () -> {
DownloadOutcome outcome = DownloadOutcome.from(PackDownloader.downloadUrl(
getWorkspaceFolder(),
url,
forceOverwrite,
sender::sendMessage
));
return finishStandalonePackMutation(outcome);
}, "Failed to download Iris pack from '" + url + "'.");
}
public void downloadBranch(VolmitSender sender, String repo, String branch, boolean forceOverwrite) {
runPackMutation(sender, LifecycleOperationCoordinator.OperationKind.PACK_DOWNLOAD, repo + "/" + branch, () -> {
DownloadOutcome outcome = downloadLocked(sender, repo, branch, forceOverwrite, false, null);
return finishStandalonePackMutation(sender, outcome);
return finishStandalonePackMutation(outcome);
}, IrisLanguage.text(BukkitRuntimeMessages.STUDIO_S_V_C_FAILED_DOWNLOAD_BRANCH, MessageArgument.untrusted("repo", String.valueOf(repo)), MessageArgument.untrusted("branch", String.valueOf(branch))));
}
@@ -376,13 +386,13 @@ public class StudioSVC implements IrisService {
String target = expectedKey == null ? repo + "/" + branch : expectedKey;
runPackMutation(sender, LifecycleOperationCoordinator.OperationKind.PACK_DOWNLOAD, target, () -> {
DownloadOutcome outcome = downloadLocked(sender, repo, branch, forceOverwrite, directUrl, expectedKey);
return finishStandalonePackMutation(sender, outcome);
return finishStandalonePackMutation(outcome);
}, "Failed to download Iris pack '" + target + "'.");
}
private DownloadOutcome downloadSearchLocked(VolmitSender sender, String key, boolean forceOverwrite) throws IOException {
if (PackDownloader.isManagedBetaPack(key)) {
return downloadManagedBetaLocked(sender, key, forceOverwrite);
if (PackDownloader.isManagedPack(key)) {
return downloadManagedLocked(sender, key, forceOverwrite);
}
String descriptor = key.contains("/") ? key : getListing(false).get(key);
@@ -412,17 +422,17 @@ public class StudioSVC implements IrisService {
return new PackListingReference(repository, ref, expectedKey);
}
private DownloadOutcome downloadManagedBetaLocked(
private DownloadOutcome downloadManagedLocked(
VolmitSender sender,
String expectedKey,
boolean forceOverwrite
) throws IOException {
if (!forceOverwrite && PackDownloader.isManagedBetaPackPresent(getWorkspaceFolder(), expectedKey)) {
if (!forceOverwrite && PackDownloader.isManagedPackPresent(getWorkspaceFolder(), expectedKey)) {
sender.sendMessage(IrisLanguage.text(PackDownloadMessages.ALREADY_INSTALLED, MessageArgument.untrusted("key", expectedKey)));
return DownloadOutcome.notChanged();
}
PackDownloader.PackInstallResult result = PackDownloader.downloadManagedBeta(
PackDownloader.PackInstallResult result = PackDownloader.downloadManaged(
getWorkspaceFolder(),
expectedKey,
forceOverwrite,
@@ -431,33 +441,6 @@ public class StudioSVC implements IrisService {
return DownloadOutcome.from(result);
}
private boolean installMissingManagedBetaPacks(VolmitSender sender) {
boolean changed = false;
boolean restartRequired = false;
for (String key : missingManagedBetaPacks(getWorkspaceFolder())) {
IrisLogging.info("Downloading managed Iris pack " + key + " (beta release)");
try {
DownloadOutcome outcome = downloadManagedBetaLocked(sender, key, false);
changed |= outcome.changed();
restartRequired |= outcome.restartRequired();
} catch (Throwable failure) {
IrisLogging.reportError("Failed to download IrisDimensions/" + key + " beta release.", failure);
sender.sendMessage("Failed to download IrisDimensions/" + key + " beta release. " + errorDetail(failure));
}
}
return finishStandalonePackMutation(sender, new DownloadOutcome(changed, restartRequired));
}
static List<String> missingManagedBetaPacks(File workspaceFolder) {
List<String> missing = new ArrayList<>();
for (String key : PackDownloader.managedBetaPacks()) {
if (!PackDownloader.isManagedBetaPackPresent(workspaceFolder, key)) {
missing.add(key);
}
}
return List.copyOf(missing);
}
private DownloadOutcome downloadLocked(
VolmitSender sender,
String repo,
@@ -1033,27 +1016,14 @@ public class StudioSVC implements IrisService {
}
if (restartRequired) {
sender.sendMessage("Iris must restart before the new pack data can be used.");
ServerConfigurator.restart();
sender.sendMessage("Restart the server before using the downloaded Iris pack.");
}
};
runOffPrimaryThread(work);
}
private boolean finishStandalonePackMutation(VolmitSender sender, DownloadOutcome outcome) {
if (!outcome.changed()) {
return false;
}
DatapackInstallResult installResult = ServerConfigurator.installDataPacksIfChanged(true);
return switch (installResult.status()) {
case FAILED -> {
sender.sendMessage("The pack was downloaded, but Iris could not install its datapack output.");
yield false;
}
case RESTART_REQUIRED -> true;
case READY, UNCHANGED -> outcome.restartRequired();
};
private boolean finishStandalonePackMutation(DownloadOutcome outcome) {
return outcome.changed() && outcome.restartRequired();
}
private void runOffPrimaryThread(Runnable work) {
@@ -75,12 +75,7 @@ public class IrisToolbelt {
private static final Method BUKKIT_IS_STOPPING_METHOD = resolveBukkitIsStoppingMethod();
/**
* Will find / download / search for the dimension or return null
* <p>
* - You can provide a dimenson in the packs folder by the folder name
* - You can provide a github repo by using (assumes branch is master unless specified)
* - GithubUsername/repository
* - GithubUsername/repository/branch
* Finds an installed dimension or returns null.
*
* @param dimension the dimension id such as overworld or flat
* @return the IrisDimension or null
@@ -105,18 +100,9 @@ public class IrisToolbelt {
}
if (pack == null) {
IrisServices.get(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender(), BukkitPlatform.volmitPlugin().getTag()), reference.pack(), false);
String installedPackName = installedPackName(reference.pack());
File found = PackDirectoryResolver.resolveExisting(packsFolder, installedPackName);
if (found == null) {
found = findCaseInsensitivePack(packsFolder, installedPackName);
}
if (found != null) {
pack = found;
}
}
if (pack == null) {
IrisLogging.warn("Iris pack '" + reference.pack()
+ "' is not installed. Install it with /iris download " + reference.pack()
+ " and restart the server.");
return null;
}
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eDie Welt wurde anscheinend bereits aus bukkit.yml entfernt",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cbukkit.yml konnte wegen {value} nicht gespeichert werden",
"iris.bukkit.commandiris.set_debug": "§aDebug gesetzt auf: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aDownload des Pack: {pack} (Beta-Version){value}",
"iris.bukkit.commandiris.downloading_pack": "§aDownload des Pack: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cDu musst dich in einer Iris-Welt befinden",
"iris.bukkit.commandiris.sending_metrics": "§aÜbermittlung von Metriken...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Pack '{key}' wurde nicht in der Pack-Liste gefunden.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Verwende /iris download <pack> branch=<branch>, um den Download manuell auszuführen.",
"iris.bukkit.runtime.studiosvc.failed_download": "Download von '{key}' fehlgeschlagen.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Download der Beta-Version IrisDimensions/overworld fehlgeschlagen.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Download von '{repo}' (Branch {branch}) fehlgeschlagen.",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Studio-Welt konnte nicht geöffnet werden: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Studio '{dimm}' kann nicht geöffnet werden das Pack enthält blockierende Fehler:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eParece que el mundo ya se había eliminado de bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cNo se pudo guardar bukkit.yml debido a {value}",
"iris.bukkit.commandiris.set_debug": "§aDepuración establecida en: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aDescargando pack: {pack} (versión beta){value}",
"iris.bukkit.commandiris.downloading_pack": "§aDescargando pack: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cDebes estar en un mundo de Iris",
"iris.bukkit.commandiris.sending_metrics": "§aEnviando métricas...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "El pack '{key}' no se encontró en la lista de packs.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Usa /iris download <pack> branch=<branch> para descargarlo manualmente.",
"iris.bukkit.runtime.studiosvc.failed_download": "No se pudo descargar '{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "No se pudo descargar la versión beta de IrisDimensions/overworld.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "No se pudo descargar '{repo}' (rama {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "No se pudo abrir el mundo de Studio: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "No se puede abrir Studio '{dimm}': el pack contiene errores bloqueantes:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eNäyttää siltä, että maailma on jo poistettu. bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cTallennus epäonnistui bukkit.yml koska {value}",
"iris.bukkit.commandiris.set_debug": "§aAseta vianetsintä: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aNoudetaan pakkaus: {pack} (beetan vapautuminen){value}",
"iris.bukkit.commandiris.downloading_pack": "§aNoudetaan pakkaus: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cSinun täytyy olla Iris maailma",
"iris.bukkit.commandiris.sending_metrics": "§aLähetetään mittareita...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Pakkaus{key}' ei löytynyt pakkauslistalta.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Käyttö /iris download <pack> branch=<branch> ladata käsin.",
"iris.bukkit.runtime.studiosvc.failed_download": "Ei voitu ladata \"{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Ei voitu ladata IrisDimensionsOverworld beta -julkaisu.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Ei voitu ladata \"{repo}\" (ala {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Studiomaailman avaaminen epäonnistui: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Studiota ei voi avata{dimm}\" - pakkauksessa on estovirheitä:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eIl semble que le monde avait déjà été supprimé de bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cImpossible d'enregistrer bukkit.yml à cause de {value}",
"iris.bukkit.commandiris.set_debug": "§aDébogage défini sur : {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aTéléchargement du pack : {pack} (version bêta){value}",
"iris.bukkit.commandiris.downloading_pack": "§aTéléchargement du pack : {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cVous devez être dans un monde Iris",
"iris.bukkit.commandiris.sending_metrics": "§aEnvoi des métriques...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Le pack '{key}' est introuvable dans la liste des packs.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Utilisez /iris download <pack> branch=<branch> pour le télécharger manuellement.",
"iris.bukkit.runtime.studiosvc.failed_download": "Impossible de télécharger '{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Impossible de télécharger la version bêta de IrisDimensions/overworld.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Impossible de télécharger '{repo}' (branche {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Impossible d'ouvrir le monde Studio : {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Impossible d'ouvrir Studio '{dimm}' : le pack contient des erreurs bloquantes :",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eנראה כאילו העולם כבר הוסר bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cנכשל להציל bukkit.yml בגלל {value}",
"iris.bukkit.commandiris.set_debug": "§aהגדר bug: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aהורדת חבילות: {pack} (שחרור בטא){value}",
"iris.bukkit.commandiris.downloading_pack": "§aהורדת חבילות: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cאתה חייב להיות בתוך Iris עולם העולם",
"iris.bukkit.commandiris.sending_metrics": "§aשליחת מדדים...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Pack »{key}\"לא נמצאו בחבילה.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "שימוש בשימוש /iris download <pack> branch=<branch> להוריד באופן ידני.",
"iris.bukkit.runtime.studiosvc.failed_download": "נכשל להוריד את \"{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "הורדת גרסת הבטא של IrisDimensions/overworld נכשלה.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "נכשל להוריד את \"{repo}(ברנץ' {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "נכשל לפתוח את עולם הסטודיו: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "\"לא יכול לפתוח סטודיו\"{dimm}חבילה חוסמת שגיאות:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eSembra che il mondo sia già stato rimosso da bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cImpossibile salvare bukkit.yml a causa di {value}",
"iris.bukkit.commandiris.set_debug": "§aDebug impostato su: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aDownload del pack: {pack} (versione beta){value}",
"iris.bukkit.commandiris.downloading_pack": "§aDownload del pack: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cDevi trovarti in un mondo Iris",
"iris.bukkit.commandiris.sending_metrics": "§aInvio di metriche...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Il Pack '{key}' non è stato trovato nell'elenco dei Pack.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Usa /iris download <pack> branch=<branch> per eseguire manualmente il download.",
"iris.bukkit.runtime.studiosvc.failed_download": "Download di '{key}' non riuscito.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Download della versione beta IrisDimensions/overworld non riuscito.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Download di '{repo}' (branch {branch}) non riuscito.",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Impossibile aprire il mondo studio: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Impossibile aprire lo studio '{dimm}': il Pack contiene errori bloccanti:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eワールドはすでに bukkit.yml から削除されているようです",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cbukkit.yml を保存できませんでした: {value}",
"iris.bukkit.commandiris.set_debug": "§aデバッグ設定: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aパックをダウンロードしています: {pack}(ベータリリース){value}",
"iris.bukkit.commandiris.downloading_pack": "§aパックをダウンロードしています: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cIris ワールド内にいる必要があります",
"iris.bukkit.commandiris.sending_metrics": "§aメトリックの送信...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "パック一覧にパック '{key}' が見つかりません。",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "手動でダウンロードするには /iris download <pack> branch=<branch> を使用してください。",
"iris.bukkit.runtime.studiosvc.failed_download": "'{key}' のダウンロードに失敗しました。",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "IrisDimensions/overworld のベータリリースをダウンロードできませんでした。",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "'{repo}'(ブランチ {branch})をダウンロードできませんでした。",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "スタジオワールドを開けませんでした: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "スタジオ '{dimm}' を開けません。パックに処理を妨げるエラーがあります:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§e세상과 같은 모습은 이미 제거되었습니다. bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§c{value} 때문에 bukkit.yml을 저장하지 못했습니다",
"iris.bukkit.commandiris.set_debug": "§a디버그 설정: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§a다운로드 팩: {pack} (베타 릴리스){value}",
"iris.bukkit.commandiris.downloading_pack": "§a다운로드 팩: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cIris 월드 안에 있어야 합니다",
"iris.bukkit.commandiris.sending_metrics": "§a메트릭 전송...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "팩 '{key}'는 팩 목록에서 찾을 수 없습니다.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "수동으로 다운로드하려면 /iris download <pack> branch=<branch>을(를) 사용하세요.",
"iris.bukkit.runtime.studiosvc.failed_download": "다운로드 실패 '{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "다운로드 실패 IrisDimensions/overworld 베타 릴리스.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "다운로드 실패 '{repo}' (branch {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "열린 스튜디오 세계에 실패: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "열린 스튜디오 '{dimm}' - 팩은 오류를 차단했습니다.",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eAtrodo, pasaulis jau buvo pašalintas iš bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cNepavyko įrašyti bukkit.yml dėl {value}",
"iris.bukkit.commandiris.set_debug": "§aNustatyti derinimą į: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aSiunčiama pakuotė: {pack} (beta atsipalaidavimas){value}",
"iris.bukkit.commandiris.downloading_pack": "§aSiunčiama pakuotė: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cJūs turite būti Iris pasaulis",
"iris.bukkit.commandiris.sending_metrics": "§aSiunčiami metrikos...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Pakuotė \"{key}\"nebuvo rasta pakuotės sąraše.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Naudojimas /iris download <pack> branch=<branch> atsisiųsti rankiniu būdu.",
"iris.bukkit.runtime.studiosvc.failed_download": "Nepavyko atsisiųsti \"{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Nepavyko atsisiųsti IrisDimensions/ overworld beta release.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Nepavyko atsisiųsti \"{repo}'(šaka {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Nepavyko atverti studijos pasaulio: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Nepavyko atverti studijos \"{dimm}\"- pakuotė turi blokavimo klaidų:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eHet lijkt erop dat de wereld al verwijderd was van bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cOpslaan is mislukt bukkit.yml vanwege {value}",
"iris.bukkit.commandiris.set_debug": "§aDebug instellen op: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aDownloadpakket: {pack} (beta vrijgeven){value}",
"iris.bukkit.commandiris.downloading_pack": "§aDownloadpakket: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cJe moet in een Iris wereld",
"iris.bukkit.commandiris.sending_metrics": "§aVerzenden van statistieken...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Verpakking{key}' werd niet gevonden in de verpakking lijst.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Gebruik /iris download <pack> branch=<branch> handmatig downloaden.",
"iris.bukkit.runtime.studiosvc.failed_download": "Downloaden mislukt{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Downloaden van de IrisDimensions/overworld bèta release.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Downloaden mislukt{repo}\" (tak {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Openen van studiowereld mislukt: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Kan studio niet openen '{dimm}' - Pack heeft blokkerende fouten:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eWygląda na to, że świat został już usunięty z bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cNie udało się zapisać bukkit.yml z powodu {value}",
"iris.bukkit.commandiris.set_debug": "§aUstaw debugowanie na: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aPobieranie opakowania: {pack} (uwalnianie beta){value}",
"iris.bukkit.commandiris.downloading_pack": "§aPobieranie opakowania: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cMusisz być w Iris świat",
"iris.bukkit.commandiris.sending_metrics": "§aWysyłanie wskaźników...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Paczka \"{key}'nie znaleziono go na liście.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Stosowanie /iris download <pack> branch=<branch> aby pobrać ręcznie.",
"iris.bukkit.runtime.studiosvc.failed_download": "Nie można pobrać '{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Nie udało się pobrać IrisDimensions/ Overworld beta wydania.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Nie można pobrać '{repo}'(oddział {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Nie udało się otworzyć studio świata: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Nie można otworzyć studia \"{dimm}\"- opakowanie ma błędy blokujące:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eParece que o mundo já foi removido de bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cFalha ao salvar bukkit.yml devido a {value}",
"iris.bukkit.commandiris.set_debug": "§aDefinir depuração como: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aBaixando pacote: {pack} (versão beta){value}",
"iris.bukkit.commandiris.downloading_pack": "§aBaixando pacote: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cVocê deve estar em um Iris mundo",
"iris.bukkit.commandiris.sending_metrics": "§aEnviando métricas...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Embalar '{key}' não foi encontrado na lista de pacotes.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Utilização /iris download <pack> branch=<branch> para baixar manualmente.",
"iris.bukkit.runtime.studiosvc.failed_download": "Falha ao transferir '{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Não foi possível transferir o IrisDimensions- Liberação beta.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Falha ao transferir '{repo}' (ramo {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Falha ao abrir o mundo do estúdio: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Não foi possível abrir o estúdio '{dimm}- a pack tem erros de bloqueio:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eПохоже, мир уже был удален из bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cНе удалось спасти bukkit.yml Потому что {value}",
"iris.bukkit.commandiris.set_debug": "§aУстановить отладку для: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aСкачать пакет: {pack} (бета-релиз){value}",
"iris.bukkit.commandiris.downloading_pack": "§aСкачать пакет: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cВы должны быть в Iris мир",
"iris.bukkit.commandiris.sending_metrics": "§aОтправка метрик...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Пакуй.{key}Не было найдено в списке упаковки.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Использовать /iris download <pack> branch=<branch> Скачать вручную.",
"iris.bukkit.runtime.studiosvc.failed_download": "Не удалось скачать{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Не удалось скачать IrisDimensions/overworld бета-версия.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Не удалось скачать{repo}' (ветка {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Не удалось открыть студию: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Не может открыть студию{dimm}У пакета есть ошибки блокировки:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eDünya gibi görünüyor zaten kaldırıldı bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§ckurtarmak için başarısız oldu bukkit.yml Çünkü {value}",
"iris.bukkit.commandiris.set_debug": "§aSet debug: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aİndirme paketi: {pack} (Bea release){value}",
"iris.bukkit.commandiris.downloading_pack": "§aİndirme paketi: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cBirde olmalısın Iris dünya dünyası",
"iris.bukkit.commandiris.sending_metrics": "§ametrikleri gönderin...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Pack \"{key}\" paketin listesinde bulunamadı.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Kullanım Kullanımı /iris download <pack> branch=<branch> manuel olarak indirmek için.",
"iris.bukkit.runtime.studiosvc.failed_download": "indirmek için başarısız oldu '{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "indirmek için başarısız oldu IrisDimensions/overworld beta serbest bırakılması.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "indirmek için başarısız oldu '{repo}\" (branch {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Stüdyo dünyasını açmaya başarısız oldu: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Açık stüdyo \"{dimm}\" - paket hataları engelliyor:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§eCó vẻ như thế giới đã bị loại bỏ bukkit.yml",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§cLỗi lưu bukkit.yml bởi vì {value}",
"iris.bukkit.commandiris.set_debug": "§aĐặt lỗi là: {to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§aĐang tải về: {pack} (beta phát hành){value}",
"iris.bukkit.commandiris.downloading_pack": "§aĐang tải về: {pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§cChắc anh đang ở trong... Iris thế giới",
"iris.bukkit.commandiris.sending_metrics": "§aName...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "Gói '{key}Không tìm thấy trong danh sách gói hàng.",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "Dùng /iris download <pack> branch=<branch> tải về bằng tay.",
"iris.bukkit.runtime.studiosvc.failed_download": "Lỗi tải xuống{key}'.",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "Việc tải về bị lỗi IrisDimensionsSự giải phóng của thế giới Bta.",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "Lỗi tải xuống{repo}' (branch {branch}).",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "Lỗi mở thế giới studio: {error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "Không thể mở studio{dimm}- gói có lỗi chặn:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§e该世界似乎已从 bukkit.yml 中移除。",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§c无法保存 bukkit.yml{value}",
"iris.bukkit.commandiris.set_debug": "§a调试模式已设为:{to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§a正在下载包:{pack}Beta 版本){value}",
"iris.bukkit.commandiris.downloading_pack": "§a正在下载包:{pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§c你必须位于 Iris 世界中。",
"iris.bukkit.commandiris.sending_metrics": "§a正在发送指标...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "内容包“{key}”不在内容包列表中。",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "使用 /iris download <pack> branch=<branch> 手动下载。",
"iris.bukkit.runtime.studiosvc.failed_download": "无法下载“{key}”。",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "无法下载 IrisDimensions/overworld 测试版。",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "无法下载“{repo}”(分支 {branch})。",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "无法打开工作室世界:{error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "无法打开工作室“{dimm}”:内容包存在阻断错误:",
@@ -179,7 +179,6 @@
"iris.bukkit.commandiris.looks_like_world_was_already_removed_from_bukkit_yml": "§e該世界似乎已從 bukkit.yml 中移除。",
"iris.bukkit.commandiris.failed_save_bukkit_yml_because": "§c無法儲存 bukkit.yml{value}",
"iris.bukkit.commandiris.set_debug": "§a偵錯模式已設為:{to}",
"iris.bukkit.commandiris.downloading_pack_beta_release": "§a正在下載包:{pack}Beta 版本){value}",
"iris.bukkit.commandiris.downloading_pack": "§a正在下載包:{pack}/{branch}{value}",
"iris.bukkit.commandiris.you_must_be_iris_world": "§c你必須位於 Iris 世界中。",
"iris.bukkit.commandiris.sending_metrics": "§a正在傳送指標...",
@@ -1153,7 +1152,6 @@
"iris.bukkit.runtime.studiosvc.pack_was_not_found_pack_listing": "內容包「{key}」不在內容包清單中。",
"iris.bukkit.runtime.studiosvc.use_iris_download_pack_branch_branch_download_manually": "使用 /iris download <pack> branch=<branch> 手動下載。",
"iris.bukkit.runtime.studiosvc.failed_download": "無法下載「{key}」。",
"iris.bukkit.runtime.studiosvc.failed_download_irisdimensions_overworld_beta_release": "無法下載 IrisDimensions/overworld 測試版。",
"iris.bukkit.runtime.studiosvc.failed_download_branch": "無法下載「{repo}」(分支 {branch})。",
"iris.bukkit.runtime.studiosvc.failed_open_studio_world": "無法開啟工作室世界:{error}",
"iris.bukkit.runtime.studiosvc.cannot_open_studio_pack_has_blocking_errors": "無法開啟工作室「{dimm}」:內容包存在阻斷錯誤:",
@@ -37,23 +37,45 @@ import static org.junit.Assert.assertTrue;
public class DefaultPackBootstrapProvisionerTest {
@Test
public void defaultBetaSourcesArePinnedPerRequiredPack() {
Map<String, DefaultPackBootstrapProvisioner.PackSpec> packs = new LinkedHashMap<>();
for (DefaultPackBootstrapProvisioner.PackSpec pack : DefaultPackBootstrapProvisioner.defaultPacks()) {
packs.put(pack.key(), pack);
}
public void startupDoesNotRequireOrDownloadDefaultPacks() {
assertTrue(DefaultPackBootstrapProvisioner.defaultPacks().isEmpty());
}
assertEquals(2, packs.size());
assertEquals(
URI.create("https://github.com/IrisDimensions/overworld/releases/download/beta/overworld.zip"),
packs.get("overworld").source()
);
assertEquals("overworld", packs.get("overworld").requiredDimension());
assertEquals(
URI.create("https://github.com/IrisDimensions/underworld/releases/download/beta/underworld.zip"),
packs.get("underworld").source()
);
assertEquals("underworld", packs.get("underworld").requiredDimension());
@Test
public void emptyInstallPublishesValidDatapackWithoutNetworkRequests() throws Exception {
AtomicInteger requests = new AtomicInteger();
HttpServer server = server(packArchive("overworld", "unused"), requests);
Path root = Files.createTempDirectory("iris-bootstrap-empty");
try {
Path dataDirectory = root.resolve("plugins/Iris");
DefaultPackBootstrapProvisioner.ProvisionOptions options = new DefaultPackBootstrapProvisioner.ProvisionOptions(
List.of(),
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2)).build(),
Clock.fixed(Instant.parse("2026-07-12T12:00:00Z"), ZoneOffset.UTC),
Duration.ofHours(1),
Duration.ofSeconds(2),
1,
Duration.ZERO,
8L * 1024L * 1024L,
root
);
DefaultPackBootstrapProvisioner.ProvisionResult result = DefaultPackBootstrapProvisioner.provision(
dataDirectory,
ignored -> {
},
options
);
assertEquals(DefaultPackBootstrapProvisioner.ProvisionStatus.INSTALLED, result.status());
assertTrue(result.packRoots().isEmpty());
assertEquals(0, requests.get());
assertTrue(Files.isRegularFile(result.datapackRoot().resolve("pack.mcmeta")));
assertTrue(DefaultPackBootstrapProvisioner.isProvisioned(dataDirectory, root, List.of()));
} finally {
server.stop(0);
delete(root);
}
}
@Test
@@ -31,8 +31,10 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Answers;
import com.sun.net.httpserver.HttpServer;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -44,6 +46,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -53,6 +56,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -71,6 +75,13 @@ public class PackDownloaderTest {
IrisPlatform platform = mock(IrisPlatform.class, Answers.CALLS_REAL_METHODS);
PlatformStructureHooks structureHooks = mock(PlatformStructureHooks.class, Answers.CALLS_REAL_METHODS);
when(platform.dataFolder()).thenReturn(temp.getRoot());
when(platform.dataFile(any(String[].class))).thenAnswer(invocation -> {
File file = temp.getRoot();
for (Object argument : invocation.getArguments()) {
file = new File(file, String.valueOf(argument));
}
return file;
});
when(platform.structureHooks()).thenReturn(structureHooks);
when(structureHooks.structureKeys()).thenReturn(List.of("minecraft:village"));
when(structureHooks.jigsawStructureKeys()).thenReturn(List.of("minecraft:village"));
@@ -90,23 +101,74 @@ public class PackDownloaderTest {
}
@Test
public void resolvesDefaultOverworldBetaRelease() {
assertEquals(
"https://github.com/IrisDimensions/overworld/releases/download/beta/overworld.zip",
PackDownloader.defaultOverworldReleaseUrl()
);
public void resolvesDefaultOverworldGitSource() {
assertEquals("IrisDimensions/overworld", PackDownloader.defaultOverworldRepository());
assertEquals("master", PackDownloader.defaultOverworldRef());
assertTrue(PackDownloader.isDefaultOverworld("overworld"));
assertTrue(PackDownloader.isManagedBetaPack("overworld"));
assertEquals(List.of("overworld", "underworld"), PackDownloader.managedBetaPacks());
assertTrue(PackDownloader.isManagedPack("overworld"));
assertEquals(List.of("overworld", "underworld"), PackDownloader.managedPacks());
}
@Test
public void resolvesUnderworldBetaRelease() {
assertEquals(
"https://github.com/IrisDimensions/underworld/releases/download/beta/underworld.zip",
PackDownloader.underworldReleaseUrl()
);
assertTrue(PackDownloader.isManagedBetaPack("underworld"));
public void resolvesUnderworldGitSource() {
assertEquals("IrisDimensions/underworld", PackDownloader.underworldRepository());
assertEquals("main", PackDownloader.underworldRef());
assertTrue(PackDownloader.isManagedPack("underworld"));
}
@Test
public void recognizesOnlyHttpZipUrlsAsDirectSources() {
assertTrue(PackDownloader.isDirectZipUrl("https://packs.example.test/overworld.zip"));
assertTrue(PackDownloader.isDirectZipUrl("http://127.0.0.1/pack.ZIP?token=value"));
assertFalse(PackDownloader.isDirectZipUrl("https://packs.example.test/overworld.tar.gz"));
assertFalse(PackDownloader.isDirectZipUrl("file:///tmp/overworld.zip"));
assertFalse(PackDownloader.isDirectZipUrl("not-a-url"));
}
@Test
public void directZipUrlInstallsSingleDimensionPackAndRequiresRestart() throws Exception {
Path archive = temp.newFile("direct-pack.zip").toPath();
Map<String, String> entries = new LinkedHashMap<>();
entries.put("README.md", "Direct pack");
entries.put("wrapped/dimensions/direct_pack.json", "{\"name\":\"Direct\",\"regions\":[\"local\"],"
+ "\"structures\":[{\"nativeStructures\":[{\"structure\":\"test:future_structure\"}]}],"
+ "\"logicalHeight\":256,\"dimensionHeight\":{\"min\":-64,\"max\":320}}");
entries.put("wrapped/regions/local.json", "{\"name\":\"Local\",\"landBiomes\":[\"local\"]}");
entries.put("wrapped/biomes/local.json", "{\"name\":\"Local\",\"derivative\":\"minecraft:plains\"}");
writeArchive(archive, entries);
byte[] response = Files.readAllBytes(archive);
AtomicInteger requests = new AtomicInteger();
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/direct-pack.zip", exchange -> {
requests.incrementAndGet();
exchange.sendResponseHeaders(200, response.length);
exchange.getResponseBody().write(response);
exchange.close();
});
server.start();
try {
File packsFolder = temp.newFolder("direct-url-packs");
String url = "http://127.0.0.1:" + server.getAddress().getPort() + "/direct-pack.zip";
PackDownloader.PackInstallResult result = PackDownloader.downloadUrl(
packsFolder,
url,
false,
ignored -> {
}
);
assertNotNull(result);
assertEquals("direct_pack", result.key());
assertTrue(result.changed());
assertTrue(result.restartRequired());
assertEquals(1, requests.get());
assertTrue(Files.isRegularFile(
packsFolder.toPath().resolve("direct_pack/dimensions/direct_pack.json")
));
} finally {
server.stop(0);
}
}
@Test
@@ -118,11 +180,11 @@ public class PackDownloaderTest {
writeDimension(packsFolder.toPath().resolve("underworld"), "underworld_roof");
assertTrue(PackDownloader.isPackPresent(packsFolder, "underworld"));
assertFalse(PackDownloader.isManagedBetaPackPresent(packsFolder, "underworld"));
assertFalse(PackDownloader.isManagedPackPresent(packsFolder, "underworld"));
writeDimension(packsFolder.toPath().resolve("underworld"), "underworld");
assertTrue(Files.isDirectory(dimensions));
assertTrue(PackDownloader.isManagedBetaPackPresent(packsFolder, "underworld"));
assertTrue(PackDownloader.isManagedPackPresent(packsFolder, "underworld"));
}
@Test
@@ -146,6 +208,7 @@ public class PackDownloaderTest {
assertNotNull(result);
assertTrue(result.changed());
assertTrue(result.restartRequired());
assertTrue(Files.isRegularFile(target.resolve("dimensions/underworld.json")));
assertTrue(Files.isRegularFile(target.resolve("dimensions/underworld_roof.json")));
assertFalse(Files.exists(target.resolve("partial.txt")));
@@ -157,9 +220,9 @@ public class PackDownloaderTest {
assertFalse(PackDownloader.isDefaultOverworld("theend"));
assertFalse(PackDownloader.isDefaultOverworld(""));
assertFalse(PackDownloader.isDefaultOverworld(null));
assertFalse(PackDownloader.isManagedBetaPack("theend"));
assertFalse(PackDownloader.isManagedBetaPack(""));
assertFalse(PackDownloader.isManagedBetaPack(null));
assertFalse(PackDownloader.isManagedPack("theend"));
assertFalse(PackDownloader.isManagedPack(""));
assertFalse(PackDownloader.isManagedPack(null));
}
@Test
@@ -299,7 +362,7 @@ public class PackDownloaderTest {
assertEquals(feedback.toString(), "replaceable", result.key());
assertTrue(result.changed());
assertFalse(result.restartRequired());
assertTrue(result.restartRequired());
assertEquals("new", Files.readString(target.toPath().resolve("state.txt"), StandardCharsets.UTF_8));
assertFalse(Files.exists(target.toPath().resolve("old-only.txt")));
assertTransactionStateClean(packsFolder);
@@ -1,78 +0,0 @@
/*
* Iris is a World Generator for Minecraft Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.core.service;
import org.junit.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
public class StudioSVCManagedBetaPackTest {
@Test
public void startupSelectsOnlyMissingManagedBetaPacks() throws IOException {
Path workspace = Files.createTempDirectory("iris-managed-beta-startup");
try {
assertEquals(
List.of("overworld", "underworld"),
StudioSVC.missingManagedBetaPacks(workspace.toFile())
);
createPack(workspace, "overworld");
assertEquals(
List.of("underworld"),
StudioSVC.missingManagedBetaPacks(workspace.toFile())
);
createDimension(workspace, "underworld", "underworld_roof");
assertEquals(
List.of("underworld"),
StudioSVC.missingManagedBetaPacks(workspace.toFile())
);
createPack(workspace, "underworld");
assertEquals(List.of(), StudioSVC.missingManagedBetaPacks(workspace.toFile()));
} finally {
deleteTree(workspace);
}
}
private static void createPack(Path workspace, String key) throws IOException {
createDimension(workspace, key, key);
}
private static void createDimension(Path workspace, String folder, String key) throws IOException {
Path dimensions = Files.createDirectories(workspace.resolve(folder).resolve("dimensions"));
Files.writeString(dimensions.resolve(key + ".json"), "{}", StandardCharsets.UTF_8);
}
private static void deleteTree(Path root) throws IOException {
try (Stream<Path> paths = Files.walk(root)) {
for (Path path : paths.sorted(Comparator.comparingInt(Path::getNameCount).reversed()).toList()) {
Files.deleteIfExists(path);
}
}
}
}
@@ -0,0 +1,24 @@
package art.arcane.iris.core.service;
import org.junit.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class StudioSVCPackDownloadContractTest {
@Test
public void downloadsRequireManualRestartWithoutMutatingLiveDatapacks() throws Exception {
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/service/StudioSVC.java"));
int mutationStart = source.indexOf("private void runPackMutation(");
int mutationEnd = source.indexOf("private boolean finishStandalonePackMutation(", mutationStart);
int finishEnd = source.indexOf("private void runOffPrimaryThread(", mutationEnd);
String downloadMutation = source.substring(mutationStart, finishEnd);
assertTrue(downloadMutation.contains("Restart the server before using the downloaded Iris pack."));
assertFalse(downloadMutation.contains("ServerConfigurator.restart()"));
assertFalse(downloadMutation.contains("installDataPacksIfChanged"));
}
}