mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 05:50:39 +00:00
Fixed runtime configuration
- Timeout overrides retain original values for restoration - Timeout restoration preserves operator changes and symbolic links - Tests cover timeout persistence and failed configuration writes - Runtime targets use separate Gradle caches and shared module outputs - Modded build inputs include test fixtures and runtime cache settings
This commit is contained in:
1 parent
64f7a7a9ac
commit
b357eee181
10 files changed
+380
-26
No files matched your search
@@ -33,6 +33,8 @@ plugins {
|
|||||||
|
|
||||||
rootProject.name = 'iris-fabric'
|
rootProject.name = 'iris-fabric'
|
||||||
|
|
||||||
|
apply from: new File(settingsDir, '../../gradle/runtime-cache.settings.gradle').canonicalFile
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
libs {
|
libs {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ plugins {
|
|||||||
|
|
||||||
rootProject.name = 'iris-forge'
|
rootProject.name = 'iris-forge'
|
||||||
|
|
||||||
|
apply from: new File(settingsDir, '../../gradle/runtime-cache.settings.gradle').canonicalFile
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
libs {
|
libs {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ plugins {
|
|||||||
|
|
||||||
rootProject.name = 'iris-neoforge'
|
rootProject.name = 'iris-neoforge'
|
||||||
|
|
||||||
|
apply from: new File(settingsDir, '../../gradle/runtime-cache.settings.gradle').canonicalFile
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
libs {
|
libs {
|
||||||
|
|||||||
+5
-2
@@ -310,6 +310,7 @@ List<Object> moddedSharedInputs = []
|
|||||||
'adapters/modded-common/src',
|
'adapters/modded-common/src',
|
||||||
'adapters/client-common/src',
|
'adapters/client-common/src',
|
||||||
'core/src/main',
|
'core/src/main',
|
||||||
|
'core/src/testFixtures',
|
||||||
'core/build.gradle',
|
'core/build.gradle',
|
||||||
'core/agent/src',
|
'core/agent/src',
|
||||||
'core/agent/build.gradle',
|
'core/agent/build.gradle',
|
||||||
@@ -327,6 +328,7 @@ List<Object> moddedSharedInputs = []
|
|||||||
'gradle/native-minecraft-sources.gradle',
|
'gradle/native-minecraft-sources.gradle',
|
||||||
'gradle/mod-target.gradle',
|
'gradle/mod-target.gradle',
|
||||||
'gradle/mod-targets.json',
|
'gradle/mod-targets.json',
|
||||||
|
'gradle/runtime-cache.settings.gradle',
|
||||||
'gradle/modded-version-sources.gradle'
|
'gradle/modded-version-sources.gradle'
|
||||||
].each { String path -> moddedSharedInputs.add(layout.projectDirectory.file(path).asFile) }
|
].each { String path -> moddedSharedInputs.add(layout.projectDirectory.file(path).asFile) }
|
||||||
if (localVolmLibCheckout != null) {
|
if (localVolmLibCheckout != null) {
|
||||||
@@ -688,12 +690,13 @@ if (!(irisRuntimeJava in [21, 25])) {
|
|||||||
throw new GradleException('irisRuntimeJava must be 21 or 25')
|
throw new GradleException('irisRuntimeJava must be 21 or 25')
|
||||||
}
|
}
|
||||||
Set<String> sharedRuntimeProjects = [':core', ':core:agent', ':spi'] as Set<String>
|
Set<String> sharedRuntimeProjects = [':core', ':core:agent', ':spi'] as Set<String>
|
||||||
|
String runtimeOutputVariant = gradle.extensions.extraProperties.get('irisRuntimeOutputVariant').toString()
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
boolean java21Runtime = irisRuntimeJava == 21 && sharedRuntimeProjects.contains(project.path)
|
boolean java21Runtime = irisRuntimeJava == 21 && sharedRuntimeProjects.contains(project.path)
|
||||||
if (java21Runtime) {
|
if (!runtimeOutputVariant.isEmpty() && sharedRuntimeProjects.contains(project.path)) {
|
||||||
layout.buildDirectory.set(layout.projectDirectory.dir('build/java21'))
|
layout.buildDirectory.set(layout.projectDirectory.dir("build/${runtimeOutputVariant}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(Test).configureEach {
|
tasks.withType(Test).configureEach {
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ import art.arcane.iris.generation.terrain.IrisDimension;
|
|||||||
import art.arcane.volmlib.util.collection.KList;
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import art.arcane.volmlib.util.collection.KSet;
|
import art.arcane.volmlib.util.collection.KSet;
|
||||||
import art.arcane.volmlib.util.io.AtomicFileIO;
|
import art.arcane.volmlib.util.io.AtomicFileIO;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
import art.arcane.iris.platform.bootstrap.ServerProperties;
|
import art.arcane.iris.platform.bootstrap.ServerProperties;
|
||||||
import art.arcane.iris.platform.bukkit.plugin.VolmitSender;
|
import art.arcane.iris.platform.bukkit.plugin.VolmitSender;
|
||||||
import art.arcane.iris.world.task.J;
|
import art.arcane.iris.world.task.J;
|
||||||
@@ -104,6 +107,12 @@ public class ServerConfigurator {
|
|||||||
if (failure != null) {
|
if (failure != null) {
|
||||||
IrisLogging.reportError("Unable to persist the Spigot timeout setting.", failure);
|
IrisLogging.reportError("Unable to persist the Spigot timeout setting.", failure);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Throwable failure = J.attemptCatch(() -> timeoutConfiguration().restore(
|
||||||
|
"settings.timeout-time", serverConfigurationOption("spigot-settings"), Bukkit.spigot().getConfig()));
|
||||||
|
if (failure != null) {
|
||||||
|
IrisLogging.reportError("Unable to restore the Spigot timeout setting.", failure);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.isConfigurePaperWatchdogDelay() && BukkitPlatform.isPaperServer()) {
|
if (s.isConfigurePaperWatchdogDelay() && BukkitPlatform.isPaperServer()) {
|
||||||
@@ -111,6 +120,12 @@ public class ServerConfigurator {
|
|||||||
if (failure != null) {
|
if (failure != null) {
|
||||||
IrisLogging.reportError("Unable to persist the Paper watchdog setting.", failure);
|
IrisLogging.reportError("Unable to persist the Paper watchdog setting.", failure);
|
||||||
}
|
}
|
||||||
|
} else if (!s.isConfigurePaperWatchdogDelay()) {
|
||||||
|
Throwable failure = J.attemptCatch(() -> timeoutConfiguration().restore(
|
||||||
|
"watchdog.early-warning-delay", null, null));
|
||||||
|
if (failure != null) {
|
||||||
|
IrisLogging.reportError("Unable to restore the Paper watchdog setting.", failure);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DefaultPackBootstrapProvisioner.wasProvisionedThisStartup()) {
|
if (DefaultPackBootstrapProvisioner.wasProvisionedThisStartup()) {
|
||||||
@@ -238,26 +253,26 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void increaseKeepAliveSpigot() throws IOException, ReflectiveOperationException {
|
private static void increaseKeepAliveSpigot() throws IOException, ReflectiveOperationException {
|
||||||
increaseKeepAliveSpigot(serverConfigurationOption("spigot-settings"), Bukkit.spigot().getConfig());
|
increaseKeepAliveSpigot(serverConfigurationOption("spigot-settings"), Bukkit.spigot().getConfig(), timeoutConfiguration());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void increaseKeepAliveSpigot(Path file, YamlConfiguration configuration) throws IOException {
|
static void increaseKeepAliveSpigot(Path file, YamlConfiguration configuration, ServerTimeoutConfiguration timeouts) throws IOException {
|
||||||
long previous = configuration.getLong("settings.timeout-time");
|
long previous = configuration.getLong("settings.timeout-time");
|
||||||
long minimum = TimeUnit.MINUTES.toSeconds(5);
|
long minimum = TimeUnit.MINUTES.toSeconds(5);
|
||||||
if (previous >= minimum) {
|
if (previous >= minimum) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
persistTimeout(file, configuration, "settings.timeout-time", minimum);
|
timeouts.raise(file, configuration, "settings.timeout-time", minimum);
|
||||||
IrisLogging.warn("Updated " + file + " settings.timeout-time: " + previous + " -> " + minimum
|
IrisLogging.warn("Updated " + file + " settings.timeout-time: " + previous + " -> " + minimum
|
||||||
+ " seconds. Takes effect after a server restart.");
|
+ " seconds. Takes effect after a server restart.");
|
||||||
IrisLogging.warn("Disable autoConfiguration.configureSpigotTimeoutTime in iris.json to manage this setting yourself.");
|
IrisLogging.warn("Disable autoConfiguration.configureSpigotTimeoutTime in iris.json to manage this setting yourself.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void increasePaperWatchdog() throws IOException, InvalidConfigurationException, ReflectiveOperationException {
|
private static void increasePaperWatchdog() throws IOException, InvalidConfigurationException, ReflectiveOperationException {
|
||||||
increasePaperWatchdog(serverConfigurationOption("paper-settings-directory").resolve("paper-global.yml"));
|
increasePaperWatchdog(serverConfigurationOption("paper-settings-directory").resolve("paper-global.yml"), timeoutConfiguration());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void increasePaperWatchdog(Path file) throws IOException, InvalidConfigurationException {
|
static void increasePaperWatchdog(Path file, ServerTimeoutConfiguration timeouts) throws IOException, InvalidConfigurationException {
|
||||||
YamlConfiguration configuration = new YamlConfiguration();
|
YamlConfiguration configuration = new YamlConfiguration();
|
||||||
configuration.load(file.toFile());
|
configuration.load(file.toFile());
|
||||||
long previous = configuration.getLong("watchdog.early-warning-delay");
|
long previous = configuration.getLong("watchdog.early-warning-delay");
|
||||||
@@ -265,22 +280,14 @@ public class ServerConfigurator {
|
|||||||
if (previous >= minimum) {
|
if (previous >= minimum) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
persistTimeout(file, configuration, "watchdog.early-warning-delay", minimum);
|
timeouts.raise(file, configuration, "watchdog.early-warning-delay", minimum);
|
||||||
IrisLogging.warn("Updated " + file + " watchdog.early-warning-delay: " + previous + " -> " + minimum
|
IrisLogging.warn("Updated " + file + " watchdog.early-warning-delay: " + previous + " -> " + minimum
|
||||||
+ " milliseconds. Takes effect after a server restart.");
|
+ " milliseconds. Takes effect after a server restart.");
|
||||||
IrisLogging.warn("Disable autoConfiguration.configurePaperWatchdogDelay in iris.json to manage this setting yourself.");
|
IrisLogging.warn("Disable autoConfiguration.configurePaperWatchdogDelay in iris.json to manage this setting yourself.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void persistTimeout(Path file, YamlConfiguration configuration, String key, long minimum) throws IOException {
|
private static ServerTimeoutConfiguration timeoutConfiguration() {
|
||||||
Path target = file.toRealPath();
|
return new ServerTimeoutConfiguration(IrisPlatforms.get().dataFolder().toPath().resolve("server-timeouts.json"));
|
||||||
Object previous = configuration.isSet(key) ? configuration.get(key) : null;
|
|
||||||
configuration.set(key, minimum);
|
|
||||||
try {
|
|
||||||
AtomicFileIO.writeString(target, configuration.saveToString());
|
|
||||||
} catch (IOException | RuntimeException failure) {
|
|
||||||
configuration.set(key, previous);
|
|
||||||
throw failure;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Path serverConfigurationOption(String name) throws ReflectiveOperationException, IOException {
|
static Path serverConfigurationOption(String name) throws ReflectiveOperationException, IOException {
|
||||||
@@ -964,4 +971,131 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class ServerTimeoutConfiguration {
|
||||||
|
private static final Object MUTATION_LOCK = new Object();
|
||||||
|
private static final Gson JSON = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
|
||||||
|
|
||||||
|
private final Path recoveryFile;
|
||||||
|
|
||||||
|
ServerTimeoutConfiguration(Path recoveryFile) {
|
||||||
|
this.recoveryFile = Objects.requireNonNull(recoveryFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void raise(Path file, YamlConfiguration configuration, String key, long minimum) throws IOException {
|
||||||
|
synchronized (MUTATION_LOCK) {
|
||||||
|
Path target = file.toRealPath();
|
||||||
|
YamlConfiguration persisted = load(target);
|
||||||
|
Long original = value(persisted, key);
|
||||||
|
List<SavedTimeout> changes = readChanges();
|
||||||
|
SavedTimeout saved = find(changes, target, key);
|
||||||
|
if (saved == null || !Objects.equals(original, saved.original()) && !Objects.equals(original, saved.applied())) {
|
||||||
|
changes.remove(saved);
|
||||||
|
changes.add(new SavedTimeout(target.toString(), key, original, minimum));
|
||||||
|
writeChanges(changes);
|
||||||
|
}
|
||||||
|
Object previous = configuration.isSet(key) ? configuration.get(key) : null;
|
||||||
|
configuration.set(key, minimum);
|
||||||
|
try {
|
||||||
|
AtomicFileIO.writeString(target, configuration.saveToString());
|
||||||
|
} catch (IOException | RuntimeException failure) {
|
||||||
|
configuration.set(key, previous);
|
||||||
|
throw failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void restore(String key, Path liveFile, YamlConfiguration live) throws IOException {
|
||||||
|
synchronized (MUTATION_LOCK) {
|
||||||
|
List<SavedTimeout> changes = readChanges();
|
||||||
|
for (SavedTimeout saved : List.copyOf(changes)) {
|
||||||
|
if (!saved.key().equals(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Path target = Path.of(saved.file());
|
||||||
|
YamlConfiguration configuration = load(target);
|
||||||
|
if (Objects.equals(value(configuration, key), saved.applied())) {
|
||||||
|
configuration.set(key, saved.original());
|
||||||
|
AtomicFileIO.writeString(target, configuration.saveToString());
|
||||||
|
if (live != null && target.equals(liveFile.toRealPath())
|
||||||
|
&& Objects.equals(value(live, key), saved.applied())) {
|
||||||
|
live.set(key, saved.original());
|
||||||
|
}
|
||||||
|
IrisLogging.info("Restored " + target + " " + key + " to "
|
||||||
|
+ (saved.original() == null ? "its server default" : saved.original())
|
||||||
|
+ ". Takes effect after a server restart.");
|
||||||
|
} else {
|
||||||
|
IrisLogging.info("Kept the current " + target + " " + key
|
||||||
|
+ " because it differs from the value saved by Iris.");
|
||||||
|
}
|
||||||
|
changes.remove(saved);
|
||||||
|
writeChanges(changes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SavedTimeout> readChanges() throws IOException {
|
||||||
|
if (!Files.exists(recoveryFile)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SavedTimeouts saved = JSON.fromJson(Files.readString(recoveryFile), SavedTimeouts.class);
|
||||||
|
if (saved == null) {
|
||||||
|
throw new IOException("Empty server timeout recovery data: " + recoveryFile);
|
||||||
|
}
|
||||||
|
return new ArrayList<>(saved.changes());
|
||||||
|
} catch (JsonParseException | IllegalArgumentException exception) {
|
||||||
|
throw new IOException("Invalid server timeout recovery data: " + recoveryFile, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeChanges(List<SavedTimeout> changes) throws IOException {
|
||||||
|
AtomicFileIO.writeString(recoveryFile, JSON.toJson(new SavedTimeouts(changes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SavedTimeout find(List<SavedTimeout> changes, Path target, String key) {
|
||||||
|
for (SavedTimeout change : changes) {
|
||||||
|
if (change.file().equals(target.toString()) && change.key().equals(key)) {
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static YamlConfiguration load(Path file) throws IOException {
|
||||||
|
YamlConfiguration configuration = new YamlConfiguration();
|
||||||
|
try {
|
||||||
|
configuration.load(file.toFile());
|
||||||
|
} catch (InvalidConfigurationException exception) {
|
||||||
|
throw new IOException("Invalid server configuration: " + file, exception);
|
||||||
|
}
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Long value(YamlConfiguration configuration, String key) throws IOException {
|
||||||
|
if (!configuration.isSet(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!configuration.isInt(key) && !configuration.isLong(key)) {
|
||||||
|
throw new IOException("Server timeout " + key + " must be an integer.");
|
||||||
|
}
|
||||||
|
return configuration.getLong(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record SavedTimeout(String file, String key, Long original, long applied) {
|
||||||
|
private SavedTimeout {
|
||||||
|
Objects.requireNonNull(file, "file");
|
||||||
|
Objects.requireNonNull(key, "key");
|
||||||
|
if (!Path.of(file).isAbsolute() || applied <= 0
|
||||||
|
|| !key.equals("settings.timeout-time") && !key.equals("watchdog.early-warning-delay")) {
|
||||||
|
throw new IllegalArgumentException("Invalid saved server timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record SavedTimeouts(List<SavedTimeout> changes) {
|
||||||
|
private SavedTimeouts {
|
||||||
|
changes = List.copyOf(changes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+12
-7
@@ -1,5 +1,6 @@
|
|||||||
package art.arcane.iris.pack.datapack;
|
package art.arcane.iris.pack.datapack;
|
||||||
|
|
||||||
|
import art.arcane.iris.pack.datapack.ServerConfigurator.ServerTimeoutConfiguration;
|
||||||
import art.arcane.iris.spi.IrisLogging;
|
import art.arcane.iris.spi.IrisLogging;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
@@ -41,7 +42,7 @@ public class ServerConfiguratorTimeoutTest {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
ServerConfigurator.increaseKeepAliveSpigot(file, live);
|
ServerConfigurator.increaseKeepAliveSpigot(file, live, timeouts());
|
||||||
|
|
||||||
assertEquals(300L, live.getLong("settings.timeout-time"));
|
assertEquals(300L, live.getLong("settings.timeout-time"));
|
||||||
live.set("world-settings.default.sample-world-setting", true);
|
live.set("world-settings.default.sample-world-setting", true);
|
||||||
@@ -62,7 +63,7 @@ public class ServerConfiguratorTimeoutTest {
|
|||||||
YamlConfiguration live = load(file);
|
YamlConfiguration live = load(file);
|
||||||
|
|
||||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
ServerConfigurator.increaseKeepAliveSpigot(file, live);
|
ServerConfigurator.increaseKeepAliveSpigot(file, live, timeouts());
|
||||||
|
|
||||||
assertEquals(600L, live.getLong("settings.timeout-time"));
|
assertEquals(600L, live.getLong("settings.timeout-time"));
|
||||||
assertEquals(original, Files.readString(file));
|
assertEquals(original, Files.readString(file));
|
||||||
@@ -81,7 +82,7 @@ public class ServerConfiguratorTimeoutTest {
|
|||||||
YamlConfiguration live = load(link);
|
YamlConfiguration live = load(link);
|
||||||
|
|
||||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
ServerConfigurator.increaseKeepAliveSpigot(link, live);
|
ServerConfigurator.increaseKeepAliveSpigot(link, live, timeouts());
|
||||||
|
|
||||||
assertTrue(Files.isSymbolicLink(link));
|
assertTrue(Files.isSymbolicLink(link));
|
||||||
assertEquals(relativeTarget, Files.readSymbolicLink(link));
|
assertEquals(relativeTarget, Files.readSymbolicLink(link));
|
||||||
@@ -99,7 +100,7 @@ public class ServerConfiguratorTimeoutTest {
|
|||||||
+ "misc:\n sample-setting: retained\n");
|
+ "misc:\n sample-setting: retained\n");
|
||||||
|
|
||||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
ServerConfigurator.increasePaperWatchdog(file);
|
ServerConfigurator.increasePaperWatchdog(file, timeouts());
|
||||||
|
|
||||||
YamlConfiguration persisted = load(file);
|
YamlConfiguration persisted = load(file);
|
||||||
assertEquals(180000L, persisted.getLong("watchdog.early-warning-delay"));
|
assertEquals(180000L, persisted.getLong("watchdog.early-warning-delay"));
|
||||||
@@ -117,7 +118,7 @@ public class ServerConfiguratorTimeoutTest {
|
|||||||
Files.writeString(file, original);
|
Files.writeString(file, original);
|
||||||
|
|
||||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
ServerConfigurator.increasePaperWatchdog(file);
|
ServerConfigurator.increasePaperWatchdog(file, timeouts());
|
||||||
|
|
||||||
assertEquals(original, Files.readString(file));
|
assertEquals(original, Files.readString(file));
|
||||||
logging.verifyNoInteractions();
|
logging.verifyNoInteractions();
|
||||||
@@ -131,7 +132,7 @@ public class ServerConfiguratorTimeoutTest {
|
|||||||
Files.writeString(file, malformed);
|
Files.writeString(file, malformed);
|
||||||
|
|
||||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
assertThrows(InvalidConfigurationException.class, () -> ServerConfigurator.increasePaperWatchdog(file));
|
assertThrows(InvalidConfigurationException.class, () -> ServerConfigurator.increasePaperWatchdog(file, timeouts()));
|
||||||
|
|
||||||
assertEquals(malformed, Files.readString(file));
|
assertEquals(malformed, Files.readString(file));
|
||||||
logging.verifyNoInteractions();
|
logging.verifyNoInteractions();
|
||||||
@@ -148,7 +149,7 @@ public class ServerConfiguratorTimeoutTest {
|
|||||||
|
|
||||||
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
assertThrows(IOException.class, () -> ServerConfigurator.increaseKeepAliveSpigot(
|
assertThrows(IOException.class, () -> ServerConfigurator.increaseKeepAliveSpigot(
|
||||||
blockedParent.resolve("spigot.yml"), live));
|
blockedParent.resolve("spigot.yml"), live, timeouts()));
|
||||||
|
|
||||||
assertEquals(60L, live.getLong("settings.timeout-time"));
|
assertEquals(60L, live.getLong("settings.timeout-time"));
|
||||||
assertEquals("retained", live.getString("settings.sample-setting"));
|
assertEquals("retained", live.getString("settings.sample-setting"));
|
||||||
@@ -175,6 +176,10 @@ public class ServerConfiguratorTimeoutTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ServerTimeoutConfiguration timeouts() {
|
||||||
|
return new ServerTimeoutConfiguration(temporaryFolder.getRoot().toPath().resolve("server-timeouts.json"));
|
||||||
|
}
|
||||||
|
|
||||||
private static YamlConfiguration load(Path file) throws IOException, InvalidConfigurationException {
|
private static YamlConfiguration load(Path file) throws IOException, InvalidConfigurationException {
|
||||||
YamlConfiguration configuration = new YamlConfiguration();
|
YamlConfiguration configuration = new YamlConfiguration();
|
||||||
configuration.load(file.toFile());
|
configuration.load(file.toFile());
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
package art.arcane.iris.pack.datapack;
|
||||||
|
|
||||||
|
import art.arcane.iris.pack.datapack.ServerConfigurator.ServerTimeoutConfiguration;
|
||||||
|
import art.arcane.iris.spi.IrisLogging;
|
||||||
|
import art.arcane.volmlib.util.io.AtomicFileIO;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
|
||||||
|
public class ServerTimeoutConfigurationTest {
|
||||||
|
private static final String SPIGOT_KEY = "settings.timeout-time";
|
||||||
|
private static final String PAPER_KEY = "watchdog.early-warning-delay";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void originalValuesSurviveRestartsAndRestoreIndependently() throws Exception {
|
||||||
|
Path spigot = configuration("spigot.yml", "settings:\n timeout-time: 60\n");
|
||||||
|
Path paper = configuration("paper-global.yml", "watchdog:\n early-warning-delay: 10000\n early-warning-every: 5000\n");
|
||||||
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
|
ServerConfigurator.increaseKeepAliveSpigot(spigot, load(spigot), timeouts());
|
||||||
|
ServerConfigurator.increasePaperWatchdog(paper, timeouts());
|
||||||
|
ServerConfigurator.increaseKeepAliveSpigot(spigot, load(spigot), timeouts());
|
||||||
|
YamlConfiguration live = load(spigot);
|
||||||
|
timeouts().restore(SPIGOT_KEY, spigot, live);
|
||||||
|
assertEquals(60L, load(spigot).getLong(SPIGOT_KEY));
|
||||||
|
assertEquals(60L, live.getLong(SPIGOT_KEY));
|
||||||
|
assertEquals(180000L, load(paper).getLong(PAPER_KEY));
|
||||||
|
live.set("settings.unrelated", true);
|
||||||
|
live.save(spigot.toFile());
|
||||||
|
assertEquals(60L, load(spigot).getLong(SPIGOT_KEY));
|
||||||
|
timeouts().restore(PAPER_KEY, null, null);
|
||||||
|
assertEquals(10000L, load(paper).getLong(PAPER_KEY));
|
||||||
|
assertEquals(5000L, load(paper).getLong("watchdog.early-warning-every"));
|
||||||
|
assertFalse(Files.readString(recoveryFile()).contains("spigot.yml"));
|
||||||
|
assertFalse(Files.readString(recoveryFile()).contains("paper-global.yml"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void restoringAbsentSettingRemovesTheOverride() throws Exception {
|
||||||
|
Path file = configuration("spigot.yml", "settings:\n unrelated: retained\n");
|
||||||
|
YamlConfiguration live = load(file);
|
||||||
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
|
ServerConfigurator.increaseKeepAliveSpigot(file, live, timeouts());
|
||||||
|
timeouts().restore(SPIGOT_KEY, file, live);
|
||||||
|
assertFalse(load(file).isSet(SPIGOT_KEY));
|
||||||
|
assertFalse(live.isSet(SPIGOT_KEY));
|
||||||
|
assertEquals("retained", load(file).getString("settings.unrelated"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void restorePreservesOperatorChangesAndOtherSettings() throws Exception {
|
||||||
|
Path file = configuration("spigot.yml", "settings:\n timeout-time: 60\n");
|
||||||
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
|
ServerConfigurator.increaseKeepAliveSpigot(file, load(file), timeouts());
|
||||||
|
Files.writeString(file, "# Operator settings\nsettings:\n timeout-time: 90\n unrelated: retained\n");
|
||||||
|
String changed = Files.readString(file);
|
||||||
|
timeouts().restore(SPIGOT_KEY, file, load(file));
|
||||||
|
assertEquals(changed, Files.readString(file));
|
||||||
|
ServerConfigurator.increaseKeepAliveSpigot(file, load(file), timeouts());
|
||||||
|
timeouts().restore(SPIGOT_KEY, file, load(file));
|
||||||
|
assertEquals(90L, load(file).getLong(SPIGOT_KEY));
|
||||||
|
assertEquals("retained", load(file).getString("settings.unrelated"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void symlinkSurvivesRaiseAndRestore() throws Exception {
|
||||||
|
Path target = configuration("shared-spigot.yml", "settings:\n timeout-time: 60\n");
|
||||||
|
Path link = temporaryFolder.getRoot().toPath().resolve("spigot.yml");
|
||||||
|
Files.createSymbolicLink(link, target.getFileName());
|
||||||
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
|
ServerConfigurator.increaseKeepAliveSpigot(link, load(link), timeouts());
|
||||||
|
timeouts().restore(SPIGOT_KEY, link, load(link));
|
||||||
|
assertTrue(Files.isSymbolicLink(link));
|
||||||
|
assertEquals(60L, load(target).getLong(SPIGOT_KEY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void aLaterAdjustmentRecordsTheOperatorsNewOriginal() throws Exception {
|
||||||
|
Path file = configuration("spigot.yml", "settings:\n timeout-time: 60\n");
|
||||||
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
|
ServerConfigurator.increaseKeepAliveSpigot(file, load(file), timeouts());
|
||||||
|
Files.writeString(file, "settings:\n timeout-time: 90\n");
|
||||||
|
ServerConfigurator.increaseKeepAliveSpigot(file, load(file), timeouts());
|
||||||
|
timeouts().restore(SPIGOT_KEY, file, load(file));
|
||||||
|
assertEquals(90L, load(file).getLong(SPIGOT_KEY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failedRecoveryWriteLeavesServerSettingsUntouched() throws Exception {
|
||||||
|
Path file = configuration("spigot.yml", "settings:\n timeout-time: 60\n");
|
||||||
|
YamlConfiguration live = load(file);
|
||||||
|
Path blocked = temporaryFolder.newFile("blocked").toPath();
|
||||||
|
ServerTimeoutConfiguration timeouts = new ServerTimeoutConfiguration(blocked.resolve("server-timeouts.json"));
|
||||||
|
assertThrows(IOException.class, () -> timeouts.raise(file, live, SPIGOT_KEY, 300L));
|
||||||
|
assertEquals(60L, load(file).getLong(SPIGOT_KEY));
|
||||||
|
assertEquals(60L, live.getLong(SPIGOT_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failedServerWriteKeepsTheOriginalAndRestoresTheLiveValue() throws Exception {
|
||||||
|
Path file = configuration("spigot.yml", "settings:\n timeout-time: 60\n");
|
||||||
|
YamlConfiguration live = load(file);
|
||||||
|
try (MockedStatic<AtomicFileIO> writes = mockStatic(AtomicFileIO.class, CALLS_REAL_METHODS)) {
|
||||||
|
writes.when(() -> AtomicFileIO.writeString(eq(file.toRealPath()), anyString()))
|
||||||
|
.thenThrow(new IOException("blocked server write"));
|
||||||
|
assertThrows(IOException.class, () -> timeouts().raise(file, live, SPIGOT_KEY, 300L));
|
||||||
|
}
|
||||||
|
assertEquals(60L, load(file).getLong(SPIGOT_KEY));
|
||||||
|
assertEquals(60L, live.getLong(SPIGOT_KEY));
|
||||||
|
assertTrue(Files.readString(recoveryFile()).contains("\"original\": 60"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void interruptedRestoreRetriesWithoutOverwritingLaterOperatorEdits() throws Exception {
|
||||||
|
Path file = configuration("spigot.yml", "settings:\n timeout-time: 60\n");
|
||||||
|
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
|
||||||
|
ServerConfigurator.increaseKeepAliveSpigot(file, load(file), timeouts());
|
||||||
|
try (MockedStatic<AtomicFileIO> writes = mockStatic(AtomicFileIO.class, CALLS_REAL_METHODS)) {
|
||||||
|
writes.when(() -> AtomicFileIO.writeString(eq(recoveryFile()), anyString()))
|
||||||
|
.thenThrow(new IOException("interrupted recovery write"));
|
||||||
|
assertThrows(IOException.class, () -> timeouts().restore(SPIGOT_KEY, file, load(file)));
|
||||||
|
}
|
||||||
|
assertEquals(60L, load(file).getLong(SPIGOT_KEY));
|
||||||
|
assertTrue(Files.readString(recoveryFile()).contains("spigot.yml"));
|
||||||
|
Files.writeString(file, "settings:\n timeout-time: 120\n");
|
||||||
|
timeouts().restore(SPIGOT_KEY, file, load(file));
|
||||||
|
assertEquals(120L, load(file).getLong(SPIGOT_KEY));
|
||||||
|
assertFalse(Files.readString(recoveryFile()).contains("spigot.yml"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void malformedRecoveryDataPreventsNewConfigurationWrites() throws Exception {
|
||||||
|
Path file = configuration("spigot.yml", "settings:\n timeout-time: 60\n");
|
||||||
|
Files.writeString(recoveryFile(), "{invalid");
|
||||||
|
assertThrows(IOException.class, () -> timeouts().raise(file, load(file), SPIGOT_KEY, 300L));
|
||||||
|
assertEquals(60L, load(file).getLong(SPIGOT_KEY));
|
||||||
|
assertEquals("{invalid", Files.readString(recoveryFile()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path configuration(String name, String content) throws IOException {
|
||||||
|
Path file = temporaryFolder.newFile(name).toPath();
|
||||||
|
Files.writeString(file, content);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerTimeoutConfiguration timeouts() {
|
||||||
|
return new ServerTimeoutConfiguration(recoveryFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path recoveryFile() {
|
||||||
|
return temporaryFolder.getRoot().toPath().resolve("server-timeouts.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static YamlConfiguration load(Path file) throws Exception {
|
||||||
|
YamlConfiguration configuration = new YamlConfiguration();
|
||||||
|
configuration.load(file.toFile());
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ TaskProvider<JavaExec> resolvedRevisionVerification = tasks.register('verifyReso
|
|||||||
classpath = files('../../buildSrc/build/libs/buildSrc.jar')
|
classpath = files('../../buildSrc/build/libs/buildSrc.jar')
|
||||||
mainClass.set('art.arcane.iris.buildtools.GenerationBuildRevision')
|
mainClass.set('art.arcane.iris.buildtools.GenerationBuildRevision')
|
||||||
doFirst {
|
doFirst {
|
||||||
String runtimeVariant = providers.gradleProperty('irisRuntimeJava').getOrElse(irisModTarget.java.toString()) == '21' ? 'java21/' : ''
|
String runtimeVariant = gradle.extensions.extraProperties.get('irisRuntimeOutputVariant').toString()
|
||||||
List<String> arguments = ['verify-dependencies', file("../../core/build/${runtimeVariant}generated/generation-revision/META-INF/iris/generation-kernels/abi-1.revision").absolutePath]
|
List<String> arguments = ['verify-dependencies', file("../../core/build/${runtimeVariant}generated/generation-revision/META-INF/iris/generation-kernels/abi-1.revision").absolutePath]
|
||||||
for (ResolvedArtifact artifact : configurations.bundle.resolvedConfiguration.resolvedArtifacts) {
|
for (ResolvedArtifact artifact : configurations.bundle.resolvedConfiguration.resolvedArtifacts) {
|
||||||
String group = artifact.moduleVersion.id.group
|
String group = artifact.moduleVersion.id.group
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import groovy.json.JsonSlurper
|
||||||
|
|
||||||
|
boolean adapter = rootProject.name.startsWith('iris-')
|
||||||
|
File irisDirectory = adapter ? new File(settingsDir, '../..').canonicalFile : settingsDir
|
||||||
|
String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.3')
|
||||||
|
Map<String, Map<String, Object>> modTargets = (Map<String, Map<String, Object>>) new JsonSlurper()
|
||||||
|
.parse(new File(irisDirectory, 'gradle/mod-targets.json'))
|
||||||
|
Map<String, Object> modTarget = modTargets.get(minecraftVersion)
|
||||||
|
if (modTarget == null) {
|
||||||
|
throw new GradleException("Unsupported Iris mod target ${minecraftVersion}; choose ${modTargets.keySet().join(', ')}")
|
||||||
|
}
|
||||||
|
String runtimeJava = providers.gradleProperty('irisRuntimeJava').getOrElse(modTarget.java.toString())
|
||||||
|
String variant = adapter ? "minecraft${minecraftVersion}-java${runtimeJava}" : "java${runtimeJava}"
|
||||||
|
File projectCacheRoot = gradle.startParameter.projectCacheDir ?: new File(settingsDir, '.gradle')
|
||||||
|
gradle.startParameter.projectCacheDir = new File(projectCacheRoot, variant)
|
||||||
|
|
||||||
|
String runtimeOutputVariant = runtimeJava == '21' ? 'java21/' : ''
|
||||||
|
if (adapter) {
|
||||||
|
runtimeOutputVariant = "modded/${rootProject.name}/${variant}/"
|
||||||
|
} else if (gradle.parent != null && gradle.parent.extensions.extraProperties.has('irisRuntimeOutputVariant')) {
|
||||||
|
runtimeOutputVariant = gradle.parent.extensions.extraProperties.get('irisRuntimeOutputVariant').toString()
|
||||||
|
}
|
||||||
|
gradle.extensions.extraProperties.set('irisRuntimeOutputVariant', runtimeOutputVariant)
|
||||||
@@ -22,6 +22,7 @@ plugins {
|
|||||||
|
|
||||||
rootProject.name = 'Iris'
|
rootProject.name = 'Iris'
|
||||||
|
|
||||||
|
apply from: new File(settingsDir, 'gradle/runtime-cache.settings.gradle')
|
||||||
apply from: new File(settingsDir, 'gradle/volmlib-resolution.settings.gradle')
|
apply from: new File(settingsDir, 'gradle/volmlib-resolution.settings.gradle')
|
||||||
|
|
||||||
include(':core', ':core:agent')
|
include(':core', ':core:agent')
|
||||||
|
|||||||
Reference in new issue
Block a user