mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-29 05:20:40 +00:00
f
This commit is contained in:
@@ -60,6 +60,7 @@ import java.nio.file.Files;
|
|||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -134,6 +135,12 @@ public class CommandIris implements DirectorExecutor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<String> startupDenial = ServerConfigurator.worldCreationDenialReason(false);
|
||||||
|
if (startupDenial.isPresent()) {
|
||||||
|
sender().sendMessage(C.YELLOW + startupDenial.get());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String resolvedType = type.equalsIgnoreCase("default")
|
String resolvedType = type.equalsIgnoreCase("default")
|
||||||
? IrisSettings.get().getGenerator().getDefaultWorldType()
|
? IrisSettings.get().getGenerator().getDefaultWorldType()
|
||||||
: type;
|
: type;
|
||||||
|
|||||||
+4
-2
@@ -120,9 +120,11 @@ public class CommandStudio implements DirectorExecutor {
|
|||||||
@Param(description = "The dimension pack to open a studio for", descriptionKey = "iris.director.commandstudio.param.dimension_pack_open_studio", aliases = "dim", customHandler = DimensionHandler.class)
|
@Param(description = "The dimension pack to open a studio for", descriptionKey = "iris.director.commandstudio.param.dimension_pack_open_studio", aliases = "dim", customHandler = DimensionHandler.class)
|
||||||
IrisDimension dimension,
|
IrisDimension dimension,
|
||||||
@Param(defaultValue = "1337", description = "The seed to generate the studio with", descriptionKey = "iris.director.commandstudio.param.seed_generate_studio_with", aliases = "s")
|
@Param(defaultValue = "1337", description = "The seed to generate the studio with", descriptionKey = "iris.director.commandstudio.param.seed_generate_studio_with", aliases = "s")
|
||||||
long seed) {
|
long seed,
|
||||||
|
@Param(defaultValue = "false", description = "Attempt to open Studio before a required registry restart", aliases = "f")
|
||||||
|
boolean force) {
|
||||||
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_OPENING_STUDIO_PACK_SEED, MessageArgument.untrusted("value", dimension.getName()), MessageArgument.untrusted("seed", seed)));
|
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_OPENING_STUDIO_PACK_SEED, MessageArgument.untrusted("value", dimension.getName()), MessageArgument.untrusted("seed", seed)));
|
||||||
Iris.service(StudioSVC.class).open(sender(), seed, dimension.getLoadKey());
|
Iris.service(StudioSVC.class).open(sender(), seed, dimension.getLoadKey(), force);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Director(description = "Import vanilla trees, mushrooms & objects (and structures/jigsaw) from the server into a pack's objects/vanilla folder", descriptionKey = "iris.director.commandstudio.director.import_vanilla_trees_mushrooms_objects_structures_jigsaw_from_server_into_pack_s", aliases = {"importv", "iv"}, origin = DirectorOrigin.BOTH)
|
@Director(description = "Import vanilla trees, mushrooms & objects (and structures/jigsaw) from the server into a pack's objects/vanilla folder", descriptionKey = "iris.director.commandstudio.director.import_vanilla_trees_mushrooms_objects_structures_jigsaw_from_server_into_pack_s", aliases = {"importv", "iv"}, origin = DirectorOrigin.BOTH)
|
||||||
|
|||||||
+16
@@ -8,9 +8,11 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Parameter;
|
import java.lang.reflect.Parameter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class CommandStudioCreationContractTest {
|
public class CommandStudioCreationContractTest {
|
||||||
@Test
|
@Test
|
||||||
@@ -24,4 +26,18 @@ public class CommandStudioCreationContractTest {
|
|||||||
assertEquals("null", template.defaultValue());
|
assertEquals("null", template.defaultValue());
|
||||||
assertEquals(NullableDimensionHandler.class, template.customHandler());
|
assertEquals(NullableDimensionHandler.class, template.customHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void openExposesAnOptInForceFlag() throws NoSuchMethodException {
|
||||||
|
Method command = CommandStudio.class.getDeclaredMethod(
|
||||||
|
"open",
|
||||||
|
IrisDimension.class,
|
||||||
|
long.class,
|
||||||
|
boolean.class
|
||||||
|
);
|
||||||
|
Param force = command.getParameters()[2].getAnnotation(Param.class);
|
||||||
|
|
||||||
|
assertEquals("false", force.defaultValue());
|
||||||
|
assertTrue(List.of(force.aliases()).contains("f"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,21 @@ public final class IrisStartupValidation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<String> denialReason() {
|
public static Optional<String> denialReason() {
|
||||||
|
return denialReason(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<String> studioDenialReason(boolean force) {
|
||||||
Snapshot current = snapshot;
|
Snapshot current = snapshot;
|
||||||
|
if (force
|
||||||
|
&& current.enforced()
|
||||||
|
&& current.datapacks() == ValidationState.RESTART_REQUIRED
|
||||||
|
&& current.packs() == ValidationState.READY) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return denialReason(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<String> denialReason(Snapshot current) {
|
||||||
if (!current.enforced() || isReady(current)) {
|
if (!current.enforced() || isReady(current)) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
@@ -171,12 +172,34 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void requireDatapackRestart() {
|
public static void requireDatapackRestart() {
|
||||||
|
requireWorldCreationRestart();
|
||||||
|
IrisStartupValidation.requireRestart(
|
||||||
|
"Iris datapack changes require a restart before player admission or world creation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void requireWorldCreationRestart() {
|
||||||
synchronized (DATAPACK_INSTALL_LOCK) {
|
synchronized (DATAPACK_INSTALL_LOCK) {
|
||||||
invalidateLoadedDatapackRuntime();
|
invalidateLoadedDatapackRuntime();
|
||||||
loadedDatapackRestartRequired = true;
|
loadedDatapackRestartRequired = true;
|
||||||
}
|
}
|
||||||
IrisStartupValidation.requireRestart(
|
}
|
||||||
"Iris datapack changes require a restart before player admission or world creation.");
|
|
||||||
|
public static Optional<String> worldCreationDenialReason(boolean forceStudio) {
|
||||||
|
Optional<String> startupDenial = IrisStartupValidation.studioDenialReason(forceStudio);
|
||||||
|
if (startupDenial.isPresent()) {
|
||||||
|
return startupDenial;
|
||||||
|
}
|
||||||
|
if (!forceStudio && loadedDatapackRestartRequired) {
|
||||||
|
return Optional.of("Iris installed or updated a dimension pack that requires a server restart before world creation or Studio open.");
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void requireWorldCreationReady(boolean forceStudio) {
|
||||||
|
Optional<String> denial = worldCreationDenialReason(forceStudio);
|
||||||
|
if (denial.isPresent()) {
|
||||||
|
throw new IllegalStateException("Iris world creation is locked: " + denial.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void restoreLoadedDatapackRuntimeIfUnchanged(
|
public static void restoreLoadedDatapackRuntimeIfUnchanged(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package art.arcane.iris.core.lifecycle;
|
|||||||
|
|
||||||
public enum WorldLifecycleCaller {
|
public enum WorldLifecycleCaller {
|
||||||
STUDIO,
|
STUDIO,
|
||||||
|
FORCED_STUDIO,
|
||||||
CREATE,
|
CREATE,
|
||||||
BENCHMARK
|
BENCHMARK
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package art.arcane.iris.core.lifecycle;
|
package art.arcane.iris.core.lifecycle;
|
||||||
|
|
||||||
import art.arcane.iris.core.IrisStartupValidation;
|
|
||||||
import art.arcane.iris.core.ServerConfigurator;
|
import art.arcane.iris.core.ServerConfigurator;
|
||||||
import art.arcane.iris.spi.IrisLogging;
|
import art.arcane.iris.spi.IrisLogging;
|
||||||
import art.arcane.iris.core.tools.IrisToolbelt;
|
import art.arcane.iris.core.tools.IrisToolbelt;
|
||||||
@@ -77,7 +76,11 @@ public final class WorldLifecycleService {
|
|||||||
public CompletableFuture<World> create(WorldLifecycleRequest request) {
|
public CompletableFuture<World> create(WorldLifecycleRequest request) {
|
||||||
WorldLifecycleBackend backend;
|
WorldLifecycleBackend backend;
|
||||||
try {
|
try {
|
||||||
IrisStartupValidation.requireWorldCreationReady();
|
if (request.callerKind() == WorldLifecycleCaller.FORCED_STUDIO) {
|
||||||
|
ServerConfigurator.requireWorldCreationReady(true);
|
||||||
|
} else {
|
||||||
|
ServerConfigurator.requireWorldCreationReady(false);
|
||||||
|
}
|
||||||
backend = selectCreateBackend(request);
|
backend = selectCreateBackend(request);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
IrisLogging.reportError("WorldLifecycle create backend selection failed for world=\"" + request.worldName()
|
IrisLogging.reportError("WorldLifecycle create backend selection failed for world=\"" + request.worldName()
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public final class StudioOpenCoordinator {
|
|||||||
return CompletableFuture.failedFuture(new IllegalStateException(
|
return CompletableFuture.failedFuture(new IllegalStateException(
|
||||||
"Studio entry point could not be resolved."));
|
"Studio entry point could not be resolved."));
|
||||||
}
|
}
|
||||||
CompletableFuture<Boolean> teleport = project.getActiveOpenKind() == StudioOpenKind.STANDARD
|
CompletableFuture<Boolean> teleport = project.getActiveOpenKind().teleportThroughStandardEntry()
|
||||||
? WorldRuntimeControlService.get().teleportInMode(player, entry, GameMode.SPECTATOR)
|
? WorldRuntimeControlService.get().teleportInMode(player, entry, GameMode.SPECTATOR)
|
||||||
: WorldRuntimeControlService.get().teleport(player, entry);
|
: WorldRuntimeControlService.get().teleport(player, entry);
|
||||||
if (teleport == null) {
|
if (teleport == null) {
|
||||||
@@ -914,6 +914,10 @@ public final class StudioOpenCoordinator {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
IrisCreator.DatapackPreparation.REUSE_LOADED_RUNTIME_IF_READY),
|
IrisCreator.DatapackPreparation.REUSE_LOADED_RUNTIME_IF_READY),
|
||||||
|
FORCED_STANDARD(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
IrisCreator.DatapackPreparation.FORCE_REUSE_LOADED_RUNTIME),
|
||||||
OBJECT(
|
OBJECT(
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -420,6 +420,7 @@ public class StudioSVC implements IrisService {
|
|||||||
reporter.fail(null);
|
reporter.fail(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
retainPackRestartRequirement(result);
|
||||||
reporter.succeed(result);
|
reporter.succeed(result);
|
||||||
}, "Failed to download built-in Iris pack '" + key + "'.");
|
}, "Failed to download built-in Iris pack '" + key + "'.");
|
||||||
}
|
}
|
||||||
@@ -447,6 +448,7 @@ public class StudioSVC implements IrisService {
|
|||||||
reporter.fail(null);
|
reporter.fail(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
retainPackRestartRequirement(result);
|
||||||
reporter.succeed(result);
|
reporter.succeed(result);
|
||||||
}, "Failed to download Iris pack.");
|
}, "Failed to download Iris pack.");
|
||||||
}
|
}
|
||||||
@@ -491,8 +493,15 @@ public class StudioSVC implements IrisService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void open(VolmitSender sender, long seed, String dimm) {
|
public void open(VolmitSender sender, long seed, String dimm) {
|
||||||
|
open(sender, seed, dimm, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open(VolmitSender sender, long seed, String dimm, boolean force) {
|
||||||
try {
|
try {
|
||||||
open(sender, seed, dimm, (w) -> {
|
StudioOpenCoordinator.StudioOpenKind openKind = force
|
||||||
|
? StudioOpenCoordinator.StudioOpenKind.FORCED_STANDARD
|
||||||
|
: StudioOpenCoordinator.StudioOpenKind.STANDARD;
|
||||||
|
open(sender, seed, dimm, openKind, (w) -> {
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
IrisLogging.reportError("Failed to open studio world \"" + dimm + "\".", e);
|
IrisLogging.reportError("Failed to open studio world \"" + dimm + "\".", e);
|
||||||
@@ -501,8 +510,13 @@ public class StudioSVC implements IrisService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static BrokenPackException reportPackAdmissionFailure(
|
private static BrokenPackException reportPackAdmissionFailure(
|
||||||
VolmitSender sender, String dimm) {
|
VolmitSender sender,
|
||||||
Optional<String> startupDenial = IrisStartupValidation.denialReason();
|
String dimm,
|
||||||
|
StudioOpenCoordinator.StudioOpenKind openKind
|
||||||
|
) {
|
||||||
|
boolean force = openKind == StudioOpenCoordinator.StudioOpenKind.FORCED_STANDARD;
|
||||||
|
Optional<String> unforcedStartupDenial = ServerConfigurator.worldCreationDenialReason(false);
|
||||||
|
Optional<String> startupDenial = ServerConfigurator.worldCreationDenialReason(force);
|
||||||
IrisDimension dimension = IrisToolbelt.getDimension(dimm);
|
IrisDimension dimension = IrisToolbelt.getDimension(dimm);
|
||||||
String packName = dimension == null || dimension.getLoader() == null
|
String packName = dimension == null || dimension.getLoader() == null
|
||||||
? dimm
|
? dimm
|
||||||
@@ -511,6 +525,9 @@ public class StudioSVC implements IrisService {
|
|||||||
BrokenPackException failure = resolvePackAdmissionFailure(
|
BrokenPackException failure = resolvePackAdmissionFailure(
|
||||||
packName, startupDenial, validation);
|
packName, startupDenial, validation);
|
||||||
if (failure == null) {
|
if (failure == null) {
|
||||||
|
if (force && unforcedStartupDenial.isPresent()) {
|
||||||
|
sender.sendMessage("Force-opening Studio with the currently loaded registry state. The open may fail; restart remains required.");
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (startupDenial.isPresent()) {
|
if (startupDenial.isPresent()) {
|
||||||
@@ -525,6 +542,12 @@ public class StudioSVC implements IrisService {
|
|||||||
return failure;
|
return failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void retainPackRestartRequirement(PackDownloader.PackInstallResult result) {
|
||||||
|
if (result != null && result.restartRequired()) {
|
||||||
|
ServerConfigurator.requireWorldCreationRestart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static BrokenPackException resolvePackAdmissionFailure(
|
static BrokenPackException resolvePackAdmissionFailure(
|
||||||
String packName,
|
String packName,
|
||||||
Optional<String> startupDenial,
|
Optional<String> startupDenial,
|
||||||
@@ -554,7 +577,7 @@ public class StudioSVC implements IrisService {
|
|||||||
Consumer<World> onDone
|
Consumer<World> onDone
|
||||||
) throws IrisException {
|
) throws IrisException {
|
||||||
long requestedAtNanos = System.nanoTime();
|
long requestedAtNanos = System.nanoTime();
|
||||||
if (reportPackAdmissionFailure(sender, dimm) != null) {
|
if (reportPackAdmissionFailure(sender, dimm, openKind) != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StudioOpenCoordinator.StudioOpenKind requiredOpenKind = Objects.requireNonNull(
|
StudioOpenCoordinator.StudioOpenKind requiredOpenKind = Objects.requireNonNull(
|
||||||
@@ -587,7 +610,7 @@ public class StudioSVC implements IrisService {
|
|||||||
Consumer<World> onDone
|
Consumer<World> onDone
|
||||||
) {
|
) {
|
||||||
long requestedAtNanos = System.nanoTime();
|
long requestedAtNanos = System.nanoTime();
|
||||||
BrokenPackException failure = reportPackAdmissionFailure(sender, dimension);
|
BrokenPackException failure = reportPackAdmissionFailure(sender, dimension, openKind);
|
||||||
if (failure != null) {
|
if (failure != null) {
|
||||||
return CompletableFuture.failedFuture(failure);
|
return CompletableFuture.failedFuture(failure);
|
||||||
}
|
}
|
||||||
@@ -1027,9 +1050,7 @@ public class StudioSVC implements IrisService {
|
|||||||
closeLease(lease);
|
closeLease(lease);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outcome == CreationOutcome.RESTART) {
|
if (outcome == CreationOutcome.OPEN) {
|
||||||
ServerConfigurator.restart();
|
|
||||||
} else if (outcome == CreationOutcome.OPEN) {
|
|
||||||
String completedProjectName = projectName;
|
String completedProjectName = projectName;
|
||||||
LifecycleOperationCoordinator.get().whenIdle(() -> open(sender, completedProjectName));
|
LifecycleOperationCoordinator.get().whenIdle(() -> open(sender, completedProjectName));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import art.arcane.iris.spi.IrisServices;
|
|||||||
import art.arcane.iris.platform.bukkit.BukkitPlatform;
|
import art.arcane.iris.platform.bukkit.BukkitPlatform;
|
||||||
import art.arcane.iris.core.link.MultiverseCoreLink;
|
import art.arcane.iris.core.link.MultiverseCoreLink;
|
||||||
import art.arcane.iris.core.IrisRuntimeSchedulerMode;
|
import art.arcane.iris.core.IrisRuntimeSchedulerMode;
|
||||||
import art.arcane.iris.core.IrisStartupValidation;
|
|
||||||
import art.arcane.iris.core.DatapackInstallResult;
|
import art.arcane.iris.core.DatapackInstallResult;
|
||||||
import art.arcane.iris.core.IrisWorldStorage;
|
import art.arcane.iris.core.IrisWorldStorage;
|
||||||
import art.arcane.iris.core.IrisWorlds;
|
import art.arcane.iris.core.IrisWorlds;
|
||||||
@@ -182,7 +181,7 @@ public class IrisCreator {
|
|||||||
throw new IrisException("Dimension cannot be found for id " + dimension());
|
throw new IrisException("Dimension cannot be found for id " + dimension());
|
||||||
}
|
}
|
||||||
reportCreationProgress(creationReporter, 0.06D, "validate_pack");
|
reportCreationProgress(creationReporter, 0.06D, "validate_pack");
|
||||||
IrisStartupValidation.requireWorldCreationReady();
|
ServerConfigurator.requireWorldCreationReady(datapackPreparation.forcesLoadedRuntime());
|
||||||
PackValidationRegistry.requireLoadable(
|
PackValidationRegistry.requireLoadable(
|
||||||
resolvedDimension.getLoader().getDataFolder().getName());
|
resolvedDimension.getLoader().getDataFolder().getName());
|
||||||
worldLease = coordinator.acquire(
|
worldLease = coordinator.acquire(
|
||||||
@@ -253,10 +252,11 @@ public class IrisCreator {
|
|||||||
if (!datapackResult.succeeded()) {
|
if (!datapackResult.succeeded()) {
|
||||||
throw new IrisException("Failed to compile datapacks for dimension \"" + dimension() + "\".");
|
throw new IrisException("Failed to compile datapacks for dimension \"" + dimension() + "\".");
|
||||||
}
|
}
|
||||||
if (datapackResult.restartRequired() || !ServerConfigurator.verifyDataPackInstalled(resolvedDimension)) {
|
if (!datapackPreparation.forcesLoadedRuntime()
|
||||||
ServerConfigurator.restart();
|
&& (datapackResult.restartRequired()
|
||||||
|
|| !ServerConfigurator.verifyDataPackInstalled(resolvedDimension))) {
|
||||||
throw new IrisException("The dimension types for pack \"" + dimension() + "\" are not loaded yet. "
|
throw new IrisException("The dimension types for pack \"" + dimension() + "\" are not loaded yet. "
|
||||||
+ "Iris queued a restart; run the command again after the server returns.");
|
+ "Restart the server, then run the command again.");
|
||||||
}
|
}
|
||||||
|
|
||||||
IrisDimension installedDimension = resolvedDimension;
|
IrisDimension installedDimension = resolvedDimension;
|
||||||
@@ -301,7 +301,13 @@ public class IrisCreator {
|
|||||||
reportCreationProgress(creationReporter, 0.44D, "create_world");
|
reportCreationProgress(creationReporter, 0.44D, "create_world");
|
||||||
long nmsStartNanos = System.nanoTime();
|
long nmsStartNanos = System.nanoTime();
|
||||||
try {
|
try {
|
||||||
WorldLifecycleCaller callerKind = benchmark ? WorldLifecycleCaller.BENCHMARK : studio() ? WorldLifecycleCaller.STUDIO : WorldLifecycleCaller.CREATE;
|
WorldLifecycleCaller callerKind = benchmark
|
||||||
|
? WorldLifecycleCaller.BENCHMARK
|
||||||
|
: studio() && datapackPreparation.forcesLoadedRuntime()
|
||||||
|
? WorldLifecycleCaller.FORCED_STUDIO
|
||||||
|
: studio()
|
||||||
|
? WorldLifecycleCaller.STUDIO
|
||||||
|
: WorldLifecycleCaller.CREATE;
|
||||||
WorldLifecycleRequest request = WorldLifecycleRequest.fromCreator(wc, studio(), benchmark, callerKind);
|
WorldLifecycleRequest request = WorldLifecycleRequest.fromCreator(wc, studio(), benchmark, callerKind);
|
||||||
world = J.sfut(() -> INMS.get().createWorldAsync(wc, request))
|
world = J.sfut(() -> INMS.get().createWorldAsync(wc, request))
|
||||||
.thenCompose(Function.identity())
|
.thenCompose(Function.identity())
|
||||||
@@ -319,9 +325,8 @@ public class IrisCreator {
|
|||||||
throw new IrisException("Runtime world creation is blocked and the selected world lifecycle backend could not create the world.", e);
|
throw new IrisException("Runtime world creation is blocked and the selected world lifecycle backend could not create the world.", e);
|
||||||
}
|
}
|
||||||
if (containsMissingDimensionTypes(e)) {
|
if (containsMissingDimensionTypes(e)) {
|
||||||
ServerConfigurator.restart();
|
|
||||||
throw new IrisException("The dimension types for pack \"" + dimension() + "\" are not loaded on this server yet. "
|
throw new IrisException("The dimension types for pack \"" + dimension() + "\" are not loaded on this server yet. "
|
||||||
+ "Iris queued a restart; run the command again after the server returns.", e);
|
+ "Restart the server, then run the command again.", e);
|
||||||
}
|
}
|
||||||
throw new IrisException("Failed to create world with backend family " + WorldLifecycleService.get().capabilities().serverFamily().id() + "!", e);
|
throw new IrisException("Failed to create world with backend family " + WorldLifecycleService.get().capabilities().serverFamily().id() + "!", e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -856,17 +861,24 @@ public class IrisCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum DatapackPreparation {
|
public enum DatapackPreparation {
|
||||||
INSTALL_IF_CHANGED(false),
|
INSTALL_IF_CHANGED(false, false),
|
||||||
REUSE_LOADED_RUNTIME_IF_READY(true);
|
REUSE_LOADED_RUNTIME_IF_READY(true, false),
|
||||||
|
FORCE_REUSE_LOADED_RUNTIME(true, true);
|
||||||
|
|
||||||
private final boolean reusesLoadedRuntime;
|
private final boolean reusesLoadedRuntime;
|
||||||
|
private final boolean forcesLoadedRuntime;
|
||||||
|
|
||||||
DatapackPreparation(boolean reusesLoadedRuntime) {
|
DatapackPreparation(boolean reusesLoadedRuntime, boolean forcesLoadedRuntime) {
|
||||||
this.reusesLoadedRuntime = reusesLoadedRuntime;
|
this.reusesLoadedRuntime = reusesLoadedRuntime;
|
||||||
|
this.forcesLoadedRuntime = forcesLoadedRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean requiresInstall(boolean runtimeReady) {
|
boolean requiresInstall(boolean runtimeReady) {
|
||||||
return !reusesLoadedRuntime || !runtimeReady;
|
return !forcesLoadedRuntime && (!reusesLoadedRuntime || !runtimeReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean forcesLoadedRuntime() {
|
||||||
|
return forcesLoadedRuntime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,20 @@ public class IrisStartupValidationTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void forceAllowsOnlyStudioAtACompletedRestartBoundary() {
|
||||||
|
IrisStartupValidation.begin();
|
||||||
|
IrisStartupValidation.markDatapacksReady();
|
||||||
|
IrisStartupValidation.markPacksReady();
|
||||||
|
IrisStartupValidation.requireRestart("restart boundary");
|
||||||
|
|
||||||
|
assertTrue(IrisStartupValidation.studioDenialReason(true).isEmpty());
|
||||||
|
assertThrows(IllegalStateException.class, IrisStartupValidation::requireWorldCreationReady);
|
||||||
|
|
||||||
|
IrisStartupValidation.markPacksInvalid(List.of("pack validation failed"));
|
||||||
|
assertFalse(IrisStartupValidation.studioDenialReason(true).isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void packValidationInfrastructureFailureDeniesCreation() {
|
public void packValidationInfrastructureFailureDeniesCreation() {
|
||||||
IrisStartupValidation.begin();
|
IrisStartupValidation.begin();
|
||||||
|
|||||||
+17
-2
@@ -27,14 +27,29 @@ public class StudioOpenCoordinatorOpenKindTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void allStudioKindsReuseOnlyAnUnchangedLoadedRuntime() {
|
public void ordinaryStudioKindsReuseOnlyAnUnchangedLoadedRuntime() {
|
||||||
for (StudioOpenCoordinator.StudioOpenKind kind : StudioOpenCoordinator.StudioOpenKind.values()) {
|
for (StudioOpenCoordinator.StudioOpenKind kind : StudioOpenCoordinator.StudioOpenKind.values()) {
|
||||||
|
if (kind == StudioOpenCoordinator.StudioOpenKind.FORCED_STANDARD) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
assertEquals(
|
assertEquals(
|
||||||
IrisCreator.DatapackPreparation.REUSE_LOADED_RUNTIME_IF_READY,
|
IrisCreator.DatapackPreparation.REUSE_LOADED_RUNTIME_IF_READY,
|
||||||
kind.datapackPreparation());
|
kind.datapackPreparation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void forcedStandardStudioAttemptsTheLoadedRuntime() {
|
||||||
|
StudioOpenCoordinator.StudioOpenKind kind = StudioOpenCoordinator.StudioOpenKind.FORCED_STANDARD;
|
||||||
|
|
||||||
|
assertTrue(kind.openWorkspace());
|
||||||
|
assertTrue(kind.teleportThroughStandardEntry());
|
||||||
|
assertTrue(kind.prepareGeneratorState());
|
||||||
|
assertEquals(
|
||||||
|
IrisCreator.DatapackPreparation.FORCE_REUSE_LOADED_RUNTIME,
|
||||||
|
kind.datapackPreparation());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void objectStudioOwnsWorkspaceWithoutUsingTheStandardEntry() {
|
public void objectStudioOwnsWorkspaceWithoutUsingTheStandardEntry() {
|
||||||
StudioOpenCoordinator.StudioOpenKind kind = StudioOpenCoordinator.StudioOpenKind.OBJECT;
|
StudioOpenCoordinator.StudioOpenKind kind = StudioOpenCoordinator.StudioOpenKind.OBJECT;
|
||||||
@@ -115,7 +130,7 @@ public class StudioOpenCoordinatorOpenKindTest {
|
|||||||
assertTrue(coordinatorMethod.contains(
|
assertTrue(coordinatorMethod.contains(
|
||||||
"WorldRuntimeControlService.get().resolveEntryAnchor(world, provider)"));
|
"WorldRuntimeControlService.get().resolveEntryAnchor(world, provider)"));
|
||||||
assertTrue(coordinatorMethod.contains(
|
assertTrue(coordinatorMethod.contains(
|
||||||
"project.getActiveOpenKind() == StudioOpenKind.STANDARD"));
|
"project.getActiveOpenKind().teleportThroughStandardEntry()"));
|
||||||
assertFalse(coordinatorMethod.contains("requestChunkAsync("));
|
assertFalse(coordinatorMethod.contains("requestChunkAsync("));
|
||||||
assertFalse(coordinatorMethod.contains("getHighestBlockYAt("));
|
assertFalse(coordinatorMethod.contains("getHighestBlockYAt("));
|
||||||
assertTrue(nativeDelegation >= 0);
|
assertTrue(nativeDelegation >= 0);
|
||||||
|
|||||||
@@ -1,16 +1,27 @@
|
|||||||
package art.arcane.iris.core.service;
|
package art.arcane.iris.core.service;
|
||||||
|
|
||||||
|
import art.arcane.iris.core.IrisStartupValidation;
|
||||||
|
import art.arcane.iris.core.ServerConfigurator;
|
||||||
import art.arcane.iris.core.pack.BrokenPackException;
|
import art.arcane.iris.core.pack.BrokenPackException;
|
||||||
|
import art.arcane.iris.core.pack.PackDownloader;
|
||||||
import art.arcane.iris.core.pack.PackValidationResult;
|
import art.arcane.iris.core.pack.PackValidationResult;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class StudioSVCPackAdmissionTest {
|
public class StudioSVCPackAdmissionTest {
|
||||||
|
@After
|
||||||
|
public void disableStartupValidation() {
|
||||||
|
IrisStartupValidation.disable();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadablePackIsAdmitted() {
|
public void loadablePackIsAdmitted() {
|
||||||
PackValidationResult validation = new PackValidationResult(
|
PackValidationResult validation = new PackValidationResult(
|
||||||
@@ -57,4 +68,25 @@ public class StudioSVCPackAdmissionTest {
|
|||||||
|
|
||||||
assertEquals(List.of(denial), failure.getReasons());
|
assertEquals(List.of(denial), failure.getReasons());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void completedPackDownloadRetainsTheCreationGateWithoutDenyingLogin() throws Exception {
|
||||||
|
Field restartRequired = ServerConfigurator.class.getDeclaredField("loadedDatapackRestartRequired");
|
||||||
|
restartRequired.setAccessible(true);
|
||||||
|
boolean previous = restartRequired.getBoolean(null);
|
||||||
|
IrisStartupValidation.begin();
|
||||||
|
IrisStartupValidation.markDatapacksReady();
|
||||||
|
IrisStartupValidation.markPacksReady();
|
||||||
|
|
||||||
|
try {
|
||||||
|
StudioSVC.retainPackRestartRequirement(
|
||||||
|
new PackDownloader.PackInstallResult("overworld", true, true));
|
||||||
|
|
||||||
|
assertTrue(ServerConfigurator.worldCreationDenialReason(false).isPresent());
|
||||||
|
assertTrue(ServerConfigurator.worldCreationDenialReason(true).isEmpty());
|
||||||
|
assertTrue(IrisStartupValidation.denialReason().isEmpty());
|
||||||
|
} finally {
|
||||||
|
restartRequired.setBoolean(null, previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,4 +17,11 @@ public class IrisCreatorDatapackPreparationTest {
|
|||||||
assertTrue(IrisCreator.DatapackPreparation.REUSE_LOADED_RUNTIME_IF_READY.requiresInstall(false));
|
assertTrue(IrisCreator.DatapackPreparation.REUSE_LOADED_RUNTIME_IF_READY.requiresInstall(false));
|
||||||
assertFalse(IrisCreator.DatapackPreparation.REUSE_LOADED_RUNTIME_IF_READY.requiresInstall(true));
|
assertFalse(IrisCreator.DatapackPreparation.REUSE_LOADED_RUNTIME_IF_READY.requiresInstall(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void forcedStudioCreationNeverInstallsDatapacks() {
|
||||||
|
assertFalse(IrisCreator.DatapackPreparation.FORCE_REUSE_LOADED_RUNTIME.requiresInstall(false));
|
||||||
|
assertFalse(IrisCreator.DatapackPreparation.FORCE_REUSE_LOADED_RUNTIME.requiresInstall(true));
|
||||||
|
assertTrue(IrisCreator.DatapackPreparation.FORCE_REUSE_LOADED_RUNTIME.forcesLoadedRuntime());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user