diff --git a/core/src/main/java/art/arcane/iris/engine/IrisEngineMantle.java b/core/src/main/java/art/arcane/iris/engine/IrisEngineMantle.java index 6e26dc4bf..794d758d2 100644 --- a/core/src/main/java/art/arcane/iris/engine/IrisEngineMantle.java +++ b/core/src/main/java/art/arcane/iris/engine/IrisEngineMantle.java @@ -18,7 +18,6 @@ package art.arcane.iris.engine; -import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.core.IrisSettings; import art.arcane.iris.core.loader.IrisData; import art.arcane.iris.core.tools.WorldMaintenance; @@ -32,6 +31,7 @@ import art.arcane.iris.engine.mantle.components.MantleFloatingObjectComponent; import art.arcane.iris.engine.mantle.components.MantleObjectComponent; import art.arcane.iris.engine.mantle.components.IrisStructureComponent; import art.arcane.iris.spi.IrisLogging; +import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformBlockState; import art.arcane.iris.util.project.matter.IrisMatterContext; import art.arcane.iris.util.project.matter.IrisMatterSupport; @@ -51,7 +51,6 @@ import art.arcane.iris.util.common.format.C; import art.arcane.volmlib.util.matter.IrisMatter; import art.arcane.volmlib.util.matter.Matter; import art.arcane.volmlib.util.matter.MatterSlice; -import art.arcane.iris.platform.bukkit.BukkitPlatform; import art.arcane.iris.util.common.parallel.HyperLock; import art.arcane.iris.util.common.parallel.MultiBurst; import lombok.AccessLevel; @@ -73,7 +72,6 @@ import java.util.function.Supplier; @EqualsAndHashCode(exclude = "engine") @ToString(exclude = "engine") public class IrisEngineMantle implements EngineMantle { - private static final boolean BUKKIT_PRESENT = IrisMatterSupport.isBukkitPresent(); private final Engine engine; private final Mantle mantle; @Getter(AccessLevel.NONE) @@ -248,10 +246,7 @@ public class IrisEngineMantle implements EngineMantle { if (value instanceof PlatformBlockState) { return PlatformBlockState.class; } - if (BUKKIT_PRESENT) { - return BukkitPlatform.classifyMantleValue(value); - } - return value.getClass(); + return IrisPlatforms.get().classifyMantleValue(value); } @Override diff --git a/core/src/main/java/art/arcane/iris/engine/decorator/DecoratorCore.java b/core/src/main/java/art/arcane/iris/engine/decorator/DecoratorCore.java index b752e8910..6420bd650 100644 --- a/core/src/main/java/art/arcane/iris/engine/decorator/DecoratorCore.java +++ b/core/src/main/java/art/arcane/iris/engine/decorator/DecoratorCore.java @@ -43,21 +43,11 @@ final class DecoratorCore { private static final String WEEPING_VINES_PLANT = "minecraft:weeping_vines_plant"; private static final String TWISTING_VINES = "minecraft:twisting_vines"; private static final String TWISTING_VINES_PLANT = "minecraft:twisting_vines_plant"; - private static final boolean BUKKIT_PRESENT = detectBukkit(); private static volatile PlatformBlockState weepingVines; private static volatile PlatformBlockState weepingVinesPlant; private static volatile PlatformBlockState twistingVines; private static volatile PlatformBlockState twistingVinesPlant; - private static boolean detectBukkit() { - try { - Class.forName("org.bukkit.Bukkit", false, DecoratorCore.class.getClassLoader()); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - static final ThreadLocal SCRATCH_OPTS = ThreadLocal.withInitial(PlaceOpts::new); static final class PlaceOpts { @@ -391,9 +381,9 @@ final class DecoratorCore { if (!B.isVineBlock(b)) { return b; } - if (!BUKKIT_PRESENT) { - DecoratorPlatformHooks.FaceFixer fixer = DecoratorPlatformHooks.faceFixer(); - return fixer == null ? b : fixer.fixFaces(b, hunk, rX, rZ, x, y, z, mantle); + DecoratorPlatformHooks.FaceFixer fixer = DecoratorPlatformHooks.faceFixer(); + if (fixer != null) { + return fixer.fixFaces(b, hunk, rX, rZ, x, y, z, mantle); } BlockData rawB = (BlockData) b.nativeHandle(); BlockData cloned = rawB.clone(); @@ -447,9 +437,9 @@ final class DecoratorCore { if (!B.canPlaceOnto(decorator, surface)) { return false; } - if (!BUKKIT_PRESENT) { - DecoratorPlatformHooks.SurfaceSturdiness sturdiness = DecoratorPlatformHooks.surfaceSturdiness(); - return sturdiness == null ? B.isSolid(surface) : sturdiness.canGoOn(surface); + DecoratorPlatformHooks.SurfaceSturdiness sturdiness = DecoratorPlatformHooks.surfaceSturdiness(); + if (sturdiness != null) { + return sturdiness.canGoOn(surface); } return ((BlockData) surface.nativeHandle()).isFaceSturdy(BlockFace.UP, BlockSupport.FULL); } diff --git a/core/src/main/java/art/arcane/iris/engine/object/BlockDataMergeSupport.java b/core/src/main/java/art/arcane/iris/engine/object/BlockDataMergeSupport.java index 562d0d6fe..2c12aedfe 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/BlockDataMergeSupport.java +++ b/core/src/main/java/art/arcane/iris/engine/object/BlockDataMergeSupport.java @@ -9,7 +9,6 @@ import java.util.Objects; import java.util.function.Function; public final class BlockDataMergeSupport { - private static final boolean BUKKIT_PRESENT = detectBukkit(); private static volatile StateMerger PLATFORM_MERGER = null; private BlockDataMergeSupport() { @@ -29,31 +28,15 @@ public final class BlockDataMergeSupport { PLATFORM_MERGER = merger; } - private static boolean detectBukkit() { - try { - Class.forName("org.bukkit.Bukkit", false, BlockDataMergeSupport.class.getClassLoader()); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - static PlatformBlockState merge(PlatformBlockState base, PlatformBlockState update) { - if (!BUKKIT_PRESENT) { - StateMerger merger = requirePlatformMerger(PLATFORM_MERGER); + StateMerger merger = PLATFORM_MERGER; + if (merger != null) { return merger.merge(base, update); } BlockData merged = merge((BlockData) base.nativeHandle(), (BlockData) update.nativeHandle(), BukkitBlockResolution::get); return merged == null ? null : BukkitBlockState.of(merged); } - static StateMerger requirePlatformMerger(StateMerger merger) { - if (merger == null) { - throw new IllegalStateException("No platform block-state merger is bound"); - } - return merger; - } - static BlockData merge(BlockData base, BlockData update, Function resolver) { try { return base.merge(update); diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisObjectRotation.java b/core/src/main/java/art/arcane/iris/engine/object/IrisObjectRotation.java index a2094c482..cba70c470 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisObjectRotation.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisObjectRotation.java @@ -53,7 +53,6 @@ import java.util.Set; @Desc("Configures rotation for iris") @Data public class IrisObjectRotation { - private static final boolean BUKKIT_PRESENT = detectBukkit(); private static volatile StateRotator PLATFORM_ROTATOR = null; public interface StateRotator { @@ -70,15 +69,6 @@ public class IrisObjectRotation { PLATFORM_ROTATOR = rotator; } - private static boolean detectBukkit() { - try { - Class.forName("org.bukkit.Bukkit", false, IrisObjectRotation.class.getClassLoader()); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - private static final class Faces { private static final List WALL_FACES = List.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST); } @@ -298,8 +288,8 @@ public class IrisObjectRotation { return null; } - if (!BUKKIT_PRESENT) { - StateRotator rotator = requirePlatformRotator(PLATFORM_ROTATOR); + StateRotator rotator = PLATFORM_ROTATOR; + if (rotator != null) { return rotator.rotate(this, state, spinx, spiny, spinz); } @@ -313,13 +303,6 @@ public class IrisObjectRotation { return rotated == null ? null : BukkitBlockState.of(rotated); } - static StateRotator requirePlatformRotator(StateRotator rotator) { - if (rotator == null) { - throw new IllegalStateException("No platform block-state rotator is bound"); - } - return rotator; - } - private static boolean canRotateBlockData(BlockData data) { return data instanceof Directional || data instanceof Rotatable diff --git a/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java b/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java index bf4458639..bb2fe4f6c 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java +++ b/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java @@ -45,9 +45,9 @@ import java.util.stream.StreamSupport; /** * Bukkit-only pre-key tile format. Unreachable from the modded loaders: every entry point is * either Bukkit-typed ({@link #fromBukkit(BlockState)}, reached only from - * {@link TileData#getTileState(Block, boolean)} and the Bukkit structure importer) or sits behind - * the {@code BUKKIT_PRESENT} short-circuit in {@link TileData#read(DataInputStream)}, which hands - * off to the bound platform reader before this class is ever referenced. The nested handler types + * {@link TileData#getTileState(Block, boolean)} and the Bukkit structure importer) or follows the + * bound platform-reader path in {@link TileData#read(DataInputStream)}, which returns before this + * class is ever referenced. The nested handler types * therefore keep their raw Bukkit fields. */ @ToString diff --git a/core/src/main/java/art/arcane/iris/engine/object/TileData.java b/core/src/main/java/art/arcane/iris/engine/object/TileData.java index cea2c1051..2b41dcccc 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/TileData.java +++ b/core/src/main/java/art/arcane/iris/engine/object/TileData.java @@ -64,7 +64,6 @@ public class TileData implements Cloneable { * never part of that walk. Bounded by the number of distinct material keys a pack can name. */ private static final Map RESOLVED_MATERIALS = new ConcurrentHashMap<>(); - private static final boolean BUKKIT_PRESENT = detectBukkit(); private static volatile TileReader PLATFORM_READER = null; private static volatile TileFactory PLATFORM_FACTORY = null; @@ -96,15 +95,6 @@ public class TileData implements Cloneable { PLATFORM_FACTORY = factory; } - private static boolean detectBukkit() { - try { - Class.forName("org.bukkit.Bukkit", false, TileData.class.getClassLoader()); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - /** * The block key this tile belongs to, stored as text so the field type never drags * org.bukkit.Material onto a Gson field walk or into generated equals/hashCode/toString. @@ -147,8 +137,9 @@ public class TileData implements Cloneable { } public static TileData of(PlatformBlockState state, KMap properties) { - if (!BUKKIT_PRESENT) { - return requirePlatformFactory(PLATFORM_FACTORY).create(state, properties); + TileFactory factory = PLATFORM_FACTORY; + if (factory != null) { + return factory.create(state, properties); } Object handle = state.nativeHandle(); if (!(handle instanceof BlockData blockData)) { @@ -158,8 +149,9 @@ public class TileData implements Cloneable { } public static TileData read(DataInputStream in) throws IOException { - if (!BUKKIT_PRESENT) { - return requirePlatformReader(PLATFORM_READER).read(in); + TileReader reader = PLATFORM_READER; + if (reader != null) { + return reader.read(in); } if (!in.markSupported()) throw new IOException("Mark not supported"); @@ -215,20 +207,6 @@ public class TileData implements Cloneable { return resolved; } - static TileFactory requirePlatformFactory(TileFactory factory) { - if (factory == null) { - throw new IllegalStateException("No platform tile-data factory is bound"); - } - return factory; - } - - static TileReader requirePlatformReader(TileReader reader) throws IOException { - if (reader == null) { - throw new IOException("No platform tile-data reader is bound"); - } - return reader; - } - public boolean isApplicable(BlockData data) { Material resolved = resolveMaterial(); return resolved != null && data.getMaterial() == resolved; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitPlatform.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitPlatform.java index a6b81b666..b681fe28e 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitPlatform.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitPlatform.java @@ -175,7 +175,8 @@ public final class BukkitPlatform implements IrisPlatform { return BukkitBiome.of((Biome) biome); } - public static Class classifyMantleValue(Object value) { + @Override + public Class classifyMantleValue(Object value) { if (value instanceof World) { return World.class; } @@ -191,6 +192,11 @@ public final class BukkitPlatform implements IrisPlatform { return value.getClass(); } + @Override + public boolean supportsMatterWorldIo() { + return true; + } + public static void unregisterListener(Object candidate) { if (candidate instanceof Listener listener) { volmitPlugin().unregisterListener(listener); diff --git a/core/src/main/java/art/arcane/iris/util/project/matter/IrisMatterSupport.java b/core/src/main/java/art/arcane/iris/util/project/matter/IrisMatterSupport.java index 83097b3cb..fd07e3554 100644 --- a/core/src/main/java/art/arcane/iris/util/project/matter/IrisMatterSupport.java +++ b/core/src/main/java/art/arcane/iris/util/project/matter/IrisMatterSupport.java @@ -36,25 +36,11 @@ import art.arcane.iris.util.common.math.IrisBlockVector; import java.io.File; public final class IrisMatterSupport { - private static final boolean BUKKIT_PRESENT = detectBukkit(); private static boolean registered; private IrisMatterSupport() { } - public static boolean isBukkitPresent() { - return BUKKIT_PRESENT; - } - - private static boolean detectBukkit() { - try { - Class.forName("org.bukkit.World", false, IrisMatterSupport.class.getClassLoader()); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - public static synchronized void ensureRegistered() { if (registered) { return; diff --git a/core/src/main/java/art/arcane/iris/util/project/matter/slices/EntityMatter.java b/core/src/main/java/art/arcane/iris/util/project/matter/slices/EntityMatter.java index 6a89092cc..0b58625b9 100644 --- a/core/src/main/java/art/arcane/iris/util/project/matter/slices/EntityMatter.java +++ b/core/src/main/java/art/arcane/iris/util/project/matter/slices/EntityMatter.java @@ -21,7 +21,7 @@ package art.arcane.iris.util.project.matter.slices; import art.arcane.iris.platform.bukkit.BukkitPlatform; import art.arcane.iris.core.nms.INMS; import art.arcane.iris.engine.object.IrisPosition; -import art.arcane.iris.util.project.matter.IrisMatterSupport; +import art.arcane.iris.spi.IrisPlatforms; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.data.Varint; @@ -53,7 +53,7 @@ public class EntityMatter extends RawMatter { public EntityMatter(int width, int height, int depth) { super(width, height, depth, MatterEntityGroup.class); - if (IrisMatterSupport.isBukkitPresent()) { + if (IrisPlatforms.isBound() && IrisPlatforms.get().supportsMatterWorldIo()) { registerBukkitIO(); } } diff --git a/core/src/main/java/art/arcane/iris/util/project/matter/slices/TileMatter.java b/core/src/main/java/art/arcane/iris/util/project/matter/slices/TileMatter.java index 71dee5144..7b2d0586c 100644 --- a/core/src/main/java/art/arcane/iris/util/project/matter/slices/TileMatter.java +++ b/core/src/main/java/art/arcane/iris/util/project/matter/slices/TileMatter.java @@ -20,7 +20,7 @@ package art.arcane.iris.util.project.matter.slices; import art.arcane.iris.engine.object.TileData; -import art.arcane.iris.util.project.matter.IrisMatterSupport; +import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.util.project.matter.TileWrapper; import art.arcane.volmlib.util.data.palette.Palette; import art.arcane.volmlib.util.matter.Sliced; @@ -42,7 +42,7 @@ public class TileMatter extends RawMatter { public TileMatter(int width, int height, int depth) { super(width, height, depth, TileWrapper.class); - if (IrisMatterSupport.isBukkitPresent()) { + if (IrisPlatforms.isBound() && IrisPlatforms.get().supportsMatterWorldIo()) { registerBukkitIO(); } } diff --git a/core/src/test/java/art/arcane/iris/engine/IrisEnginePlatformHookIsolationTest.java b/core/src/test/java/art/arcane/iris/engine/IrisEnginePlatformHookIsolationTest.java index b6b0f2edf..a92760089 100644 --- a/core/src/test/java/art/arcane/iris/engine/IrisEnginePlatformHookIsolationTest.java +++ b/core/src/test/java/art/arcane/iris/engine/IrisEnginePlatformHookIsolationTest.java @@ -47,7 +47,7 @@ public class IrisEnginePlatformHookIsolationTest { ); @Test - public void sharedGeneratorHotPathsDoNotLinkBukkitImplementations() throws IOException { + public void sharedGeneratorHotPathsDoNotLinkBukkitImplementations() throws IOException, ClassNotFoundException { assertNoClassLinks(IrisEngine.class, BUKKIT_ENGINE_CLASSES); assertNoClassLinks(EngineBackgroundTasks.class, BUKKIT_ENGINE_CLASSES); assertNoClassLinks(EngineDataStore.class, BUKKIT_ENGINE_CLASSES); @@ -56,6 +56,8 @@ public class IrisEnginePlatformHookIsolationTest { assertNoClassLinks(EngineRuntimeBuilder.class, BUKKIT_ENGINE_CLASSES); assertNoClassLinks(EngineShutdownSequence.class, BUKKIT_ENGINE_CLASSES); assertNoClassLinks(EngineTickRegistry.class, BUKKIT_ENGINE_CLASSES); + assertNoClassLinks(IrisEngineMantle.class, BUKKIT_ENGINE_CLASSES); + assertNoClassLinks(Class.forName(IrisEngineMantle.class.getName() + "$1"), BUKKIT_ENGINE_CLASSES); assertNoClassLinks(Engine.class, ENGINE_POLICY_CLASSES); assertNoClassLinks(EngineMode.class, PLATFORM_POLICY_CLASSES); assertNoClassLinks(EngineMantle.class, PLATFORM_POLICY_CLASSES); @@ -63,7 +65,8 @@ public class IrisEnginePlatformHookIsolationTest { } private static void assertNoClassLinks(Class type, List forbiddenClasses) throws IOException { - InputStream stream = type.getResourceAsStream(type.getSimpleName() + ".class"); + String resourceName = type.getName().substring(type.getName().lastIndexOf('.') + 1) + ".class"; + InputStream stream = type.getResourceAsStream(resourceName); assertNotNull(stream); String bytecode; try (InputStream input = stream) { diff --git a/core/src/test/java/art/arcane/iris/engine/decorator/DecoratorCoreTest.java b/core/src/test/java/art/arcane/iris/engine/decorator/DecoratorCoreTest.java index 4170a13c7..14841123d 100644 --- a/core/src/test/java/art/arcane/iris/engine/decorator/DecoratorCoreTest.java +++ b/core/src/test/java/art/arcane/iris/engine/decorator/DecoratorCoreTest.java @@ -429,6 +429,28 @@ public class DecoratorCoreTest { assertEquals("minecraft:twisting_vines", DecoratorCore.stackedVineKey(vine, 3, 2)); } + @Test + public void boundDecoratorHooksWinWhenBukkitClassesArePresent() { + PlatformBlockState vine = mock(PlatformBlockState.class); + PlatformBlockState fixed = mock(PlatformBlockState.class); + PlatformBlockState decorator = mock(PlatformBlockState.class); + PlatformBlockState surface = mock(PlatformBlockState.class); + Hunk output = Hunk.newArrayHunk(1, 1, 1); + DecoratorPlatformHooks.FaceFixer faceFixer = mock(DecoratorPlatformHooks.FaceFixer.class); + DecoratorPlatformHooks.SurfaceSturdiness sturdiness = mock(DecoratorPlatformHooks.SurfaceSturdiness.class); + when(vine.isVineBlock()).thenReturn(true); + when(faceFixer.fixFaces(vine, output, 0, 0, 0, 0, 0, null)).thenReturn(fixed); + when(decorator.canPlaceOnto(surface)).thenReturn(true); + when(sturdiness.canGoOn(surface)).thenReturn(true); + DecoratorPlatformHooks.Bindings previous = DecoratorPlatformHooks.bind(faceFixer, sturdiness); + try { + assertSame(fixed, DecoratorCore.fixFacesForHunk(vine, output, 0, 0, 0, 0, 0, null)); + assertTrue(DecoratorCore.canGoOn(decorator, surface)); + } finally { + DecoratorPlatformHooks.restore(previous); + } + } + private PlatformBlockState airState() { PlatformBlockState air = mock(PlatformBlockState.class); when(air.isAir()).thenReturn(true); diff --git a/core/src/test/java/art/arcane/iris/engine/object/PlatformStateHookContractTest.java b/core/src/test/java/art/arcane/iris/engine/object/PlatformStateHookContractTest.java index 0a099d011..b0c0ef2dc 100644 --- a/core/src/test/java/art/arcane/iris/engine/object/PlatformStateHookContractTest.java +++ b/core/src/test/java/art/arcane/iris/engine/object/PlatformStateHookContractTest.java @@ -1,42 +1,74 @@ package art.arcane.iris.engine.object; +import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.util.collection.KMap; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; import java.io.IOException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class PlatformStateHookContractTest { @Test - public void missingPlatformRotatorFailsLoudly() { - IllegalStateException error = assertThrows(IllegalStateException.class, - () -> IrisObjectRotation.requirePlatformRotator(null)); - - assertEquals("No platform block-state rotator is bound", error.getMessage()); + public void boundRotatorWinsWhenBukkitClassesArePresent() { + PlatformBlockState source = mock(PlatformBlockState.class); + PlatformBlockState rotated = mock(PlatformBlockState.class); + IrisObjectRotation.StateRotator hook = mock(IrisObjectRotation.StateRotator.class); + IrisObjectRotation rotation = new IrisObjectRotation(); + when(hook.rotate(rotation, source, 1, 2, 3)).thenReturn(rotated); + IrisObjectRotation.StateRotator previous = IrisObjectRotation.bindPlatformRotator(hook); + try { + assertSame(rotated, rotation.rotate(source, 1, 2, 3)); + } finally { + IrisObjectRotation.restorePlatformRotator(previous); + } } @Test - public void missingPlatformMergerFailsLoudly() { - IllegalStateException error = assertThrows(IllegalStateException.class, - () -> BlockDataMergeSupport.requirePlatformMerger(null)); - - assertEquals("No platform block-state merger is bound", error.getMessage()); + public void boundMergerWinsWhenBukkitClassesArePresent() { + PlatformBlockState base = mock(PlatformBlockState.class); + PlatformBlockState update = mock(PlatformBlockState.class); + PlatformBlockState merged = mock(PlatformBlockState.class); + BlockDataMergeSupport.StateMerger hook = mock(BlockDataMergeSupport.StateMerger.class); + when(hook.merge(base, update)).thenReturn(merged); + BlockDataMergeSupport.StateMerger previous = BlockDataMergeSupport.bindPlatformMerger(hook); + try { + assertSame(merged, BlockDataMergeSupport.merge(base, update)); + } finally { + BlockDataMergeSupport.restorePlatformMerger(previous); + } } @Test - public void missingPlatformTileFactoryFailsLoudly() { - IllegalStateException error = assertThrows(IllegalStateException.class, - () -> TileData.requirePlatformFactory(null)); - - assertEquals("No platform tile-data factory is bound", error.getMessage()); + public void boundTileFactoryWinsWhenBukkitClassesArePresent() { + PlatformBlockState state = mock(PlatformBlockState.class); + KMap properties = new KMap<>(); + TileData expected = mock(TileData.class); + TileData.TileFactory hook = mock(TileData.TileFactory.class); + when(hook.create(state, properties)).thenReturn(expected); + TileData.TileFactory previous = TileData.bindPlatformFactory(hook); + try { + assertSame(expected, TileData.of(state, properties)); + } finally { + TileData.restorePlatformFactory(previous); + } } @Test - public void missingPlatformTileReaderFailsLoudly() { - IOException error = assertThrows(IOException.class, - () -> TileData.requirePlatformReader(null)); - - assertEquals("No platform tile-data reader is bound", error.getMessage()); + public void boundTileReaderWinsWhenBukkitClassesArePresent() throws IOException { + TileData expected = mock(TileData.class); + TileData.TileReader hook = mock(TileData.TileReader.class); + DataInputStream input = new DataInputStream(new ByteArrayInputStream(new byte[0])); + when(hook.read(input)).thenReturn(expected); + TileData.TileReader previous = TileData.bindPlatformReader(hook); + try { + assertSame(expected, TileData.read(input)); + } finally { + TileData.restorePlatformReader(previous); + } } } diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitSpiConformanceTest.java b/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitSpiConformanceTest.java index 4b31e14de..939f33eed 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitSpiConformanceTest.java +++ b/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitSpiConformanceTest.java @@ -26,7 +26,9 @@ import art.arcane.iris.spi.PlatformRegistries; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Server; +import org.bukkit.World; import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Entity; import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; @@ -102,6 +104,16 @@ public class BukkitSpiConformanceTest { assertSame(BukkitBlockState.of(first), BukkitBlockState.of(second)); } + @Test + public void mantleValuesUseStableBukkitInterfaceSlices() { + BukkitPlatform platform = new BukkitPlatform(); + assertTrue(platform.supportsMatterWorldIo()); + assertSame(World.class, platform.classifyMantleValue(mock(World.class))); + assertSame(BlockData.class, platform.classifyMantleValue(blockData("minecraft:stone"))); + assertSame(Entity.class, platform.classifyMantleValue(mock(Entity.class))); + assertSame(String.class, platform.classifyMantleValue("value")); + } + @Test public void blockStateKeyMatchesCanonicalString() { BlockData data = blockData("iristest:key_block[facing=north,lit=true]"); diff --git a/core/src/test/java/art/arcane/iris/util/project/matter/slices/IrisMatterPlatformSelectionTest.java b/core/src/test/java/art/arcane/iris/util/project/matter/slices/IrisMatterPlatformSelectionTest.java new file mode 100644 index 000000000..0b1323d0c --- /dev/null +++ b/core/src/test/java/art/arcane/iris/util/project/matter/slices/IrisMatterPlatformSelectionTest.java @@ -0,0 +1,46 @@ +package art.arcane.iris.util.project.matter.slices; + +import art.arcane.iris.spi.IrisPlatform; +import art.arcane.iris.spi.IrisPlatforms; +import org.bukkit.World; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class IrisMatterPlatformSelectionTest { + @Test + public void hybridClasspathDoesNotEnableBukkitMatterIoForModdedPlatform() { + IrisPlatform platform = mock(IrisPlatform.class); + withPlatform(platform, () -> { + assertNull(new EntityMatter().readFrom(World.class)); + assertNull(new TileMatter().readFrom(World.class)); + }); + } + + @Test + public void bukkitPlatformCapabilityEnablesBukkitMatterIo() { + IrisPlatform platform = mock(IrisPlatform.class); + when(platform.supportsMatterWorldIo()).thenReturn(true); + withPlatform(platform, () -> { + assertNotNull(new EntityMatter().readFrom(World.class)); + assertNotNull(new TileMatter().readFrom(World.class)); + }); + } + + private void withPlatform(IrisPlatform platform, Runnable test) { + IrisPlatform previous = IrisPlatforms.isBound() ? IrisPlatforms.get() : null; + IrisPlatforms.unbind(); + IrisPlatforms.bind(platform); + try { + test.run(); + } finally { + IrisPlatforms.unbind(); + if (previous != null) { + IrisPlatforms.bind(previous); + } + } + } +} diff --git a/spi/src/main/java/art/arcane/iris/spi/IrisPlatform.java b/spi/src/main/java/art/arcane/iris/spi/IrisPlatform.java index cef86964d..a928e9eaf 100644 --- a/spi/src/main/java/art/arcane/iris/spi/IrisPlatform.java +++ b/spi/src/main/java/art/arcane/iris/spi/IrisPlatform.java @@ -66,6 +66,14 @@ public interface IrisPlatform { */ PlatformBiomeWriter biomeWriter(); + default Class classifyMantleValue(Object value) { + return value.getClass(); + } + + default boolean supportsMatterWorldIo() { + return false; + } + /** * Root folder Iris owns for packs, settings and generated data. Created if missing. Never null. */