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:
Brian Neumann-Fopiano committed 2026-10-07 10:25:41 -04:00
1 parent e72189adc7
commit cfa274825f
56 files changed
+1656 -280

No files matched your search

@@ -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");
@@ -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);