mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 04:37:47 +00:00
s
This commit is contained in:
@@ -62,12 +62,13 @@ content selecting on `#minecraft:is_overworld` and friends.
|
||||
## Install
|
||||
|
||||
**Plugin (Paper/Purpur/Leaf/Canvas/Folia/Spigot):** drop the plugin jar into `plugins/` and start
|
||||
the server. On first boot Iris downloads the managed `overworld` and `underworld` beta packs automatically.
|
||||
the server. First boot performs no pack download. Run `/iris download overworld`,
|
||||
`/iris download underworld`, or `/iris download https://host/path/pack.zip`, then restart manually.
|
||||
|
||||
**Mod (Fabric/Forge/NeoForge):** drop the mod jar into `mods/` and start the server. The jar is
|
||||
self-contained (core, SPI, and required Fabric API modules are bundled). On first boot Iris
|
||||
prefetches the managed `overworld` and `underworld` beta packs and rebuilds the worldgen datapack;
|
||||
restart once if startup reports that registry-visible pack data was installed too late for that boot. Packs installed later register their custom dimension types
|
||||
self-contained (core, SPI, and required Fabric API modules are bundled). First boot compiles only
|
||||
packs already on disk and never accesses the network. `/iris download` installs a pack atomically
|
||||
without stopping the server; restart manually afterward. Packs register their custom dimension types
|
||||
(height ranges) and custom biomes through the forced datapack at server start - restart once after
|
||||
adding a pack so worlds get its full heights and biomes; worlds created before that restart run
|
||||
with fallback heights.
|
||||
|
||||
+2
-9
@@ -34,9 +34,7 @@ import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.object.IrisWorld;
|
||||
import art.arcane.iris.engine.platform.BukkitChunkGenerator;
|
||||
import art.arcane.iris.util.common.plugin.VolmitPlugin;
|
||||
import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||
import lombok.NonNull;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
@@ -177,13 +175,8 @@ public final class IrisWorldGeneratorResolver {
|
||||
+ " but its dimension failed to load; not redownloading. Fix or delete the pack folder.");
|
||||
return null;
|
||||
}
|
||||
Iris.warn("Unable to find dimension type " + id + " Looking for online packs...");
|
||||
Iris.service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, false);
|
||||
dimension = IrisData.loadAnyDimension(id, null);
|
||||
|
||||
if (dimension != null) {
|
||||
Iris.info("Resolved missing dimension, proceeding.");
|
||||
}
|
||||
Iris.warn("Unable to find dimension type " + id + ". Install its pack with /iris download "
|
||||
+ id + " and restart the server.");
|
||||
}
|
||||
|
||||
return dimension;
|
||||
|
||||
@@ -710,9 +710,14 @@ public class CommandIris implements DirectorExecutor {
|
||||
@Param(name = "overwrite", description = "Whether or not to overwrite the pack with the downloaded one", descriptionKey = "iris.director.commandiris.param.whether_not_overwrite_pack_with_downloaded_one", aliases = "force", defaultValue = "false")
|
||||
boolean overwrite
|
||||
) {
|
||||
if (PackDownloader.isManagedBetaPack(pack)) {
|
||||
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_DOWNLOADING_PACK_BETA_RELEASE, MessageArgument.untrusted("pack", pack), MessageArgument.trusted("value", overwrite ? IrisLanguage.text(RuntimeUiMessages.DOWNLOAD_OVERWRITE_SUFFIX) : "")));
|
||||
Iris.service(StudioSVC.class).downloadManagedBeta(sender(), pack, overwrite);
|
||||
if (PackDownloader.isDirectZipUrl(pack)) {
|
||||
sender().sendMessage("Downloading Iris pack from " + pack
|
||||
+ (overwrite ? IrisLanguage.text(RuntimeUiMessages.DOWNLOAD_OVERWRITE_SUFFIX) : "") + ".");
|
||||
Iris.service(StudioSVC.class).downloadUrl(sender(), pack, overwrite);
|
||||
} else if (PackDownloader.isManagedPack(pack)) {
|
||||
sender().sendMessage("Downloading managed Iris pack '" + pack + "' from its configured Git source"
|
||||
+ (overwrite ? IrisLanguage.text(RuntimeUiMessages.DOWNLOAD_OVERWRITE_SUFFIX) : "") + ".");
|
||||
Iris.service(StudioSVC.class).downloadManaged(sender(), pack, overwrite);
|
||||
} else {
|
||||
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_DOWNLOADING_PACK, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("branch", branch), MessageArgument.trusted("value", overwrite ? IrisLanguage.text(RuntimeUiMessages.DOWNLOAD_OVERWRITE_SUFFIX) : "")));
|
||||
Iris.service(StudioSVC.class).downloadSearch(sender(), "IrisDimensions/" + pack + "/" + branch, overwrite);
|
||||
|
||||
+1
-1
@@ -260,7 +260,7 @@ public final class ModdedEngineBootstrap {
|
||||
selfTest(moddedLoader.getClass().getClassLoader());
|
||||
bind();
|
||||
IrisLanguage.initialize();
|
||||
ModdedStartup.prefetchDefaultPack();
|
||||
ModdedStartup.prefetchStartupDatapack();
|
||||
if (!moddedLoader.clientEnvironment()) {
|
||||
MainWorldService.reconcileEarly();
|
||||
}
|
||||
|
||||
@@ -253,8 +253,6 @@ public final class ModdedForcedDatapack {
|
||||
}
|
||||
|
||||
private static Path write() throws IOException {
|
||||
// No ensureDefaultPack() here: write() is reachable from loadPacks on the first boot, and loadPacks
|
||||
// must never touch the network. ModdedStartup.prefetchDefaultPack covers the download at boot.
|
||||
String packsHash = packsHash();
|
||||
Path datapackRoot = datapackRoot();
|
||||
Files.createDirectories(datapackRoot);
|
||||
|
||||
@@ -33,17 +33,15 @@ public final class ModdedModConfig {
|
||||
private static volatile ModdedModConfig instance;
|
||||
|
||||
private final String defaultPack;
|
||||
private final boolean autoDownloadDefaultPack;
|
||||
private final String primaryWorld;
|
||||
private final boolean routePlayersToPrimaryWorld;
|
||||
private final String mainWorldPack;
|
||||
private final long mainWorldSeed;
|
||||
private final boolean mainWorldAutoRestart;
|
||||
|
||||
private ModdedModConfig(String defaultPack, boolean autoDownloadDefaultPack, String primaryWorld, boolean routePlayersToPrimaryWorld,
|
||||
private ModdedModConfig(String defaultPack, String primaryWorld, boolean routePlayersToPrimaryWorld,
|
||||
String mainWorldPack, long mainWorldSeed, boolean mainWorldAutoRestart) {
|
||||
this.defaultPack = defaultPack;
|
||||
this.autoDownloadDefaultPack = autoDownloadDefaultPack;
|
||||
this.primaryWorld = primaryWorld == null ? "" : primaryWorld.trim();
|
||||
this.routePlayersToPrimaryWorld = routePlayersToPrimaryWorld;
|
||||
this.mainWorldPack = mainWorldPack == null ? "" : mainWorldPack.trim();
|
||||
@@ -68,7 +66,7 @@ public final class ModdedModConfig {
|
||||
public static void setPrimaryWorld(String dimensionId) {
|
||||
synchronized (LOCK) {
|
||||
ModdedModConfig current = get();
|
||||
ModdedModConfig updated = new ModdedModConfig(current.defaultPack, current.autoDownloadDefaultPack, dimensionId, current.routePlayersToPrimaryWorld,
|
||||
ModdedModConfig updated = new ModdedModConfig(current.defaultPack, dimensionId, current.routePlayersToPrimaryWorld,
|
||||
current.mainWorldPack, current.mainWorldSeed, current.mainWorldAutoRestart);
|
||||
instance = updated;
|
||||
write(configFile(), updated);
|
||||
@@ -78,7 +76,7 @@ public final class ModdedModConfig {
|
||||
public static void setMainWorld(String packRef, long seed) {
|
||||
synchronized (LOCK) {
|
||||
ModdedModConfig current = get();
|
||||
ModdedModConfig updated = new ModdedModConfig(current.defaultPack, current.autoDownloadDefaultPack, current.primaryWorld, current.routePlayersToPrimaryWorld,
|
||||
ModdedModConfig updated = new ModdedModConfig(current.defaultPack, current.primaryWorld, current.routePlayersToPrimaryWorld,
|
||||
packRef == null ? "" : packRef.trim(), seed, current.mainWorldAutoRestart);
|
||||
instance = updated;
|
||||
write(configFile(), updated);
|
||||
@@ -89,10 +87,6 @@ public final class ModdedModConfig {
|
||||
return defaultPack;
|
||||
}
|
||||
|
||||
public boolean autoDownloadDefaultPack() {
|
||||
return autoDownloadDefaultPack;
|
||||
}
|
||||
|
||||
public String primaryWorld() {
|
||||
return primaryWorld;
|
||||
}
|
||||
@@ -119,7 +113,7 @@ public final class ModdedModConfig {
|
||||
|
||||
private static ModdedModConfig load() {
|
||||
Path file = configFile();
|
||||
ModdedModConfig defaults = new ModdedModConfig("overworld", true, "", true, "", 0L, false);
|
||||
ModdedModConfig defaults = new ModdedModConfig("overworld", "", true, "", 0L, false);
|
||||
if (!Files.isRegularFile(file)) {
|
||||
write(file, defaults);
|
||||
return defaults;
|
||||
@@ -128,7 +122,6 @@ public final class ModdedModConfig {
|
||||
JSONObject json = new JSONObject(Files.readString(file, StandardCharsets.UTF_8));
|
||||
return new ModdedModConfig(
|
||||
json.optString("defaultPack", defaults.defaultPack),
|
||||
json.optBoolean("autoDownloadDefaultPack", defaults.autoDownloadDefaultPack),
|
||||
json.optString("primaryWorld", defaults.primaryWorld),
|
||||
json.optBoolean("routePlayersToPrimaryWorld", defaults.routePlayersToPrimaryWorld),
|
||||
json.optString("mainWorldPack", defaults.mainWorldPack),
|
||||
@@ -143,7 +136,6 @@ public final class ModdedModConfig {
|
||||
private static void write(Path file, ModdedModConfig config) {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("defaultPack", config.defaultPack);
|
||||
json.put("autoDownloadDefaultPack", config.autoDownloadDefaultPack);
|
||||
json.put("primaryWorld", config.primaryWorld);
|
||||
json.put("routePlayersToPrimaryWorld", config.routePlayersToPrimaryWorld);
|
||||
json.put("mainWorldPack", config.mainWorldPack);
|
||||
|
||||
+5
-5
@@ -48,7 +48,7 @@ public final class ModdedPackInstaller {
|
||||
feedback.accept(IrisLanguage.plain(PackDownloadMessages.INVALID_PACK_NAME, MessageArgument.untrusted("pack", String.valueOf(pack))));
|
||||
return false;
|
||||
}
|
||||
boolean managedBeta = PackDownloader.isManagedBetaPack(pack);
|
||||
boolean managed = PackDownloader.isManagedPack(pack);
|
||||
if (!acceptsBranch(pack, branch)) {
|
||||
feedback.accept(IrisLanguage.plain(PackDownloadMessages.INVALID_BRANCH_NAME, MessageArgument.untrusted("branch", String.valueOf(branch))));
|
||||
return false;
|
||||
@@ -58,8 +58,8 @@ public final class ModdedPackInstaller {
|
||||
synchronized (installLock) {
|
||||
File packs = configDir.resolve("irisworldgen").resolve("packs").toFile();
|
||||
try {
|
||||
PackDownloader.PackInstallResult result = managedBeta
|
||||
? PackDownloader.downloadManagedBeta(packs, pack, forceOverwrite, feedback)
|
||||
PackDownloader.PackInstallResult result = managed
|
||||
? PackDownloader.downloadManaged(packs, pack, forceOverwrite, feedback)
|
||||
: PackDownloader.download(
|
||||
packs,
|
||||
"IrisDimensions/" + pack,
|
||||
@@ -84,7 +84,7 @@ public final class ModdedPackInstaller {
|
||||
}
|
||||
return installed;
|
||||
} catch (IOException error) {
|
||||
String source = managedBeta ? "beta release" : "branch " + branch;
|
||||
String source = managed ? "configured Git source" : "branch " + branch;
|
||||
LOGGER.error("Iris pack download failed for IrisDimensions/{} ({})", pack, source, error);
|
||||
feedback.accept(IrisLanguage.plain(
|
||||
PackDownloadMessages.DOWNLOAD_FAILED,
|
||||
@@ -97,7 +97,7 @@ public final class ModdedPackInstaller {
|
||||
}
|
||||
|
||||
static boolean acceptsBranch(String pack, String branch) {
|
||||
return PackDownloader.isManagedBetaPack(pack)
|
||||
return PackDownloader.isManagedPack(pack)
|
||||
|| (branch != null && BRANCH_NAME.matcher(branch).matches());
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -47,7 +46,6 @@ public final class ModdedStartup {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
|
||||
private static final AtomicBoolean PREPARED = new AtomicBoolean(false);
|
||||
private static final AtomicBoolean STARTED = new AtomicBoolean(false);
|
||||
private static final Object PACK_LOCK = new Object();
|
||||
|
||||
private ModdedStartup() {
|
||||
}
|
||||
@@ -69,14 +67,13 @@ public final class ModdedStartup {
|
||||
* ModdedEngineBootstrap.start clears the async queue at SERVER_STARTING, which would silently drop this
|
||||
* one-shot task, and the datapack must be regenerated before the level PackRepository reload if it can.
|
||||
*/
|
||||
public static void prefetchDefaultPack() {
|
||||
Thread thread = new Thread(ModdedStartup::refreshPacksAndDatapack, "iris-modded-pack-prefetch");
|
||||
public static void prefetchStartupDatapack() {
|
||||
Thread thread = new Thread(ModdedStartup::refreshDatapack, "iris-modded-datapack-prefetch");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private static void refreshPacksAndDatapack() {
|
||||
ensureDefaultPack();
|
||||
private static void refreshDatapack() {
|
||||
try {
|
||||
ModdedForcedDatapack.regenerateIfStale("boot");
|
||||
} catch (Throwable failure) {
|
||||
@@ -98,10 +95,10 @@ public final class ModdedStartup {
|
||||
|
||||
ModdedScheduler scheduler = ModdedEngineBootstrap.schedulerOrNull();
|
||||
if (scheduler == null) {
|
||||
refreshPacksAndDatapack();
|
||||
refreshDatapack();
|
||||
return;
|
||||
}
|
||||
scheduler.async(ModdedStartup::refreshPacksAndDatapack);
|
||||
scheduler.async(ModdedStartup::refreshDatapack);
|
||||
}
|
||||
|
||||
public static void validateAllPacks() {
|
||||
@@ -248,45 +245,4 @@ public final class ModdedStartup {
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureDefaultPack() {
|
||||
synchronized (PACK_LOCK) {
|
||||
ModdedModConfig config = ModdedModConfig.get();
|
||||
if (!config.autoDownloadDefaultPack()) {
|
||||
return;
|
||||
}
|
||||
Path configDir = ModdedEngineBootstrap.loader().configDir();
|
||||
File packsRoot = ModdedPackCommands.packsRoot();
|
||||
for (String pack : startupPacks(config.defaultPack())) {
|
||||
ensureStartupPack(configDir, packsRoot, pack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> startupPacks(String configuredDefault) {
|
||||
LinkedHashSet<String> packs = new LinkedHashSet<>(PackDownloader.managedBetaPacks());
|
||||
if (configuredDefault != null && !configuredDefault.isBlank()) {
|
||||
packs.add(configuredDefault);
|
||||
}
|
||||
return List.copyOf(packs);
|
||||
}
|
||||
|
||||
private static void ensureStartupPack(Path configDir, File packsRoot, String pack) {
|
||||
boolean present = PackDownloader.isManagedBetaPack(pack)
|
||||
? PackDownloader.isManagedBetaPackPresent(packsRoot, pack)
|
||||
: PackDownloader.isPackPresent(packsRoot, pack);
|
||||
if (present) {
|
||||
return;
|
||||
}
|
||||
boolean managedBeta = PackDownloader.isManagedBetaPack(pack);
|
||||
String branch = managedBeta ? "beta" : "master";
|
||||
String source = managedBeta ? "beta release" : "master branch";
|
||||
String role = managedBeta ? "managed beta pack" : "default pack";
|
||||
LOGGER.info("Iris {} '{}' missing; downloading IrisDimensions/{} ({})", role, pack, pack, source);
|
||||
boolean installed = ModdedPackInstaller.install(
|
||||
configDir, pack, branch, false, false,
|
||||
(String line) -> LOGGER.info("Iris: {}", line));
|
||||
if (!installed) {
|
||||
LOGGER.warn("Iris {} '{}' could not be downloaded; install it with /iris download {}", role, pack, pack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
-6
@@ -49,6 +49,8 @@ import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -258,8 +260,10 @@ public final class IrisModdedCommands {
|
||||
|
||||
static int download(CommandSourceStack source, String pack,
|
||||
String branch, boolean forceOverwrite) {
|
||||
boolean managedBeta = PackDownloader.isManagedBetaPack(pack);
|
||||
String baseDownloadSource = managedBeta ? "beta release" : "branch " + branch;
|
||||
boolean managed = PackDownloader.isManagedPack(pack);
|
||||
boolean directUrl = PackDownloader.isDirectZipUrl(pack);
|
||||
String baseDownloadSource = directUrl ? "direct ZIP URL"
|
||||
: managed ? "configured Git source" : "branch " + branch;
|
||||
String downloadSource = forceOverwrite
|
||||
? baseDownloadSource + IrisLanguage.plain(RuntimeUiMessages.DOWNLOAD_OVERWRITE_SUFFIX)
|
||||
: baseDownloadSource;
|
||||
@@ -273,11 +277,28 @@ public final class IrisModdedCommands {
|
||||
return 0;
|
||||
}
|
||||
scheduler.async(() -> {
|
||||
boolean installed = ModdedPackInstaller.install(
|
||||
ModdedEngineBootstrap.loader().configDir(), pack, branch, forceOverwrite, true,
|
||||
(String message) -> scheduler.global(() -> ok(source, message)));
|
||||
boolean installed;
|
||||
if (directUrl) {
|
||||
File packs = ModdedPackCommands.packsRoot();
|
||||
try {
|
||||
PackDownloader.PackInstallResult result = PackDownloader.downloadUrl(
|
||||
packs,
|
||||
pack,
|
||||
forceOverwrite,
|
||||
(String message) -> scheduler.global(() -> ok(source, message))
|
||||
);
|
||||
installed = result != null;
|
||||
} catch (IOException error) {
|
||||
LOGGER.error("Iris pack download failed for direct URL {}", pack, error);
|
||||
installed = false;
|
||||
}
|
||||
} else {
|
||||
installed = ModdedPackInstaller.install(
|
||||
ModdedEngineBootstrap.loader().configDir(), pack, branch, forceOverwrite, false,
|
||||
(String message) -> scheduler.global(() -> ok(source, message)));
|
||||
}
|
||||
if (installed) {
|
||||
scheduler.global(() -> ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PACK_INSTALLED_ITS_EXACT_DIMENSION_TYPES_CUSTOM_BIOMES_JOIN_FORCED, MessageArgument.untrusted("pack", pack))));
|
||||
scheduler.global(() -> ok(source, "Pack installed on disk. Restart the server before using it."));
|
||||
} else {
|
||||
scheduler.global(() -> fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PACK_DOWNLOAD_FAILED_SEE_CONSOLE, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("downloadSource", downloadSource))));
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedPackInstallerTest {
|
||||
@Test
|
||||
public void managedBetaPacksIgnoreTheRequestedBranch() {
|
||||
public void managedPacksIgnoreTheRequestedBranch() {
|
||||
assertTrue(ModdedPackInstaller.acceptsBranch("overworld", null));
|
||||
assertTrue(ModdedPackInstaller.acceptsBranch("underworld", "feature/arbitrary"));
|
||||
}
|
||||
|
||||
-41
@@ -1,41 +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.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ModdedStartupPackSelectionTest {
|
||||
@Test
|
||||
public void managedBetaPacksAreAlwaysInstalledInStableOrder() {
|
||||
assertEquals(List.of("overworld", "underworld"), ModdedStartup.startupPacks("overworld"));
|
||||
assertEquals(List.of("overworld", "underworld"), ModdedStartup.startupPacks("underworld"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuredNonManagedDefaultIsInstalledAfterManagedBetaPacks() {
|
||||
assertEquals(
|
||||
List.of("overworld", "underworld", "custom"),
|
||||
ModdedStartup.startupPacks("custom")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-5
@@ -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}」:內容包存在阻斷錯誤:",
|
||||
|
||||
+38
-16
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -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"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user