From e3c6a329683e7163e36c7908b08e066c210004fb Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sun, 13 Sep 2026 12:53:43 -0400 Subject: [PATCH] Propere types --- .../storage/matter/IrisMatterSupport.java | 14 +- .../mantle/TerrainComposedCavernTest.java | 6 +- .../matter/IrisMatterPersistenceTest.java | 202 ++++++++++++++++++ 3 files changed, 211 insertions(+), 11 deletions(-) create mode 100644 core/src/test/java/art/arcane/iris/world/storage/matter/IrisMatterPersistenceTest.java diff --git a/core/src/main/java/art/arcane/iris/world/storage/matter/IrisMatterSupport.java b/core/src/main/java/art/arcane/iris/world/storage/matter/IrisMatterSupport.java index de15b2127..f72ad9690 100644 --- a/core/src/main/java/art/arcane/iris/world/storage/matter/IrisMatterSupport.java +++ b/core/src/main/java/art/arcane/iris/world/storage/matter/IrisMatterSupport.java @@ -40,14 +40,14 @@ public final class IrisMatterSupport { } IrisMatter.registerSliceType(new EntityMatter()); - IrisMatter.registerSliceType(new IdentifierMatter()); - IrisMatter.registerSliceType(new NativeStructureOwnershipMatter()); + IrisMatter.registerSliceType("art.arcane.iris.core.link.Identifier", new IdentifierMatter()); + IrisMatter.registerSliceType("art.arcane.iris.engine.framework.NativeStructureOwnershipBundle", new NativeStructureOwnershipMatter()); IrisMatter.registerSliceType(new PlatformBlockMatter()); - IrisMatter.registerSliceType(new HydrologyCaveMatter()); - IrisMatter.registerSliceType(new PreObjectMatter()); - IrisMatter.registerSliceType(new SpawnerMatter()); - IrisMatter.registerSliceType(new TileMatter()); - IrisMatter.registerSliceType(new TreeBlockMaterialMatter()); + IrisMatter.registerSliceType("art.arcane.iris.engine.hydrology.cave.HydrologyCaveCell", new HydrologyCaveMatter()); + IrisMatter.registerSliceType("art.arcane.iris.util.project.matter.PreObjectMatterCell", new PreObjectMatter()); + IrisMatter.registerSliceType("art.arcane.iris.engine.object.IrisSpawner", new SpawnerMatter()); + IrisMatter.registerSliceType("art.arcane.iris.util.project.matter.TileWrapper", new TileMatter()); + IrisMatter.registerSliceType("art.arcane.iris.engine.framework.TreeBlockMaterial", new TreeBlockMaterialMatter()); registered = true; } diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/TerrainComposedCavernTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/TerrainComposedCavernTest.java index a4f46d781..bc24f3d56 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/TerrainComposedCavernTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/TerrainComposedCavernTest.java @@ -3,8 +3,7 @@ package art.arcane.iris.generation.mantle; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.world.storage.matter.PreObjectMatterCell; -import art.arcane.iris.world.storage.matter.HydrologyCaveMatter; -import art.arcane.iris.world.storage.matter.PreObjectMatter; +import art.arcane.iris.world.storage.matter.IrisMatterSupport; import art.arcane.volmlib.util.mantle.runtime.MantleChunk; import art.arcane.volmlib.util.matter.IrisMatter; import art.arcane.volmlib.util.matter.Matter; @@ -34,8 +33,7 @@ import static org.mockito.Mockito.verifyNoInteractions; public class TerrainComposedCavernTest { @BeforeClass public static void registerMatter() { - IrisMatter.registerSliceType(new PreObjectMatter()); - IrisMatter.registerSliceType(new HydrologyCaveMatter()); + IrisMatterSupport.ensureRegistered(); } @Test diff --git a/core/src/test/java/art/arcane/iris/world/storage/matter/IrisMatterPersistenceTest.java b/core/src/test/java/art/arcane/iris/world/storage/matter/IrisMatterPersistenceTest.java new file mode 100644 index 000000000..c3535a3b6 --- /dev/null +++ b/core/src/test/java/art/arcane/iris/world/storage/matter/IrisMatterPersistenceTest.java @@ -0,0 +1,202 @@ +package art.arcane.iris.world.storage.matter; + +import art.arcane.iris.generation.block.TileData; +import art.arcane.iris.generation.decoration.tree.TreeBlockMaterial; +import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; +import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; +import art.arcane.iris.integration.Identifier; +import art.arcane.iris.pack.loading.IrisData; +import art.arcane.iris.pack.loading.IrisRegistrant; +import art.arcane.iris.pack.loading.ResourceLoader; +import art.arcane.iris.structure.nativegen.NativeStructureOwnershipBundle; +import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; +import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord.DecisionSnapshot; +import art.arcane.iris.testsupport.PlatformLeakGuard; +import art.arcane.iris.world.entity.IrisSpawner; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.matter.IrisMatter; +import art.arcane.volmlib.util.matter.Matter; +import art.arcane.volmlib.util.matter.MatterHeader; +import art.arcane.volmlib.util.matter.MatterSlice; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(Parameterized.class) +public class IrisMatterPersistenceTest { + private static final TypeToken> TILE_PROPERTIES = new TypeToken<>() { }; + + @ClassRule + public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean(); + + private final Fixture fixture; + private TileData.TileReader previousReader; + + public IrisMatterPersistenceTest(Fixture fixture) { + this.fixture = fixture; + } + + @Parameterized.Parameters(name = "{index}: {0}") + public static List> fixtures() { + IrisSpawner spawner = new IrisSpawner(); + spawner.setLoadKey("test-spawner"); + KMap tileProperties = new KMap<>(); + tileProperties.put("CustomName", "stored chest"); + HydrologyCaveCell hydrology = new HydrologyCaveCell( + HydrologyCaveAction.WET_SOURCE, "river", "iris:flooded"); + NativeStructureOwnershipRecord ownership = new NativeStructureOwnershipRecord( + NativeStructureOwnershipRecord.CURRENT_SCHEMA, "minecraft:village_plains", + 0, 0, 42L, 64, 0, 64, 0, 15, 80, 15, 70, + 0, 0, 0, 0, "1".repeat(64), new DecisionSnapshot("null", "{}")); + return List.of( + new Fixture<>(Identifier.class, new Identifier("custom", "stored_block"), + "art.arcane.iris.core.link.Identifier"), + new Fixture<>(IrisSpawner.class, spawner, + "art.arcane.iris.engine.object.IrisSpawner"), + new Fixture<>(TileWrapper.class, new TileWrapper(new TileData("minecraft:chest", tileProperties)), + "art.arcane.iris.util.project.matter.TileWrapper"), + new Fixture<>(PreObjectMatterCell.class, + PreObjectMatterCell.string("stored marker").captureHydrology(hydrology), + "art.arcane.iris.util.project.matter.PreObjectMatterCell"), + new Fixture<>(HydrologyCaveCell.class, hydrology, + "art.arcane.iris.engine.hydrology.cave.HydrologyCaveCell"), + new Fixture<>(NativeStructureOwnershipBundle.class, + NativeStructureOwnershipBundle.empty().with(ownership), + "art.arcane.iris.engine.framework.NativeStructureOwnershipBundle"), + new Fixture<>(TreeBlockMaterial.class, TreeBlockMaterial.of("minecraft:oak_log"), + "art.arcane.iris.engine.framework.TreeBlockMaterial")); + } + + @Before + public void registerStorage() { + IrisMatterSupport.ensureRegistered(); + Gson gson = new Gson(); + previousReader = TileData.bindPlatformReader(input -> new TileData(input.readUTF(), + gson.fromJson(input.readUTF(), TILE_PROPERTIES))); + } + + @After + public void restoreStorage() { + TileData.restorePlatformReader(previousReader); + } + + @Test + public void savesKeepTheirEstablishedTypeIdentifier() throws IOException { + byte[] saved = save(source(fixture)); + try (DataInputStream input = new DataInputStream(new ByteArrayInputStream(saved))) { + assertEquals(2, input.readInt()); + assertEquals(2, input.readInt()); + assertEquals(2, input.readInt()); + assertEquals(1, input.readUnsignedByte()); + new MatterHeader().read(input); + int sliceBytes = input.readInt(); + assertEquals(input.available(), sliceBytes); + assertEquals(fixture.id(), input.readUTF()); + } + assertValues(read(saved)); + } + + @Test + public void readsAndResavesDataWithEstablishedIdentifiers() throws IOException { + Matter loaded = read(establishedFile()); + assertValues(loaded); + assertValues(read(save(loaded))); + } + + private byte[] establishedFile() throws IOException { + Matter source = source(fixture); + ByteArrayOutputStream sliceBytes = new ByteArrayOutputStream(); + source.getSlice(fixture.type()).write(new DataOutputStream(sliceBytes)); + ByteArrayOutputStream storedSlice = new ByteArrayOutputStream(); + try (DataInputStream input = new DataInputStream(new ByteArrayInputStream(sliceBytes.toByteArray())); + DataOutputStream output = new DataOutputStream(storedSlice)) { + input.readUTF(); + output.writeUTF(fixture.id()); + input.transferTo(output); + } + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try (DataOutputStream output = new DataOutputStream(bytes)) { + output.writeInt(2); + output.writeInt(2); + output.writeInt(2); + output.writeByte(1); + output.writeUTF("stored world"); + output.writeLong(123456L); + output.writeShort(1); + output.writeInt(storedSlice.size()); + storedSlice.writeTo(output); + } + return bytes.toByteArray(); + } + + private Matter source(Fixture entry) { + Matter matter = new IrisMatter(2, 2, 2); + MatterSlice slice = matter.slice(entry.type()); + slice.set(1, 0, 1, entry.value()); + slice.set(1, 1, 1, entry.value()); + return matter; + } + + private byte[] save(Matter matter) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + matter.write(bytes); + return bytes.toByteArray(); + } + + private Matter read(byte[] bytes) throws IOException { + try (IrisMatterContext.Scope scope = IrisMatterContext.open(data()); + ByteArrayInputStream input = new ByteArrayInputStream(bytes)) { + return Matter.read(input); + } + } + + @SuppressWarnings("unchecked") + private IrisData data() { + IrisData data = mock(IrisData.class); + KMap, ResourceLoader> loaders = new KMap<>(); + if (fixture.value() instanceof IrisSpawner spawner) { + ResourceLoader loader = mock(ResourceLoader.class); + when(loader.load(spawner.getLoadKey())).thenReturn(spawner); + loaders.put(IrisSpawner.class, loader); + } + when(data.getLoaders()).thenReturn(loaders); + return data; + } + + private void assertValues(Matter matter) { + assertEquals(1, matter.getSliceTypes().size()); + MatterSlice slice = matter.getSlice(fixture.type()); + assertNotNull(slice); + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 2; y++) { + for (int z = 0; z < 2; z++) { + assertEquals(x == 1 && z == 1 ? fixture.value() : null, slice.get(x, y, z)); + } + } + } + } + + public record Fixture(Class type, T value, String id) { + @Override + public String toString() { + return type.getSimpleName(); + } + } +}