mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-25 13:30:08 +00:00
Refine terrain and pregeneration
- Add coherent river width variation and shape source and terminal pools. - Preserve separated hydrology intervals when checking river connectivity. - Reduce footprint sorting, engine context lookup, and terrain blending work. - Reuse modded snapshot validation after content and registry checks. - Explain production pack update staging and restart requirements. - Retain pregeneration map updates before its desktop window opens. - Improve pregeneration layout, coordinate hover, keyboard controls, and error details. - Add the new desktop labels to every supported language catalog. - Add the buildPsychoLT entry point for verified platform outputs. - Cover terrain, validation, and desktop behavior with regression tests.
This commit is contained in:
56 files changed
+1656
-280
No files matched your search
+9
-7
@@ -6,10 +6,9 @@ import art.arcane.volmlib.nativelib.terrain.NativeWorld;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.configuration.IrisSettings;
|
||||
import art.arcane.iris.pack.BrokenPackException;
|
||||
import art.arcane.iris.pack.PackValidationCache;
|
||||
import art.arcane.iris.pack.PackFingerprints;
|
||||
import art.arcane.iris.pack.PackValidationRegistry;
|
||||
import art.arcane.iris.pack.PackValidationResult;
|
||||
import art.arcane.iris.pack.PackValidator;
|
||||
import art.arcane.iris.world.history.GenerationEpoch;
|
||||
import art.arcane.iris.world.history.GenerationEpochContractFactory;
|
||||
import art.arcane.iris.world.history.GenerationHistory;
|
||||
@@ -174,7 +173,7 @@ final class ModdedGenerationHistoryStorage {
|
||||
static ActivePack resolveActive(GenerationHistory history) throws IOException {
|
||||
GenerationEpoch epoch = history.activeEpoch();
|
||||
Path packRoot = normalize(history.activePackRoot());
|
||||
validatePack(packRoot, epoch.packFingerprint());
|
||||
validatePack(packRoot);
|
||||
validateDimension(packRoot, epoch);
|
||||
return new ActivePack(
|
||||
packRoot,
|
||||
@@ -184,13 +183,16 @@ final class ModdedGenerationHistoryStorage {
|
||||
);
|
||||
}
|
||||
|
||||
private static void validatePack(Path packRoot, String fingerprint) {
|
||||
String contextFingerprint = PackValidationCache.contextFingerprint();
|
||||
PackValidationResult validation = PackValidator.validate(packRoot.toFile());
|
||||
PackValidationRegistry.publish(packRoot, validation, fingerprint, contextFingerprint);
|
||||
static PackValidationResult validatePack(Path packRoot) {
|
||||
PackValidationResult validation = PackValidationRegistry.getMatching(
|
||||
packRoot, PackFingerprints.computePackTreeFingerprint(packRoot.toFile()));
|
||||
if (validation == null) {
|
||||
validation = ModdedStartup.validatePack(packRoot.toFile());
|
||||
}
|
||||
if (!validation.isLoadable()) {
|
||||
throw new BrokenPackException(packRoot.toString(), validation.getBlockingErrors());
|
||||
}
|
||||
return validation;
|
||||
}
|
||||
|
||||
private static void validateDimension(
|
||||
|
||||
+2
-1
@@ -119,7 +119,8 @@ public final class ModdedPackUpdateService implements ModdedTickableService {
|
||||
+ ", latest built-in release v" + update.version() + ".");
|
||||
updates.add("Back up and review custom edits in " + new File(packsFolder, pack.name())
|
||||
+ "; close Studio, then update with /iris download pack=" + pack.name() + " overwrite=true.");
|
||||
updates.add("Existing worlds retain their recorded production pack; this updates the authoring pack for new worlds.");
|
||||
updates.add("Existing worlds retain their recorded production pack. Back up the complete world, then stage an update with /iris world update <dimension> "
|
||||
+ pack.name() + " and restart the server to activate it.");
|
||||
}
|
||||
}
|
||||
notices = List.copyOf(updates);
|
||||
|
||||
+52
@@ -3,6 +3,7 @@ package art.arcane.iris.modded;
|
||||
import art.arcane.iris.modded.command.ModdedPackCommands;
|
||||
import art.arcane.iris.pack.BrokenPackException;
|
||||
import art.arcane.iris.pack.PackValidationRegistry;
|
||||
import art.arcane.iris.pack.PackValidationCache;
|
||||
import art.arcane.iris.pack.PackValidationResult;
|
||||
import art.arcane.iris.pack.PackValidator;
|
||||
import org.junit.After;
|
||||
@@ -15,6 +16,7 @@ import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
@@ -22,6 +24,7 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.times;
|
||||
|
||||
public class ModdedPackValidationPublicationTest {
|
||||
@Rule
|
||||
@@ -32,6 +35,55 @@ public class ModdedPackValidationPublicationTest {
|
||||
PackValidationRegistry.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatedSnapshotValidationParsesUnchangedPackOnce() throws Exception {
|
||||
File root = temporaryFolder.newFolder("snapshot");
|
||||
PackValidationResult result = loadable(root);
|
||||
try (MockedStatic<PackValidator> validator = mockStatic(PackValidator.class)) {
|
||||
validator.when(() -> PackValidator.validate(root)).thenReturn(result);
|
||||
assertSame(result, ModdedGenerationHistoryStorage.validatePack(root.toPath()));
|
||||
assertSame(result, ModdedGenerationHistoryStorage.validatePack(root.toPath()));
|
||||
validator.verify(() -> PackValidator.validate(root), times(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void snapshotValidationRejectsChangedBytesWithPreservedTimestamp() throws Exception {
|
||||
File root = temporaryFolder.newFolder("snapshot");
|
||||
File content = new File(root, "content.json");
|
||||
Files.writeString(content.toPath(), "valid");
|
||||
FileTime timestamp = Files.getLastModifiedTime(content.toPath());
|
||||
PackValidationResult broken = new PackValidationResult(root.getName(), List.of("invalid"), List.of(), 1L);
|
||||
try (MockedStatic<PackValidator> validator = mockStatic(PackValidator.class)) {
|
||||
validator.when(() -> PackValidator.validate(root)).thenReturn(loadable(root), broken);
|
||||
ModdedGenerationHistoryStorage.validatePack(root.toPath());
|
||||
Files.writeString(content.toPath(), "wrong");
|
||||
Files.setLastModifiedTime(content.toPath(), timestamp);
|
||||
assertThrows(BrokenPackException.class,
|
||||
() -> ModdedGenerationHistoryStorage.validatePack(root.toPath()));
|
||||
assertThrows(BrokenPackException.class,
|
||||
() -> ModdedGenerationHistoryStorage.validatePack(root.toPath()));
|
||||
validator.verify(() -> PackValidator.validate(root), times(2));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void snapshotValidationReparsesChangedRegistryContext() throws Exception {
|
||||
File root = temporaryFolder.newFolder("snapshot");
|
||||
AtomicReference<String> context = new AtomicReference<>("first");
|
||||
PackValidationResult first = loadable(root);
|
||||
PackValidationResult second = loadable(root);
|
||||
try (MockedStatic<PackValidator> validator = mockStatic(PackValidator.class);
|
||||
MockedStatic<PackValidationCache> cache = mockStatic(PackValidationCache.class)) {
|
||||
cache.when(PackValidationCache::contextFingerprint).thenAnswer(ignored -> context.get());
|
||||
validator.when(() -> PackValidator.validate(root)).thenReturn(first, second);
|
||||
assertSame(first, ModdedGenerationHistoryStorage.validatePack(root.toPath()));
|
||||
context.set("second");
|
||||
assertSame(second, ModdedGenerationHistoryStorage.validatePack(root.toPath()));
|
||||
validator.verify(() -> PackValidator.validate(root), times(2));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validationPublishesTheCanonicalRootAndPackName() throws Exception {
|
||||
File root = temporaryFolder.newFolder("terrain");
|
||||
|
||||
+3
@@ -65,6 +65,9 @@ public class ModdedPackUpdateServiceTest {
|
||||
verify(online, times(1)).sendMessage(contains("installed v1, latest built-in release v2"));
|
||||
verify(online, times(1)).sendMessage(contains(new File(root, "overworld").toString()));
|
||||
verify(online, times(1)).sendMessage(contains("/iris download pack=overworld overwrite=true"));
|
||||
verify(online, times(1)).sendMessage(contains("Back up the complete world"));
|
||||
verify(online, times(1)).sendMessage(contains("/iris world update <dimension> overworld"));
|
||||
verify(online, times(1)).sendMessage(contains("restart the server to activate it"));
|
||||
verify(online, never()).sendMessage(contains("plugins/"));
|
||||
NativeProtocolPlayer later = player(true, false);
|
||||
service.notifyPlayer(later);
|
||||
|
||||
Reference in new issue
Block a user