Unlatch platform block states

- Replaced the nine static final PlatformBlockState fields in IrisDimensionStackActuator, IrisTerrainNormalActuator, IrisPerfectionModifier, IrisPostModifier, IrisBiomeLayerGenerator, BiomeBuffetGenerator, ChunkDataHunkHolder and EngineMantle with BoundBlockState holders that re-resolve when a different platform binds, costing one volatile read of the cached binding, one of the bound platform and a reference compare per read
- Turned the IrisPerfectionModifier ore-hiding table into a Map of BoundBlockState so the four base states follow the bound platform instead of the first one loaded
- Kept the last resolution in BoundBlockState while nothing is bound so in-flight generation on an unbinding server reads the same states it did before, matching what B.registries already does
- Added IrisPlatforms.getOrNull so the block-state holders read the binding once without an exception on a bare JVM
- Added PlatformStateRebindTest, which fails on the old code: it binds two mock platforms in turn and asserts ChunkDataHunkHolder and EngineMantle serve the second one, and rejects any static PlatformBlockState field left on the converted classes
- Indexed the .iob write palette by key so palette lookup is a hash probe rather than a scan of the palette list, cutting the 32767-state IrisObjectIoWriteLimitsTest from 4.9s to 0.7s while writing byte-identical files
- Built the palette once per write instead of once in validateWritable and again in the writer, and shared it between the plain and progress-reporting write paths
- Added IrisObjectIoPaletteTest, which pins the SHA-256 of a 30000-block, 3011-state object written by the previous implementation and asserts the palette keeps first-seen insertion order
- Added BukkitTestServer, a single mock Bukkit server installed once per JVM under a lock, stubbing logger, name, version, plugin manager and both createBlockData overloads, and failing loudly if the install does not take
- Moved IrisRuntimeSchedulerModeRoutingTest, WorldRuntimeControlServiceTimeLockTest, PreObjectMatterTest, CNGInjectorParityTest, BukkitSpiConformanceTest, MantleWriterPreObjectJournalTest, IrisFloatingChildBiomesCarvingResolutionTest and IrisDimensionCarvingResolverParityTest onto it, so whichever class runs first no longer decides whether createBlockData or getPluginManager is stubbed
- Extracted the KeyedBlockState test double out of IrisObjectIoWriteLimitsTest so the palette test can reuse it
- Replaced the IrisServices.clear() teardowns in nine test classes with a targeted remove of the PreservationRegistry each one registers, so a teardown no longer wipes a binding it did not install
- Registered a PreservationRegistry in PackDownloaderTest and StudioSVCWorldPackPublishTest, which reach IrisData.get and openRuntime and were silently getting loaderless IrisData whenever no other class had leaked one
This commit is contained in:
Brian Neumann-Fopiano
2026-09-07 19:59:22 -04:00
parent 266fe0d71c
commit 9b9ca8de34
40 changed files with 668 additions and 481 deletions
@@ -29,7 +29,7 @@ import art.arcane.iris.engine.hydrology.cave.HydrologyCaveCell;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.common.data.BoundBlockState;
import art.arcane.iris.util.project.context.ChunkContext;
import art.arcane.iris.util.project.hunk.Hunk;
import art.arcane.iris.util.project.matter.TileWrapper;
@@ -49,8 +49,8 @@ import java.util.ArrayList;
import java.util.List;
public final class IrisDimensionStackActuator extends EngineAssignedActuator<PlatformBlockState> {
private static final PlatformBlockState AIR = B.getState("AIR");
private static final PlatformBlockState BEDROCK = B.getState("BEDROCK");
private static final BoundBlockState AIR = BoundBlockState.of("AIR");
private static final BoundBlockState BEDROCK = BoundBlockState.of("BEDROCK");
private static final List<Class<?>> REPLACED_METADATA = List.of(
TileWrapper.class,
Identifier.class,
@@ -161,7 +161,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator<Pla
);
for (int y = minimumY; y <= maximumY; y++) {
metadata.clear(localX, y, localZ);
output.setRaw(localX, y, localZ, AIR);
output.setRaw(localX, y, localZ, AIR.get());
}
}
@@ -199,7 +199,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator<Pla
for (int y = layer.renderMaxY(); y >= layer.renderMinY(); y--) {
int sourceY = y - layer.localBaseY();
if (sourceY == 0 && dimension.isBedrock()) {
writeBlock(output, metadata, localX, y, localZ, BEDROCK);
writeBlock(output, metadata, localX, y, localZ, BEDROCK.get());
continue;
}
if (sourceY > layer.normalTerrainHeight() && sourceY <= layer.fluidHeight()) {
@@ -34,6 +34,7 @@ import art.arcane.iris.engine.object.IrisOreGeneratorBounds;
import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.iris.engine.object.IrisRiverMaterialConfig;
import art.arcane.iris.engine.object.IrisSurfaceRiverBankConfig;
import art.arcane.iris.util.common.data.BoundBlockState;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.iris.util.project.context.ChunkedDataCache;
import art.arcane.iris.util.project.context.ChunkContext;
@@ -42,11 +43,10 @@ import art.arcane.iris.util.project.hunk.Hunk;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.data.B;
import lombok.Getter;
public class IrisTerrainNormalActuator extends EngineAssignedActuator<PlatformBlockState> {
private static final PlatformBlockState BEDROCK = B.getState("BEDROCK");
private static final BoundBlockState BEDROCK = BoundBlockState.of("BEDROCK");
@Getter
private final RNG rng;
@Getter
@@ -161,7 +161,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<PlatformBl
for (int i = topY; i >= 0; i--) {
if (i == 0 && bedrockEnabled) {
h.setRaw(xf, i, zf, BEDROCK);
h.setRaw(xf, i, zf, BEDROCK.get());
lastBedrock = i;
continue;
}
@@ -256,7 +256,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<PlatformBl
for (int y = chunkHeight - 1; y >= upperSurfaceY; y--) {
if (y == chunkHeight - 1 && bedrockEnabled) {
h.setRaw(xf, y, zf, BEDROCK);
h.setRaw(xf, y, zf, BEDROCK.get());
continue;
}
if (y == upperSurfaceY && upperMappedSurface != null) {
@@ -413,7 +413,7 @@ final class DecoratorCore {
if (mantle != null) {
rs = mantle.getMantle().get(x + f.getModX(), yy, z + f.getModZ(), PlatformBlockState.class);
}
BlockData r = rs == null ? (BlockData) EngineMantle.AIR.nativeHandle() : (BlockData) rs.nativeHandle();
BlockData r = rs == null ? (BlockData) EngineMantle.AIR.get().nativeHandle() : (BlockData) rs.nativeHandle();
if (r.isFaceSturdy(f.getOppositeFace(), BlockSupport.FULL)) {
if (data.getAllowedFaces().contains(f)) {
found = true;
@@ -34,9 +34,9 @@ import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.hydrology.cave.HydrologyCaveCell;
import art.arcane.iris.engine.hydrology.cave.HydrologyCaveStorage;
import art.arcane.iris.engine.object.IrisPosition;
import art.arcane.iris.util.common.data.BoundBlockState;
import art.arcane.iris.util.project.matter.PreObjectMatterCell;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.iris.util.common.data.B;
import art.arcane.volmlib.util.documentation.BlockCoordinates;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.iris.util.project.hunk.Hunk;
@@ -61,7 +61,7 @@ import java.util.OptionalInt;
import java.util.concurrent.TimeUnit;
public interface EngineMantle extends MatterGenerator {
PlatformBlockState AIR = B.getState("AIR");
BoundBlockState AIR = BoundBlockState.of("AIR");
Mantle<Matter> getMantle();
@@ -161,7 +161,7 @@ public interface EngineMantle extends MatterGenerator {
return natural;
}
}
return block == null ? AIR : block;
return block == null ? AIR.get() : block;
}
default boolean isPreventingDecay() {
@@ -487,7 +487,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable {
return resolvedBlock(resolved.get(), y);
}
PlatformBlockState block = getPrerequisiteDataIfPresent(x, y, z, PlatformBlockState.class);
return block == null ? AIR : block;
return block == null ? AIR.get() : block;
}
public boolean isPrerequisiteCarved(int x, int y, int z) {
@@ -856,7 +856,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable {
}
}
if (block == null)
return AIR;
return AIR.get();
return block;
}
@@ -126,7 +126,7 @@ final class ObjectDestinationTransaction implements ObjectPassPlacer {
DataKey key = new DataKey(x, y, z, PlatformBlockState.class);
Object value = overlay.get(key);
if (value == CLEARED) {
return EngineMantle.AIR;
return EngineMantle.AIR.get();
}
return value instanceof PlatformBlockState state
? state
@@ -55,8 +55,6 @@ import java.util.Optional;
import java.util.IdentityHashMap;
import static art.arcane.iris.engine.mantle.EngineMantle.AIR;
public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<PlatformBlockState> {
public static final long FLOATING_BASE_SEED_SALT = 0x5EED_F107_00F1B10CL;
private static final Runnable NOOP_DECORATION_MISS = () -> {
@@ -22,6 +22,7 @@ import art.arcane.iris.engine.decorator.IrisSpeleothems;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.EngineAssignedModifier;
import art.arcane.iris.engine.object.IrisProceduralBlocks;
import art.arcane.iris.util.common.data.BoundBlockState;
import art.arcane.iris.util.project.context.ChunkContext;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.project.hunk.Hunk;
@@ -36,22 +37,20 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class IrisPerfectionModifier extends EngineAssignedModifier<PlatformBlockState> {
private static final class States {
private static final PlatformBlockState AIR = B.getState("AIR");
private static final PlatformBlockState WATER = B.getState("WATER");
private static final Map<String, PlatformBlockState> ORE_BASES = buildOreBases();
}
private static final BoundBlockState AIR = BoundBlockState.of("AIR");
private static final BoundBlockState WATER = BoundBlockState.of("WATER");
private static final Map<String, BoundBlockState> ORE_BASES = buildOreBases();
public IrisPerfectionModifier(Engine engine) {
super(engine, "Perfection");
}
private static Map<String, PlatformBlockState> buildOreBases() {
Map<String, PlatformBlockState> map = new HashMap<>();
PlatformBlockState stone = B.getState("STONE");
PlatformBlockState deepslate = B.getState("DEEPSLATE");
PlatformBlockState netherrack = B.getState("NETHERRACK");
PlatformBlockState blackstone = B.getState("BLACKSTONE");
private static Map<String, BoundBlockState> buildOreBases() {
Map<String, BoundBlockState> map = new HashMap<>();
BoundBlockState stone = BoundBlockState.of("STONE");
BoundBlockState deepslate = BoundBlockState.of("DEEPSLATE");
BoundBlockState netherrack = BoundBlockState.of("NETHERRACK");
BoundBlockState blackstone = BoundBlockState.of("BLACKSTONE");
map.put("minecraft:coal_ore", stone);
map.put("minecraft:copper_ore", stone);
map.put("minecraft:iron_ore", stone);
@@ -106,7 +105,7 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<PlatformBlock
for (int k = top; k >= 0; k--) {
PlatformBlockState b = output.get(finalI, k, j);
if (IrisSpeleothems.isSpike(b)) {
b = normalizeSpike(b, output, finalI, j, k, States.AIR, States.WATER);
b = normalizeSpike(b, output, finalI, j, k, AIR.get(), WATER.get());
}
boolean now = b != null && !(B.isAir(b) || B.isFluid(b));
@@ -148,10 +147,10 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<PlatformBlock
if (remove) {
changed.set(true);
output.set(finalI, k, j, States.AIR);
output.set(finalI, k, j, AIR.get());
if (remove2) {
output.set(finalI, k - 1, j, States.AIR);
output.set(finalI, k - 1, j, AIR.get());
}
}
}
@@ -211,9 +210,9 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<PlatformBlock
if (block == null) {
continue;
}
PlatformBlockState base = States.ORE_BASES.get(baseKey(block));
BoundBlockState base = ORE_BASES.get(baseKey(block));
if (base != null) {
output.set(finalI, k, j, base);
output.set(finalI, k, j, base.get());
}
}
}
@@ -25,6 +25,7 @@ import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.object.IrisProceduralBlocks;
import art.arcane.iris.engine.object.IrisSlopeClip;
import art.arcane.iris.util.common.data.BoundBlockState;
import art.arcane.iris.util.project.context.ChunkContext;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.project.hunk.Hunk;
@@ -34,9 +35,7 @@ import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import art.arcane.iris.spi.PlatformBlockState;
public class IrisPostModifier extends EngineAssignedModifier<PlatformBlockState> {
private static final class States {
private static final PlatformBlockState AIR = B.getState("AIR");
}
private static final BoundBlockState AIR = BoundBlockState.of("AIR");
private final RNG rng;
@@ -136,7 +135,7 @@ public class IrisPostModifier extends EngineAssignedModifier<PlatformBlockState>
g += hd < h - 1 ? 1 : 0;
if (!river && g == 4 && isAir(x, h - 1, z, originX, originZ, currentData)) {
setPostBlock(x, h, z, States.AIR, originX, originZ, currentData);
setPostBlock(x, h, z, AIR.get(), originX, originZ, currentData);
for (int i = h - 1; i > 0; i--) {
if (!isAir(x, i, z, originX, originZ, currentData)) {
@@ -296,7 +295,7 @@ public class IrisPostModifier extends EngineAssignedModifier<PlatformBlockState>
PlatformBlockState onto = getPostBlock(x, h, z, originX, originZ, currentData);
if (!B.canPlaceOnto(b, onto) && !B.isDecorant(b)) {
setPostBlock(x, h + 1, z, States.AIR, originX, originZ, currentData);
setPostBlock(x, h + 1, z, AIR.get(), originX, originZ, currentData);
}
}
}
@@ -366,6 +365,6 @@ public class IrisPostModifier extends EngineAssignedModifier<PlatformBlockState>
public PlatformBlockState getPostBlock(int x, int y, int z, int originX, int originZ, Hunk<PlatformBlockState> h) {
PlatformBlockState b = h.getClosest(x - originX, y, z - originZ);
return b == null ? States.AIR : b;
return b == null ? AIR.get() : b;
}
}
@@ -25,6 +25,7 @@ import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.common.data.BoundBlockState;
import art.arcane.iris.util.project.noise.CNG;
import art.arcane.iris.util.project.stream.ProceduralStream;
import art.arcane.volmlib.util.collection.KList;
@@ -35,9 +36,7 @@ import art.arcane.volmlib.util.math.RNG;
* JSON, so its fields stay put and only the behavior lives here.
*/
final class IrisBiomeLayerGenerator {
private static final class States {
private static final PlatformBlockState BARRIER = B.getState("BARRIER");
}
private static final BoundBlockState BARRIER = BoundBlockState.of("BARRIER");
private IrisBiomeLayerGenerator() {
}
@@ -96,7 +95,7 @@ final class IrisBiomeLayerGenerator {
if (dim.isExplodeBiomePalettes()) {
for (int j = 0; j < dim.getExplodeBiomePaletteSize(); j++) {
data.add(States.BARRIER);
data.add(BARRIER.get());
if (data.size() >= maxDepth) {
break;
@@ -143,7 +142,7 @@ final class IrisBiomeLayerGenerator {
if (dim.isExplodeBiomePalettes()) {
for (int j = 0; j < dim.getExplodeBiomePaletteSize(); j++) {
data.add(States.BARRIER);
data.add(BARRIER.get());
if (data.size() >= maxDepth) {
break;
@@ -41,6 +41,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -264,10 +265,7 @@ public final class IrisObjectIO {
* write path now rejects them with a descriptive error before any byte is written.
*/
static void validateWritable(IrisObject self) throws IOException {
KList<String> palette = new KList<>();
for (PlatformBlockState i : self.blocks.values()) {
palette.addIfMissing(i.key());
}
Palette palette = buildPalette(self);
if (palette.size() > MAX_PALETTE_ENTRIES) {
throw new IOException("Object '" + self.getLoadKey() + "' has " + palette.size()
+ " distinct block states; the .iob format supports at most " + MAX_PALETTE_ENTRIES + ".");
@@ -280,6 +278,14 @@ public final class IrisObjectIO {
}
}
private static Palette buildPalette(IrisObject self) {
Palette palette = new Palette();
for (PlatformBlockState i : self.blocks.values()) {
palette.add(i.key());
}
return palette;
}
private static void requireShortCoordinates(IrisObject self, String kind, IrisBlockVector position) throws IOException {
requireShort(self, kind, "x", position.getBlockX(), position);
requireShort(self, kind, "y", position.getBlockY(), position);
@@ -305,15 +311,11 @@ public final class IrisObjectIO {
dos.writeInt(self.h);
dos.writeInt(self.d);
dos.writeUTF("Iris V2 IOB;");
KList<String> palette = new KList<>();
for (PlatformBlockState i : self.blocks.values()) {
palette.addIfMissing(i.key());
}
Palette palette = buildPalette(self);
dos.writeShort(palette.size());
for (String i : palette) {
for (String i : palette.keys()) {
dos.writeUTF(i);
}
@@ -363,17 +365,13 @@ public final class IrisObjectIO {
dos.writeInt(self.d);
dos.writeUTF("Iris V2 IOB;");
KList<String> palette = new KList<>();
for (PlatformBlockState i : self.blocks.values()) {
palette.addIfMissing(i.key());
++c;
}
Palette palette = buildPalette(self);
c += self.blocks.size();
total -= self.blocks.size() - palette.size();
dos.writeShort(palette.size());
for (String i : palette) {
for (String i : palette.keys()) {
dos.writeUTF(i);
++c;
}
@@ -458,4 +456,29 @@ public final class IrisObjectIO {
super("Invalid Header");
}
}
private static final class Palette {
private final KList<String> keys = new KList<>();
private final Map<String, Integer> index = new HashMap<>();
private void add(String key) {
index.computeIfAbsent(key, k -> {
keys.add(k);
return keys.size() - 1;
});
}
private int indexOf(String key) {
Integer found = index.get(key);
return found == null ? -1 : found;
}
private int size() {
return keys.size();
}
private KList<String> keys() {
return keys;
}
}
}
@@ -26,13 +26,13 @@ import art.arcane.iris.engine.framework.WrongEngineBroException;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.platform.studio.EnginedStudioGenerator;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.common.data.BoundBlockState;
import art.arcane.iris.util.project.context.IrisContext;
import java.util.Objects;
public class BiomeBuffetGenerator extends EnginedStudioGenerator {
private static final PlatformBlockState FLOOR = B.getState("BARRIER");
private static final BoundBlockState FLOOR = BoundBlockState.of("BARRIER");
private final IrisBiome[] biomes;
private final int width;
private final int biomeSize;
@@ -66,7 +66,7 @@ public class BiomeBuffetGenerator extends EnginedStudioGenerator {
if (biome == null) {
try (GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_biome_buffet_stage");
IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
tc.setRegion(0, 0, 0, 16, 1, 16, FLOOR);
tc.setRegion(0, 0, 0, 16, 1, 16, FLOOR.get());
}
return;
}
@@ -0,0 +1,53 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.util.common.data;
import art.arcane.iris.spi.IrisPlatform;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.PlatformBlockState;
public final class BoundBlockState {
private final String key;
private volatile Bound bound;
private BoundBlockState(String key) {
this.key = key;
}
public static BoundBlockState of(String key) {
return new BoundBlockState(key);
}
public PlatformBlockState get() {
Bound current = bound;
IrisPlatform platform = IrisPlatforms.getOrNull();
if (current != null && (platform == null || current.platform() == platform)) {
return current.state();
}
IrisPlatform resolvedPlatform = IrisPlatforms.get();
Bound resolved = new Bound(resolvedPlatform, B.getState(key));
bound = resolved;
return resolved.state();
}
private record Bound(IrisPlatform platform, PlatformBlockState state) {
}
}
@@ -20,7 +20,7 @@ package art.arcane.iris.util.project.hunk.view;
import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.common.data.BoundBlockState;
import art.arcane.iris.util.common.data.IrisCustomData;
import art.arcane.iris.util.project.hunk.storage.AtomicHunk;
import org.bukkit.block.data.BlockData;
@@ -28,9 +28,7 @@ import org.bukkit.generator.ChunkGenerator.ChunkData;
@SuppressWarnings("ClassCanBeRecord")
public class ChunkDataHunkHolder extends AtomicHunk<PlatformBlockState> {
private static final class States {
private static final PlatformBlockState AIR = B.getState("AIR");
}
private static final BoundBlockState AIR = BoundBlockState.of("AIR");
private final ChunkData chunk;
@@ -63,12 +61,12 @@ public class ChunkDataHunkHolder extends AtomicHunk<PlatformBlockState> {
@Override
public PlatformBlockState getRaw(int x, int y, int z) {
if (y < 0 || y >= getHeight()) {
return States.AIR;
return AIR.get();
}
PlatformBlockState b = super.getRaw(x, y, z);
return b != null ? b : States.AIR;
return b != null ? b : AIR.get();
}
public PlatformBlockState getStoredRaw(int x, int y, int z) {
@@ -1,22 +1,11 @@
package art.arcane.iris.core;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.block.data.BlockData;
import art.arcane.iris.testsupport.BukkitTestServer;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import java.util.Locale;
import java.util.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockingDetails;
public class IrisRuntimeSchedulerModeRoutingTest {
@@ -65,30 +54,7 @@ public class IrisRuntimeSchedulerModeRoutingTest {
}
private void installServer(String name, String version) {
Server server = Bukkit.getServer();
if (server == null) {
server = mock(Server.class);
try {
Bukkit.setServer(server);
} catch (Throwable ignored) {
server = Bukkit.getServer();
}
}
assumeTrue(server != null && mockingDetails(server).isMock());
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
doReturn(name).when(server).getName();
doReturn(version).when(server).getVersion();
doReturn(version).when(server).getBukkitVersion();
doAnswer((InvocationOnMock invocation) -> namedBlockData(invocation.getArgument(0, Material.class).name().toLowerCase(Locale.ROOT))).when(server).createBlockData(any(Material.class));
doAnswer((InvocationOnMock invocation) -> namedBlockData(invocation.getArgument(0, String.class))).when(server).createBlockData(anyString());
}
private static BlockData namedBlockData(String key) {
String canonical = key.indexOf(':') >= 0 ? key : "minecraft:" + key;
BlockData data = mock(BlockData.class);
doReturn(canonical).when(data).getAsString();
return data;
assumeTrue(mockingDetails(BukkitTestServer.install()).isMock());
BukkitTestServer.brand(name, version);
}
}
@@ -44,7 +44,7 @@ public class IrisDataAuthoringCacheTest {
if (epoch != null) {
epoch.close();
}
IrisServices.clear();
IrisServices.remove(PreservationRegistry.class);
IrisSettings.settings = previousSettings;
}
@@ -36,7 +36,7 @@ public class IrisDataDatapackCompilerCacheTest {
if (runtimeData != null) {
runtimeData.close();
}
IrisServices.clear();
IrisServices.remove(PreservationRegistry.class);
}
@Test
@@ -36,7 +36,7 @@ public class IrisDataEngineRegistryTest {
if (data != null) {
data.close();
}
IrisServices.clear();
IrisServices.remove(PreservationRegistry.class);
}
@Test
@@ -37,7 +37,7 @@ public class IrisDataRetainedPackTest {
if (retained != null) {
retained.close();
}
IrisServices.clear();
IrisServices.remove(PreservationRegistry.class);
}
@Test
@@ -20,10 +20,12 @@ package art.arcane.iris.core.pack;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.framework.PreservationRegistry;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.object.IrisEnvironment;
import art.arcane.iris.spi.IrisPlatform;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.IrisServices;
import art.arcane.iris.spi.PlatformStructureHooks;
import org.junit.Assume;
import org.junit.After;
@@ -92,6 +94,7 @@ public class PackDownloaderTest {
when(structureHooks.templatePoolKeys()).thenReturn(List.of("minecraft:empty"));
IrisPlatforms.bind(platform);
IrisSettings.settings = new IrisSettings();
IrisServices.register(PreservationRegistry.class, mock(PreservationRegistry.class));
}
@After
@@ -102,6 +105,7 @@ public class PackDownloaderTest {
}
IrisSettings.settings = previousSettings;
PackValidationRegistry.clear();
IrisServices.remove(PreservationRegistry.class);
}
@Test
@@ -39,7 +39,7 @@ public class PregenCacheExecutorRegistrationTest {
@After
public void clearServices() {
IrisServices.clear();
IrisServices.remove(PreservationRegistry.class);
}
@Test
@@ -61,7 +61,7 @@ public class IrisCodeWorkspaceTest {
data.close();
data = null;
}
IrisServices.clear();
IrisServices.remove(PreservationRegistry.class);
IrisPlatforms.unbind();
if (previousPlatform != null) {
IrisPlatforms.bind(previousPlatform);
@@ -2,10 +2,9 @@ package art.arcane.iris.core.runtime;
import art.arcane.iris.core.lifecycle.CapabilitySnapshot;
import art.arcane.iris.core.lifecycle.ServerFamily;
import art.arcane.iris.testsupport.BukkitTestServer;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.plugin.PluginManager;
import org.junit.Before;
import org.junit.Test;
@@ -14,28 +13,14 @@ import java.lang.reflect.Proxy;
import java.util.OptionalLong;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
public class WorldRuntimeControlServiceTimeLockTest {
@Before
public void ensureBukkitServer() {
if (Bukkit.getServer() != null) {
return;
}
Server server = mock(Server.class);
PluginManager pluginManager = mock(PluginManager.class);
doReturn(pluginManager).when(server).getPluginManager();
doReturn(Logger.getLogger("WorldRuntimeControlServiceTimeLockTest")).when(server).getLogger();
try {
Bukkit.setServer(server);
} catch (Throwable ignored) {
}
BukkitTestServer.install();
}
@Test
@@ -6,8 +6,11 @@ import art.arcane.iris.core.pack.AtomicDirectoryPublisher;
import art.arcane.iris.core.pack.BrokenPackException;
import art.arcane.iris.core.pack.PackValidationRegistry;
import art.arcane.iris.core.pack.PackValidationResult;
import art.arcane.iris.engine.framework.PreservationRegistry;
import art.arcane.iris.spi.IrisServices;
import org.junit.Assume;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -26,14 +29,21 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
public class StudioSVCWorldPackPublishTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Before
public void registerPreservationService() {
IrisServices.register(PreservationRegistry.class, mock(PreservationRegistry.class));
}
@After
public void clearValidationRegistry() {
PackValidationRegistry.clear();
IrisServices.remove(PreservationRegistry.class);
}
@Test
@@ -37,7 +37,7 @@ public class IrisDataStructureRecoveryTest {
if (data != null) {
data.close();
}
IrisServices.clear();
IrisServices.remove(PreservationRegistry.class);
}
@Test
@@ -66,7 +66,7 @@ public class SavedBiomeRuntimeTest {
@After
public void restoreServices() {
IrisServices.clear();
IrisServices.remove(PreservationRegistry.class);
IrisSettings.settings = previousSettings;
}
@@ -6,6 +6,7 @@ import art.arcane.iris.spi.IrisPlatform;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.spi.PlatformRegistries;
import art.arcane.iris.testsupport.BukkitTestServer;
import art.arcane.iris.util.project.matter.IrisMatterSupport;
import art.arcane.iris.util.project.matter.PreObjectMatterCell;
import art.arcane.volmlib.util.mantle.runtime.Mantle;
@@ -14,10 +15,6 @@ import art.arcane.volmlib.util.matter.IrisMatter;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.MatterCavern;
import art.arcane.volmlib.util.matter.MatterSlice;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.block.data.BlockData;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -28,8 +25,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.Locale;
import java.util.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -38,9 +33,6 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -54,20 +46,7 @@ public class MantleWriterPreObjectJournalTest {
@BeforeClass
public static void setUpBukkit() {
if (Bukkit.getServer() != null) {
return;
}
Server server = mock(Server.class);
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
doReturn("IrisTestServer").when(server).getName();
doReturn("1.0").when(server).getVersion();
doReturn("1.0").when(server).getBukkitVersion();
doAnswer(invocation -> blockData(invocation.getArgument(0, Material.class).name()
.toLowerCase(Locale.ROOT))).when(server).createBlockData(any(Material.class));
try {
Bukkit.setServer(server);
} catch (Throwable ignored) {
}
BukkitTestServer.install();
}
@Before
@@ -230,9 +209,4 @@ public class MantleWriterPreObjectJournalTest {
}
}
private static BlockData blockData(String key) {
BlockData data = mock(BlockData.class);
doReturn("minecraft:" + key).when(data).getAsString();
return data;
}
}
@@ -69,7 +69,7 @@ final class CompatTestPlatform {
}
void unbind() {
IrisServices.clear();
IrisServices.remove(PreservationRegistry.class);
IrisPlatforms.unbind();
if (previous != null) {
IrisPlatforms.bind(previous);
@@ -4,12 +4,9 @@ import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.loader.ResourceLoader;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.SeedManager;
import art.arcane.iris.testsupport.BukkitTestServer;
import art.arcane.iris.util.project.noise.CNG;
import art.arcane.volmlib.util.collection.KList;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.block.data.BlockData;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
@@ -19,7 +16,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
@@ -27,7 +23,6 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -38,7 +33,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
@@ -53,28 +47,7 @@ public class IrisDimensionCarvingResolverParityTest {
@BeforeClass
public static void setupBukkit() {
if (Bukkit.getServer() != null) {
return;
}
Server server = mock(Server.class);
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
doReturn("IrisTestServer").when(server).getName();
doReturn("1.0").when(server).getVersion();
doReturn("1.0").when(server).getBukkitVersion();
doAnswer((InvocationOnMock invocation) -> namedBlockData(invocation.getArgument(0, Material.class).name().toLowerCase(Locale.ROOT))).when(server).createBlockData(any(Material.class));
doAnswer((InvocationOnMock invocation) -> namedBlockData(invocation.getArgument(0, String.class))).when(server).createBlockData(anyString());
try {
Bukkit.setServer(server);
} catch (Throwable ignored) {
}
}
private static BlockData namedBlockData(String key) {
String canonical = key.indexOf(':') >= 0 ? key : "minecraft:" + key;
BlockData data = mock(BlockData.class);
doReturn(canonical).when(data).getAsString();
return data;
BukkitTestServer.install();
}
@Test
@@ -3,52 +3,22 @@ package art.arcane.iris.engine.object;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.loader.ResourceLoader;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.testsupport.BukkitTestServer;
import art.arcane.volmlib.util.collection.KList;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.block.data.BlockData;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Logger;
import static org.junit.Assert.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
public class IrisFloatingChildBiomesCarvingResolutionTest {
@BeforeClass
public static void setupBukkit() {
if (Bukkit.getServer() != null) {
return;
}
Server server = mock(Server.class);
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
doReturn("IrisTestServer").when(server).getName();
doReturn("1.0").when(server).getVersion();
doReturn("1.0").when(server).getBukkitVersion();
doAnswer((InvocationOnMock invocation) -> namedBlockData(invocation.getArgument(0, Material.class).name().toLowerCase(Locale.ROOT))).when(server).createBlockData(any(Material.class));
doAnswer((InvocationOnMock invocation) -> namedBlockData(invocation.getArgument(0, String.class))).when(server).createBlockData(anyString());
try {
Bukkit.setServer(server);
} catch (Throwable ignored) {
}
}
private static BlockData namedBlockData(String key) {
String canonical = key.indexOf(':') >= 0 ? key : "minecraft:" + key;
BlockData data = mock(BlockData.class);
doReturn(canonical).when(data).getAsString();
return data;
BukkitTestServer.install();
}
@Test
@@ -0,0 +1,102 @@
package art.arcane.iris.engine.object;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.math.IrisBlockVector;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HexFormat;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.junit.Assert.assertEquals;
public class IrisObjectIoPaletteTest {
private static final int BLOCK_COUNT = 30_000;
private static final int UNIQUE_KEYS = 3_000;
private static final String PINNED_DIGEST = "9fbd42fe7686cc725e0acab8595b0223d71c1a45762b78ac0e6146230dbb8849";
@Test
public void writesTheSameBytesAsTheOrderedPaletteScan() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
IrisObjectIO.write(representativeObject(), out);
assertEquals(PINNED_DIGEST, digest(out.toByteArray()));
}
@Test
public void paletteKeepsFirstSeenInsertionOrder() throws IOException {
IrisObject object = representativeObject();
List<String> firstSeen = new ArrayList<>();
Set<String> seen = new HashSet<>();
for (Map.Entry<IrisBlockVector, PlatformBlockState> entry : object.blocks) {
if (seen.add(entry.getValue().key())) {
firstSeen.add(entry.getValue().key());
}
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
IrisObjectIO.write(object, out);
assertEquals(firstSeen, writtenPalette(out.toByteArray()));
}
private static List<String> writtenPalette(byte[] bytes) throws IOException {
DataInputStream din = new DataInputStream(new ByteArrayInputStream(bytes));
din.readInt();
din.readInt();
din.readInt();
din.readUTF();
int count = din.readShort();
List<String> palette = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
palette.add(din.readUTF());
}
return palette;
}
private static IrisObject representativeObject() {
IrisObject object = new IrisObject(64, 64, 64);
object.setLoadKey("palette-order");
int placed = 0;
int distinct = 0;
outer:
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 64; y++) {
for (int z = 0; z < 64; z++) {
if (placed >= BLOCK_COUNT) {
break outer;
}
String key;
if (placed % 7 == 0 && distinct < UNIQUE_KEYS) {
key = "iris:unique_" + distinct;
distinct++;
} else {
key = "iris:shared_" + (placed % 11);
}
object.blocks.put(new IrisBlockVector(x, y, z), new KeyedBlockState(key));
placed++;
}
}
}
return object;
}
private static String digest(byte[] bytes) {
try {
return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(bytes));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
}
}
@@ -106,172 +106,4 @@ public class IrisObjectIoWriteLimitsTest {
assertArrayEquals("a rejected write must not truncate the previous object", before,
Files.readAllBytes(file.toPath()));
}
private static final class KeyedBlockState implements PlatformBlockState {
private final String key;
private KeyedBlockState(String key) {
this.key = key;
}
@Override
public String key() {
return key;
}
@Override
public String namespace() {
return null;
}
@Override
public String materialKey() {
return null;
}
@Override
public boolean isAir() {
return false;
}
@Override
public boolean isSolid() {
return false;
}
@Override
public boolean isOccluding() {
return false;
}
@Override
public boolean isCustom() {
return false;
}
@Override
public String deferredPlacementKey() {
return null;
}
@Override
public PlatformBlockState placementBaseState() {
return null;
}
@Override
public boolean isFluid() {
return false;
}
@Override
public boolean isWater() {
return false;
}
@Override
public boolean isWaterLogged() {
return false;
}
@Override
public boolean isLit() {
return false;
}
@Override
public boolean isUpdatable() {
return false;
}
@Override
public boolean isFoliage() {
return false;
}
@Override
public boolean isTreeBlock() {
return false;
}
@Override
public boolean isFoliagePlantable() {
return false;
}
@Override
public boolean isDecorant() {
return false;
}
@Override
public boolean isStorage() {
return false;
}
@Override
public boolean isStorageChest() {
return false;
}
@Override
public boolean isOre() {
return false;
}
@Override
public boolean isDeepSlate() {
return false;
}
@Override
public boolean isVineBlock() {
return false;
}
@Override
public boolean canPlaceOnto(PlatformBlockState onto) {
return false;
}
@Override
public boolean matches(PlatformBlockState state) {
return false;
}
@Override
public boolean isAirOrFluid() {
return false;
}
@Override
public boolean hasTileEntity() {
return false;
}
@Override
public PlatformBlockState withProperty(String name, String value) {
return null;
}
@Override
public Object nativeHandle() {
return null;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public boolean equals(Object other) {
return this == other;
}
@Override
public String toString() {
return key;
}
}
}
@@ -0,0 +1,171 @@
package art.arcane.iris.engine.object;
import art.arcane.iris.spi.PlatformBlockState;
final class KeyedBlockState implements PlatformBlockState {
private final String key;
KeyedBlockState(String key) {
this.key = key;
}
@Override
public String key() {
return key;
}
@Override
public String namespace() {
return null;
}
@Override
public String materialKey() {
return null;
}
@Override
public boolean isAir() {
return false;
}
@Override
public boolean isSolid() {
return false;
}
@Override
public boolean isOccluding() {
return false;
}
@Override
public boolean isCustom() {
return false;
}
@Override
public String deferredPlacementKey() {
return null;
}
@Override
public PlatformBlockState placementBaseState() {
return null;
}
@Override
public boolean isFluid() {
return false;
}
@Override
public boolean isWater() {
return false;
}
@Override
public boolean isWaterLogged() {
return false;
}
@Override
public boolean isLit() {
return false;
}
@Override
public boolean isUpdatable() {
return false;
}
@Override
public boolean isFoliage() {
return false;
}
@Override
public boolean isTreeBlock() {
return false;
}
@Override
public boolean isFoliagePlantable() {
return false;
}
@Override
public boolean isDecorant() {
return false;
}
@Override
public boolean isStorage() {
return false;
}
@Override
public boolean isStorageChest() {
return false;
}
@Override
public boolean isOre() {
return false;
}
@Override
public boolean isDeepSlate() {
return false;
}
@Override
public boolean isVineBlock() {
return false;
}
@Override
public boolean canPlaceOnto(PlatformBlockState onto) {
return false;
}
@Override
public boolean matches(PlatformBlockState state) {
return false;
}
@Override
public boolean isAirOrFluid() {
return false;
}
@Override
public boolean hasTileEntity() {
return false;
}
@Override
public PlatformBlockState withProperty(String name, String value) {
return null;
}
@Override
public Object nativeHandle() {
return null;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public boolean equals(Object other) {
return this == other;
}
@Override
public String toString() {
return key;
}
}
@@ -24,12 +24,11 @@ import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.PlatformBlockProperty;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.spi.PlatformRegistries;
import art.arcane.iris.testsupport.BukkitTestServer;
import art.arcane.iris.util.common.data.IrisCustomData;
import art.arcane.iris.util.project.matter.slices.PlatformBlockMatter;
import io.papermc.paper.registry.RegistryAccess;
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;
@@ -38,7 +37,6 @@ import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -46,10 +44,8 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import java.util.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -57,29 +53,12 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
public class BukkitSpiConformanceTest {
@BeforeClass
public static void setup() {
Server server = Bukkit.getServer();
if (server == null) {
server = mock(Server.class);
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
doReturn("IrisTestServer").when(server).getName();
doReturn("1.0").when(server).getVersion();
doReturn("1.0").when(server).getBukkitVersion();
doAnswer((InvocationOnMock invocation) -> blockData("minecraft:" + invocation.getArgument(0, Material.class).name().toLowerCase(Locale.ROOT))).when(server).createBlockData(any(Material.class));
try {
Bukkit.setServer(server);
} catch (Throwable ignored) {
}
}
doAnswer((InvocationOnMock invocation) -> blockData(invocation.getArgument(0))).when(server).createBlockData(anyString());
BukkitTestServer.install();
}
@Before
@@ -93,10 +72,7 @@ public class BukkitSpiConformanceTest {
}
private static BlockData blockData(String asString) {
BlockData data = mock(BlockData.class);
doReturn(asString).when(data).getAsString();
doReturn(asString).when(data).getAsString(anyBoolean());
return data;
return BukkitTestServer.blockData(asString);
}
private static PlatformRegistries registries() {
@@ -0,0 +1,84 @@
package art.arcane.iris.testsupport;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.block.data.BlockData;
import org.bukkit.plugin.PluginManager;
import org.mockito.invocation.InvocationOnMock;
import java.util.Locale;
import java.util.logging.Logger;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
public final class BukkitTestServer {
private static final Object LOCK = new Object();
private static volatile Server installed;
private BukkitTestServer() {
}
public static Server install() {
Server current = installed;
if (current != null) {
return current;
}
synchronized (LOCK) {
if (installed != null) {
return installed;
}
Server existing = Bukkit.getServer();
if (existing != null) {
installed = existing;
return existing;
}
Server server = mock(Server.class);
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
doReturn(mock(PluginManager.class)).when(server).getPluginManager();
doReturn("IrisTestServer").when(server).getName();
doReturn("1.0").when(server).getVersion();
doReturn("1.0").when(server).getBukkitVersion();
doAnswer((InvocationOnMock invocation) -> blockData(invocation.getArgument(0, Material.class).name().toLowerCase(Locale.ROOT)))
.when(server).createBlockData(any(Material.class));
doAnswer((InvocationOnMock invocation) -> blockData(invocation.getArgument(0, String.class)))
.when(server).createBlockData(anyString());
try {
Bukkit.setServer(server);
} catch (Throwable e) {
if (Bukkit.getServer() != server) {
throw new IllegalStateException("The shared Bukkit test server could not be installed", e);
}
}
installed = server;
return server;
}
}
public static void brand(String name, String version) {
Server server = install();
doReturn(name).when(server).getName();
doReturn(version).when(server).getVersion();
doReturn(version).when(server).getBukkitVersion();
}
public static BlockData blockData(String key) {
String canonical = key.indexOf(':') >= 0 ? key : "minecraft:" + key;
BlockData data = mock(BlockData.class);
doReturn(canonical).when(data).getAsString();
doReturn(canonical).when(data).getAsString(anyBoolean());
return data;
}
}
@@ -0,0 +1,125 @@
package art.arcane.iris.util.common.data;
import art.arcane.iris.engine.mantle.EngineMantle;
import art.arcane.iris.spi.IrisPlatform;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.spi.PlatformRegistries;
import art.arcane.iris.util.project.hunk.view.ChunkDataHunkHolder;
import org.bukkit.generator.ChunkGenerator;
import org.junit.After;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class PlatformStateRebindTest {
private static final List<String> CONVERTED = List.of(
"art.arcane.iris.engine.actuator.IrisDimensionStackActuator",
"art.arcane.iris.engine.actuator.IrisTerrainNormalActuator",
"art.arcane.iris.engine.modifier.IrisPerfectionModifier",
"art.arcane.iris.engine.modifier.IrisPostModifier",
"art.arcane.iris.engine.object.IrisBiomeLayerGenerator",
"art.arcane.iris.engine.platform.studio.generators.BiomeBuffetGenerator",
"art.arcane.iris.engine.mantle.EngineMantle",
"art.arcane.iris.util.project.hunk.view.ChunkDataHunkHolder");
@After
public void unbindPlatform() {
IrisPlatforms.unbind();
}
@Test
public void chunkDataAirIsResolvedAgainstTheCurrentlyBoundPlatform() {
PlatformBlockState first = bindPlatform("first");
ChunkGenerator.ChunkData chunk = mock(ChunkGenerator.ChunkData.class);
when(chunk.getMinHeight()).thenReturn(-64);
when(chunk.getMaxHeight()).thenReturn(320);
ChunkDataHunkHolder holder = new ChunkDataHunkHolder(chunk);
assertSame(first, holder.getRaw(0, -1, 0));
IrisPlatforms.unbind();
PlatformBlockState second = bindPlatform("second");
assertSame(second, holder.getRaw(0, -1, 0));
}
@Test
public void mantleAirIsResolvedAgainstTheCurrentlyBoundPlatform() {
PlatformBlockState first = bindPlatform("first");
assertSame(first, EngineMantle.AIR.get());
IrisPlatforms.unbind();
PlatformBlockState second = bindPlatform("second");
assertSame(second, EngineMantle.AIR.get());
}
@Test
public void aLatchKeepsServingTheLastResolutionWhileNothingIsBound() {
PlatformBlockState first = bindPlatform("first");
BoundBlockState state = BoundBlockState.of("AIR");
assertSame(first, state.get());
IrisPlatforms.unbind();
assertSame(first, state.get());
}
@Test
public void noGenerationClassLatchesABlockStateAtClassInitialisation() throws ClassNotFoundException {
List<String> latched = new ArrayList<>();
for (String name : CONVERTED) {
Class<?> owner = Class.forName(name, false, PlatformStateRebindTest.class.getClassLoader());
collectLatches(owner, latched);
for (Class<?> nested : owner.getDeclaredClasses()) {
collectLatches(nested, latched);
}
}
assertEquals(List.of(), latched);
}
private static void collectLatches(Class<?> type, List<String> latched) {
for (Field field : type.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
continue;
}
Class<?> fieldType = field.getType();
if (PlatformBlockState.class.isAssignableFrom(fieldType)
|| (fieldType.isArray() && PlatformBlockState.class.isAssignableFrom(fieldType.getComponentType()))) {
latched.add(type.getName() + "." + field.getName());
}
}
}
private static PlatformBlockState bindPlatform(String label) {
Map<String, PlatformBlockState> states = new HashMap<>();
PlatformRegistries registries = mock(PlatformRegistries.class);
when(registries.block(anyString())).thenAnswer(invocation -> states.computeIfAbsent(
label + ':' + invocation.<String>getArgument(0),
key -> {
PlatformBlockState state = mock(PlatformBlockState.class);
when(state.key()).thenReturn(key);
return state;
}));
IrisPlatform platform = mock(IrisPlatform.class);
when(platform.registries()).thenReturn(registries);
IrisPlatforms.bind(platform);
return registries.block("AIR");
}
}
@@ -3,16 +3,13 @@ package art.arcane.iris.util.project.matter.slices;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.engine.hydrology.cave.HydrologyCaveCell;
import art.arcane.iris.engine.hydrology.cave.HydrologyCaveAction;
import art.arcane.iris.testsupport.BukkitTestServer;
import art.arcane.iris.util.project.matter.IrisMatterSupport;
import art.arcane.iris.util.project.matter.PreObjectMatterCell;
import art.arcane.volmlib.util.matter.IrisMatter;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.MatterCavern;
import art.arcane.volmlib.util.matter.MatterSlice;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.block.data.BlockData;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -21,8 +18,6 @@ import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Locale;
import java.util.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -30,28 +25,12 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
public class PreObjectMatterTest {
@BeforeClass
public static void setUpBukkit() {
if (Bukkit.getServer() != null) {
return;
}
Server server = mock(Server.class);
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
doReturn("IrisTestServer").when(server).getName();
doReturn("1.0").when(server).getVersion();
doReturn("1.0").when(server).getBukkitVersion();
doAnswer(invocation -> blockData(invocation.getArgument(0, Material.class).name()
.toLowerCase(Locale.ROOT))).when(server).createBlockData(any(Material.class));
try {
Bukkit.setServer(server);
} catch (Throwable ignored) {
}
BukkitTestServer.install();
}
@Test
@@ -149,9 +128,4 @@ public class PreObjectMatterTest {
return new DataInputStream(new ByteArrayInputStream(new byte[]{(byte) flags}));
}
private static BlockData blockData(String key) {
BlockData data = mock(BlockData.class);
doReturn("minecraft:" + key).when(data).getAsString();
return data;
}
}
@@ -1,52 +1,20 @@
package art.arcane.iris.util.project.noise;
import art.arcane.iris.testsupport.BukkitTestServer;
import art.arcane.volmlib.util.function.NoiseInjector;
import art.arcane.volmlib.util.math.RNG;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.block.data.BlockData;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
public class CNGInjectorParityTest {
@BeforeClass
public static void setupBukkit() {
if (Bukkit.getServer() != null) {
return;
}
Server server = mock(Server.class);
doReturn(Logger.getLogger("IrisTest")).when(server).getLogger();
doReturn("IrisTestServer").when(server).getName();
doReturn("1.0").when(server).getVersion();
doReturn("1.0").when(server).getBukkitVersion();
doAnswer((InvocationOnMock invocation) -> namedBlockData(invocation.getArgument(0, Material.class).name().toLowerCase(Locale.ROOT))).when(server).createBlockData(any(Material.class));
doAnswer((InvocationOnMock invocation) -> namedBlockData(invocation.getArgument(0, String.class))).when(server).createBlockData(anyString());
try {
Bukkit.setServer(server);
} catch (Throwable ignored) {
}
}
private static BlockData namedBlockData(String key) {
String canonical = key.indexOf(':') >= 0 ? key : "minecraft:" + key;
BlockData data = mock(BlockData.class);
doReturn(canonical).when(data).getAsString();
return data;
BukkitTestServer.install();
}
@Test