(removed ai file from when i generated docs)
This commit is contained in:
Brian Neumann-Fopiano
2026-08-10 15:47:57 -04:00
parent ebfe278b3b
commit 998a5c9f5f
256 changed files with 49776 additions and 1221 deletions
@@ -0,0 +1,289 @@
package art.arcane.iris.core.nms.v26_2_R1;
import art.arcane.iris.core.datapack.DatapackStructureScopeIndex;
import net.minecraft.core.Holder;
import net.minecraft.core.Vec3i;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
final class DatapackStructureStateFilter {
private DatapackStructureStateFilter() {
}
static Selection filter(
List<Holder<StructureSet>> structureSets,
DatapackStructureScopeIndex scopeIndex,
Set<String> declaredSources
) {
Map<String, Holder<StructureSet>> holdersByKey = new HashMap<>();
for (Holder<StructureSet> holder : structureSets) {
String key = structureSetKey(holder);
if (key != null) {
holdersByKey.putIfAbsent(key, holder);
}
}
Map<Holder<StructureSet>, Holder<StructureSet>> scopedByIdentity = new IdentityHashMap<>();
Set<Holder<StructureSet>> visiting = Collections.newSetFromMap(new IdentityHashMap<>());
List<Holder<StructureSet>> filteredSets = new ArrayList<>(structureSets.size());
int retainedManagedSets = 0;
int excludedManagedSets = 0;
for (Holder<StructureSet> holder : structureSets) {
String setKey = structureSetKey(holder);
boolean managedSet = setKey != null && scopeIndex.isManagedStructureSet(setKey);
Holder<StructureSet> scopedHolder = scopeHolder(
holder,
scopeIndex,
declaredSources,
holdersByKey,
scopedByIdentity,
visiting);
if (scopedHolder == null) {
if (managedSet) {
excludedManagedSets++;
}
continue;
}
if (managedSet) {
retainedManagedSets++;
}
filteredSets.add(scopedHolder);
}
return new Selection(
List.copyOf(filteredSets),
retainedManagedSets,
excludedManagedSets);
}
static String structureSetKey(Holder<StructureSet> holder) {
Optional<ResourceKey<StructureSet>> key = holder.unwrapKey();
if (key.isPresent()) {
return key.get().identifier().toString();
}
if (holder.value().placement()
instanceof ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyedPlacement) {
return keyedPlacement.key.identifier().toString();
}
return null;
}
private static List<StructureSet.StructureSelectionEntry> filterEntries(
List<StructureSet.StructureSelectionEntry> entries,
DatapackStructureScopeIndex scopeIndex,
Set<String> declaredSources
) {
List<StructureSet.StructureSelectionEntry> filtered = new ArrayList<>(entries.size());
for (StructureSet.StructureSelectionEntry entry : entries) {
String structureKey = entry.structure().unwrapKey()
.map(key -> key.identifier().toString())
.orElse(null);
if (structureKey == null || scopeIndex.allowsStructure(structureKey, declaredSources)) {
filtered.add(entry);
}
}
return filtered;
}
private static Holder<StructureSet> scopeHolder(
Holder<StructureSet> holder,
DatapackStructureScopeIndex scopeIndex,
Set<String> declaredSources,
Map<String, Holder<StructureSet>> holdersByKey,
Map<Holder<StructureSet>, Holder<StructureSet>> scopedByIdentity,
Set<Holder<StructureSet>> visiting
) {
if (scopedByIdentity.containsKey(holder)) {
return scopedByIdentity.get(holder);
}
if (!visiting.add(holder)) {
return null;
}
try {
String setKey = structureSetKey(holder);
if (setKey != null && scopeIndex.isManagedStructureSet(setKey)
&& !scopeIndex.allowsStructureSet(setKey, declaredSources)) {
scopedByIdentity.put(holder, null);
return null;
}
StructureSet originalSet = holder.value();
List<StructureSet.StructureSelectionEntry> entries = filterEntries(
originalSet.structures(), scopeIndex, declaredSources);
if (entries.isEmpty()) {
scopedByIdentity.put(holder, null);
return null;
}
StructurePlacement placement = scopePlacement(
originalSet.placement(),
scopeIndex,
declaredSources,
holdersByKey,
scopedByIdentity,
visiting);
if (entries.size() == originalSet.structures().size()
&& placement == originalSet.placement()) {
scopedByIdentity.put(holder, holder);
return holder;
}
Holder<StructureSet> scoped = Holder.direct(new StructureSet(entries, placement));
scopedByIdentity.put(holder, scoped);
return scoped;
} finally {
visiting.remove(holder);
}
}
static Optional<StructurePlacement.ExclusionZone> exclusionZone(StructurePlacement placement) {
Object value = declaredFieldValue(StructurePlacement.class, placement, Optional.class);
if (value instanceof Optional<?> optional && (optional.isEmpty()
|| optional.get() instanceof StructurePlacement.ExclusionZone)) {
@SuppressWarnings("unchecked")
Optional<StructurePlacement.ExclusionZone> resolved =
(Optional<StructurePlacement.ExclusionZone>) optional;
return resolved;
}
throw new IllegalStateException("Could not read structure placement exclusion zone from "
+ placement.getClass().getName());
}
private static StructurePlacement scopePlacement(
StructurePlacement placement,
DatapackStructureScopeIndex scopeIndex,
Set<String> declaredSources,
Map<String, Holder<StructureSet>> holdersByKey,
Map<Holder<StructureSet>, Holder<StructureSet>> scopedByIdentity,
Set<Holder<StructureSet>> visiting
) {
Optional<StructurePlacement.ExclusionZone> currentZone = exclusionZone(placement);
if (currentZone.isEmpty()) {
return placement;
}
Holder<StructureSet> target = currentZone.get().otherSet();
String targetKey = structureSetKey(target);
if (targetKey != null) {
target = holdersByKey.getOrDefault(targetKey, target);
}
Holder<StructureSet> scopedTarget = scopeHolder(
target,
scopeIndex,
declaredSources,
holdersByKey,
scopedByIdentity,
visiting);
Optional<StructurePlacement.ExclusionZone> scopedZone = Optional.empty();
if (scopedTarget != null) {
scopedZone = Optional.of(new StructurePlacement.ExclusionZone(
scopedTarget,
currentZone.get().chunkCount()));
}
if (scopedTarget == currentZone.get().otherSet()) {
return placement;
}
return copyPlacement(placement, scopedZone);
}
private static StructurePlacement copyPlacement(
StructurePlacement placement,
Optional<StructurePlacement.ExclusionZone> exclusionZone
) {
Vec3i locateOffset = (Vec3i) declaredFieldValue(
StructurePlacement.class, placement, Vec3i.class);
StructurePlacement.FrequencyReductionMethod frequencyReductionMethod =
(StructurePlacement.FrequencyReductionMethod) declaredFieldValue(
StructurePlacement.class,
placement,
StructurePlacement.FrequencyReductionMethod.class);
float frequency = (float) declaredFieldValue(
StructurePlacement.class, placement, float.class);
int salt = (int) declaredFieldValue(
StructurePlacement.class, placement, int.class);
if (placement instanceof ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyedPlacement) {
return new ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement(
keyedPlacement.key,
locateOffset,
frequencyReductionMethod,
frequency,
salt,
exclusionZone,
keyedPlacement.spacing(),
keyedPlacement.separation(),
keyedPlacement.spreadType());
}
if (placement instanceof RandomSpreadStructurePlacement randomSpread) {
return new RandomSpreadStructurePlacement(
locateOffset,
frequencyReductionMethod,
frequency,
salt,
exclusionZone,
randomSpread.spacing(),
randomSpread.separation(),
randomSpread.spreadType());
}
if (placement instanceof ConcentricRingsStructurePlacement rings) {
return new ConcentricRingsStructurePlacement(
locateOffset,
frequencyReductionMethod,
frequency,
salt,
exclusionZone,
rings.distance(),
rings.spread(),
rings.count(),
rings.preferredBiomes());
}
throw new IllegalStateException("Unsupported structure placement with an exclusion zone: "
+ placement.getClass().getName());
}
private static Object declaredFieldValue(
Class<?> declaringType,
Object target,
Class<?> fieldType
) {
Field match = null;
for (Field field : declaringType.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers()) || field.getType() != fieldType) {
continue;
}
if (match != null) {
throw new IllegalStateException("Ambiguous " + fieldType.getName() + " field on "
+ declaringType.getName());
}
match = field;
}
if (match == null) {
throw new IllegalStateException("Missing " + fieldType.getName() + " field on "
+ declaringType.getName());
}
try {
match.setAccessible(true);
return match.get(target);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not read " + fieldType.getName() + " field on "
+ declaringType.getName(), e);
}
}
record Selection(
List<Holder<StructureSet>> structureSets,
int retainedManagedSets,
int excludedManagedSets
) {
}
}
@@ -7,6 +7,8 @@ import art.arcane.iris.engine.framework.IrisStructureLocator;
import art.arcane.iris.engine.framework.NativeStructureOwnershipRecord;
import art.arcane.iris.engine.framework.NativeStructureStartPlan;
import art.arcane.iris.engine.framework.NativeStructureGenerationPolicy;
import art.arcane.iris.engine.IrisEngine;
import art.arcane.iris.engine.platform.BukkitChunkGenerator;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.object.IrisMaterialPalette;
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
@@ -25,6 +27,7 @@ import art.arcane.iris.nativegen.NativeStructureVerticalPlacer;
import art.arcane.iris.nativegen.NativeStructureVanillaLocator;
import art.arcane.iris.nativegen.NativeStructureVolumeIndex;
import art.arcane.iris.nativegen.WorldgenTerrainHeightmaps;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.data.IrisCustomData;
import art.arcane.iris.util.common.reflect.WrappedField;
@@ -43,6 +46,7 @@ import net.minecraft.core.SectionPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.util.random.Weighted;
@@ -91,6 +95,8 @@ import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@@ -98,23 +104,32 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.IntBinaryOperator;
public class IrisChunkGenerator extends CustomChunkGenerator {
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
private static final Runnable NO_OP = () -> {
};
private final ChunkGenerator delegate;
private final Engine engine;
private final CustomBiomeSource customBiomeSource;
private final ServerLevel runtimeLevel;
private final @Nullable BukkitChunkGenerator platformGenerator;
private final int runtimeMinY;
private final int runtimeHeight;
private final int runtimeSeaLevel;
private final ConcurrentHashMap<SpawnTableKey, WeightedList<MobSpawnSettings.SpawnerData>> mergedSpawnTables = new ConcurrentHashMap<>();
private final ImportedFeatureStage importedFeatures;
private final AtomicReference<StudioStructureState> retainedStudioStructureState = new AtomicReference<>();
private volatile ReachableStructureCache reachableStructureCache;
private volatile StructureStepCache structureStepCache;
@@ -129,6 +144,10 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
this.customBiomeSource = customBiomeSource;
this.importedFeatures = new ImportedFeatureStage(engine);
ServerLevel level = ((CraftWorld) world).getHandle();
this.runtimeLevel = level;
this.platformGenerator = world.getGenerator() instanceof BukkitChunkGenerator bukkitGenerator
? bukkitGenerator
: null;
this.runtimeMinY = level.getMinY();
this.runtimeHeight = level.getHeight();
this.runtimeSeaLevel = runtimeMinY + engine.getDimension().getFluidHeight();
@@ -154,6 +173,12 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
if (platformGenerator != null && !platformGenerator.shouldGenerateStructures()) {
return null;
}
if (level != runtimeLevel || level.getChunkSource().getGenerator() != this) {
return null;
}
try (GenerationSessionLease lease = requireGenerationLease("bukkit_nms_structure_locate");
IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
HolderSet<Structure> reachable = filterReachableStructures(level, holders);
@@ -325,7 +350,15 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
try (GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_structures");
if (platformGenerator != null && !platformGenerator.shouldGenerateStructures()) {
return;
}
if (runtimeLevel.getChunkSource().getGenerator() != this
|| runtimeLevel.getChunkSource().getGeneratorState() != structureState) {
return;
}
try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_create_structures");
GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_structures");
IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
Map<Structure, StructureStart> previousStarts = new HashMap<>(access.getAllStarts());
super.createStructures(registryAccess, structureState, structureManager, access, templateManager, levelKey);
@@ -409,9 +442,192 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
return delegate.createState(holderlookup, randomstate, i, conf);
}
void retainStudioStructureState(
ServerLevel level,
ChunkMap chunkMap,
ChunkGeneratorStructureState structureState
) {
requireCurrentStructureOwner(level, chunkMap);
StudioStructureState retained = new StudioStructureState(
level,
chunkMap,
Objects.requireNonNull(structureState, "Studio native structure state"));
if (!retainedStudioStructureState.compareAndSet(null, retained)) {
throw new IllegalStateException("Studio native structure state is already retained.");
}
}
StudioStructureState retainedStudioStructureState(ServerLevel level, ChunkMap chunkMap) {
StudioStructureState retained = retainedStudioStructureState.get();
if (retained == null) {
return null;
}
requireCurrentStructureOwner(level, chunkMap);
if (retained.level() != level || retained.chunkMap() != chunkMap) {
throw new IllegalStateException("Retained Studio native structure state belongs to another world runtime.");
}
if (level.getChunkSource().getGeneratorState() != retained.structureState()) {
throw new IllegalStateException("Studio native structure state is no longer current.");
}
return retained;
}
void claimStudioStructureState(StudioStructureState retained) {
if (!retainedStudioStructureState.compareAndSet(retained, null)) {
throw new IllegalStateException("Studio native structure state changed before activation began.");
}
}
void abandonStudioStructureState() {
retainedStudioStructureState.set(null);
}
CompletableFuture<Void> initializeAndPublishStructureState(
ChunkGeneratorStructureState structureState,
StructureStatePublisher publisher
) {
return startStructureStateBootstrap(
structureState,
NO_OP,
() -> publishStructureState(publisher));
}
CompletableFuture<Void> activateStudioStructureState(StudioStructureState retained) {
Objects.requireNonNull(retained, "Retained Studio native structure state");
return startStructureStateBootstrap(
retained.structureState(),
() -> {
StudioStructureState current = retainedStudioStructureState(
retained.level(), retained.chunkMap());
if (current != retained) {
throw new IllegalStateException("Studio native structure state changed before activation.");
}
claimStudioStructureState(retained);
},
NO_OP);
}
private CompletableFuture<Void> startStructureStateBootstrap(
ChunkGeneratorStructureState structureState,
Runnable claim,
Runnable activation
) {
if (!(engine instanceof IrisEngine irisEngine)) {
throw new IllegalStateException("Native structure bootstrap requires an IrisEngine runtime.");
}
AtomicReference<CompletableFuture<Void>> registeredCompletion = new AtomicReference<>();
CompletableFuture<Void> completion = irisEngine.startNativeStructureBootstrap(
claim,
() -> {
CompletableFuture<Void> rings = initializeStructureState(structureState);
registeredCompletion.set(rings);
return rings;
},
() -> {
CompletableFuture<Void> rings = Objects.requireNonNull(
registeredCompletion.get(),
"Registered native structure ring completion");
if (rings.isCompletedExceptionally()) {
throw new IllegalStateException(
"Minecraft native structure ring bootstrap failed before activation.");
}
activation.run();
});
completion.whenComplete((ignored, failure) -> {
if (failure != null) {
Throwable cause = failure instanceof CompletionException && failure.getCause() != null
? failure.getCause()
: failure;
IrisLogging.reportError("Native structure ring bootstrap failed for world '"
+ runtimeLevel.getWorld().getName() + "'.", cause);
}
});
return completion;
}
private CompletableFuture<Void> initializeStructureState(ChunkGeneratorStructureState structureState) {
Map<?, ?> ringPositions = structureRingPositions(structureState);
structureState.ensureStructuresGenerated();
return structureRingCompletion(ringPositions);
}
private Map<?, ?> structureRingPositions(ChunkGeneratorStructureState structureState) {
try {
Field field = structureRingPositionsField();
field.setAccessible(true);
Object value = field.get(structureState);
if (!(value instanceof Map<?, ?> ringPositions)) {
throw new IllegalStateException("Minecraft native structure ring state is unavailable.");
}
return ringPositions;
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not bind Minecraft native structure ring completions.", e);
}
}
private CompletableFuture<Void> structureRingCompletion(Map<?, ?> ringPositions) {
List<CompletableFuture<?>> futures = new ArrayList<>(ringPositions.size());
for (Object candidate : ringPositions.values()) {
if (candidate instanceof CompletableFuture<?> future) {
futures.add(future);
} else {
throw new IllegalStateException(
"Minecraft native structure ring completion is not a future.");
}
}
CompletableFuture<?>[] completions = futures.toArray(new CompletableFuture<?>[0]);
return CompletableFuture.allOf(completions);
}
private Field structureRingPositionsField() {
List<Field> candidates = new ArrayList<>(1);
for (Field field : ChunkGeneratorStructureState.class.getDeclaredFields()) {
if (!Map.class.isAssignableFrom(field.getType())) {
continue;
}
Type genericType = field.getGenericType();
if (!(genericType instanceof ParameterizedType parameterizedType)) {
continue;
}
Type[] arguments = parameterizedType.getActualTypeArguments();
if (arguments.length == 2
&& arguments[1].getTypeName().contains(CompletableFuture.class.getName())) {
candidates.add(field);
}
}
if (candidates.size() != 1) {
throw new IllegalStateException("Expected one Minecraft native structure ring-future map, found "
+ candidates.size() + ".");
}
return candidates.getFirst();
}
private void requireCurrentStructureOwner(ServerLevel level, ChunkMap chunkMap) {
if (runtimeLevel != level
|| level.getChunkSource().chunkMap != chunkMap
|| level.getChunkSource().getGenerator() != this) {
throw new IllegalStateException("Iris native structure state no longer belongs to the active world runtime.");
}
}
private void publishStructureState(StructureStatePublisher publisher) {
try {
publisher.publish();
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not publish Minecraft native structure state.", e);
}
}
@Override
public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) {
try (GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_references");
if (platformGenerator != null && !platformGenerator.shouldGenerateStructures()) {
return;
}
if (runtimeLevel.getChunkSource().getGenerator() != this) {
return;
}
try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_create_references");
GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_references");
IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
NativeStructureReferenceRepair.createReferences(
engine, generatoraccessseed, structuremanager, ichunkaccess);
@@ -420,7 +636,8 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public CompletableFuture<ChunkAccess> createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) {
try (GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_biomes");
try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_create_biomes");
GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_biomes");
IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
ichunkaccess.fillBiomesFromNoise(customBiomeSource::getVisibleNoiseBiome, randomstate.sampler());
return CompletableFuture.completedFuture(ichunkaccess);
@@ -439,17 +656,52 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public CompletableFuture<ChunkAccess> fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess)
.thenApply(filled -> {
try (GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_nms_worldgen_heightmaps");
IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
primeWorldgenHeightmaps(filled);
return filled;
} catch (GenerationSessionException e) {
throw new IllegalStateException(
"Iris worldgen heightmap priming could not acquire its engine runtime.", e);
}
});
BukkitChunkGenerator.GenerationStagePermit stage = requireNoiseGenerationStage(
ichunkaccess.getPos(),
"bukkit_nms_chunk_pipeline");
GenerationSessionLease lease;
try {
lease = requireGenerationLease("bukkit_nms_chunk_pipeline");
} catch (RuntimeException | Error failure) {
stage.close();
throw failure;
}
try {
CompletableFuture<ChunkAccess> pipeline = delegate
.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess)
.thenApply(filled -> {
try (IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
primeWorldgenHeightmaps(filled);
return filled;
}
});
CompletableFuture<ChunkAccess> completion = new CompletableFuture<>();
pipeline.whenComplete((filled, failure) -> {
boolean cancelled = isCancellationFailure(failure);
lease.close();
stage.close();
if (failure == null) {
completion.complete(filled);
} else if (cancelled) {
completion.cancel(false);
} else {
completion.completeExceptionally(failure);
}
});
return completion;
} catch (RuntimeException | Error failure) {
lease.close();
stage.close();
throw failure;
}
}
private static boolean isCancellationFailure(Throwable failure) {
Throwable current = failure;
while (current instanceof CompletionException && current.getCause() != null) {
current = current.getCause();
}
return current instanceof CancellationException;
}
private void primeWorldgenHeightmaps(ChunkAccess chunkAccess) {
@@ -502,11 +754,12 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
// Bind-time equivalent for Bukkit: the table is built on the first decorated chunk, which is where a
// feature-order cycle is reported once and degraded to features-off.
importedFeatures.prepare(generatoraccessseed);
try (GenerationSessionLease lease = requireGenerationLease("bukkit_nms_biome_decoration");
try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_biome_decoration");
GenerationSessionLease lease = requireGenerationLease("bukkit_nms_biome_decoration");
IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
// Bind-time equivalent for Bukkit: the table is built on the first decorated chunk, which is where a
// feature-order cycle is reported once and degraded to features-off.
importedFeatures.prepare(generatoraccessseed);
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
placeVanillaStructures(generatoraccessseed, ichunkaccess, structuremanager);
// Vanilla's placed-feature pass, on THIS thread. The delegate is still called with
@@ -833,6 +1086,25 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
}
}
private BukkitChunkGenerator.GenerationStagePermit requireGenerationStage(String operation) {
return platformGenerator == null
? BukkitChunkGenerator.GenerationStagePermit.noop()
: platformGenerator.acquireGenerationStage(operation);
}
private BukkitChunkGenerator.GenerationStagePermit requireNoiseGenerationStage(
ChunkPos chunkPos,
String operation
) {
return platformGenerator == null
? BukkitChunkGenerator.GenerationStagePermit.noop()
: platformGenerator.acquireNoiseGenerationStage(
engine,
chunkPos.x(),
chunkPos.z(),
operation);
}
@Override
public Optional<Identifier> getTypeNameForDataFixer() {
return delegate.getTypeNameForDataFixer();
@@ -894,6 +1166,18 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
private record StructureStepCache(Registry<Structure> registry, List<List<Structure>> structures) {
}
record StudioStructureState(
ServerLevel level,
ChunkMap chunkMap,
ChunkGeneratorStructureState structureState
) {
}
@FunctionalInterface
interface StructureStatePublisher {
void publish() throws IllegalAccessException;
}
private record NativePlacement(StructureStart start, IrisNativeStructureDecision decision) {
}
@@ -4,6 +4,8 @@ import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import art.arcane.iris.core.datapack.DatapackStructureScopeIndex;
import art.arcane.iris.core.nms.DatapackStructureScopeResult;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.core.nms.INMSBinding;
import art.arcane.iris.core.nms.MinecraftVersion;
@@ -17,6 +19,7 @@ import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.NativeStructureVolume;
import art.arcane.iris.nativegen.NativeStructureVolumeIndex;
import art.arcane.iris.engine.object.IrisDimensionRuntimeContract;
import art.arcane.iris.engine.platform.BukkitChunkGenerator;
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
import art.arcane.iris.nativegen.NativeStructureFactory;
import art.arcane.iris.nativegen.NativeStructureGenerationException;
@@ -95,6 +98,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.ProtoChunk;
@@ -156,6 +160,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -313,8 +318,14 @@ public class NMSBinding implements INMSBinding {
return;
}
var level = ((CraftWorld) pos.getWorld()).getHandle();
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
ServerLevel level = ((CraftWorld) pos.getWorld()).getHandle();
BlockPos blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
int chunkX = pos.getBlockX() >> 4;
int chunkZ = pos.getBlockZ() >> 4;
if (J.isOwnedByCurrentRegion(pos.getWorld(), chunkX, chunkZ)) {
merge(level, blockPos, tag);
return;
}
if (!J.runAt(pos, () -> merge(level, blockPos, tag))) {
IrisLogging.warn("[NMS] Failed to schedule tile deserialize at " + blockPos + " in world " + pos.getWorld().getName());
}
@@ -1201,6 +1212,121 @@ public class NMSBinding implements INMSBinding {
retargetStructureCheck(level, irisGenerator);
}
@Override
public DatapackStructureScopeResult scopeDatapackStructures(
World world,
DatapackStructureScopeIndex scopeIndex,
Set<String> declaredSources
) throws NoSuchFieldException, IllegalAccessException {
ServerLevel level = ((CraftWorld) world).getHandle();
ChunkMap chunkMap = level.getChunkSource().chunkMap;
ChunkGeneratorStructureState currentState = level.getChunkSource().getGeneratorState();
net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator();
ChunkGeneratorStructureState scopedState = createStructureState(level, generator, currentState);
DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter(
scopedState.possibleStructureSets(), scopeIndex, declaredSources);
Field possibleSetsField = getField(scopedState.getClass(), List.class);
possibleSetsField.setAccessible(true);
possibleSetsField.set(scopedState, selection.structureSets());
Field stateField = getField(chunkMap.getClass(), ChunkGeneratorStructureState.class);
stateField.setAccessible(true);
BukkitChunkGenerator platformGenerator = world.getGenerator() instanceof BukkitChunkGenerator bukkitGenerator
? bukkitGenerator
: null;
boolean studioBootstrap = platformGenerator != null
&& platformGenerator.isStudioEntryBootstrapActive();
boolean jigsawStudio = platformGenerator != null
&& platformGenerator.isJigsawStudioActive();
if (jigsawStudio) {
requireIrisGenerator(generator);
if (currentState.possibleStructureSets().isEmpty()) {
currentState.ensureStructuresGenerated();
} else {
ChunkGeneratorStructureState bootstrapState = createStructureState(level, generator, currentState);
Field bootstrapSetsField = getField(bootstrapState.getClass(), List.class);
bootstrapSetsField.setAccessible(true);
bootstrapSetsField.set(bootstrapState, List.of());
bootstrapState.ensureStructuresGenerated();
stateField.set(chunkMap, bootstrapState);
}
} else if (studioBootstrap) {
IrisChunkGenerator irisGenerator = requireIrisGenerator(generator);
irisGenerator.retainStudioStructureState(level, chunkMap, scopedState);
try {
stateField.set(chunkMap, scopedState);
} catch (IllegalAccessException | RuntimeException | Error failure) {
irisGenerator.abandonStudioStructureState();
throw failure;
}
} else {
initializeAndPublishStructureState(
generator,
scopedState,
() -> stateField.set(chunkMap, scopedState));
}
return new DatapackStructureScopeResult(
selection.retainedManagedSets(),
selection.excludedManagedSets());
}
@Override
public void completeStudioStructureBootstrap(World world) throws NoSuchFieldException, IllegalAccessException {
ServerLevel level = ((CraftWorld) world).getHandle();
ChunkMap chunkMap = level.getChunkSource().chunkMap;
IrisChunkGenerator generator = requireIrisGenerator(level.getChunkSource().getGenerator());
IrisChunkGenerator.StudioStructureState retained =
generator.retainedStudioStructureState(level, chunkMap);
if (retained == null) {
return;
}
generator.activateStudioStructureState(retained);
}
@Override
public void abandonStudioStructureBootstrap(World world) {
ServerLevel level = ((CraftWorld) world).getHandle();
net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator();
if (generator instanceof IrisChunkGenerator irisGenerator) {
irisGenerator.abandonStudioStructureState();
}
}
private ChunkGeneratorStructureState createStructureState(
ServerLevel level,
net.minecraft.world.level.chunk.ChunkGenerator generator,
ChunkGeneratorStructureState currentState
) {
return generator.createState(
level.registryAccess().lookupOrThrow(Registries.STRUCTURE_SET),
currentState.randomState(),
currentState.getLevelSeed(),
currentState.conf);
}
private void initializeAndPublishStructureState(
net.minecraft.world.level.chunk.ChunkGenerator generator,
ChunkGeneratorStructureState structureState,
IrisChunkGenerator.StructureStatePublisher publisher
) throws IllegalAccessException {
if (generator instanceof IrisChunkGenerator irisGenerator) {
irisGenerator.initializeAndPublishStructureState(structureState, publisher);
return;
}
structureState.ensureStructuresGenerated();
publisher.publish();
}
private IrisChunkGenerator requireIrisGenerator(
net.minecraft.world.level.chunk.ChunkGenerator generator
) {
if (generator instanceof IrisChunkGenerator irisGenerator) {
return irisGenerator;
}
throw new IllegalStateException("Studio native structure state is not owned by the active Iris generator.");
}
private void validateDimensionContract(Engine engine, World world, ServerLevel level) {
DimensionType actualType = level.dimensionType();
String actualTypeKey = level.dimensionTypeRegistration().unwrapKey()
@@ -5,9 +5,21 @@ import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
public class IrisChunkGeneratorFailureContractTest {
@@ -20,9 +32,17 @@ public class IrisChunkGeneratorFailureContractTest {
int reachabilityStart = source.indexOf("private Set<String> reachableStructureKeys");
int reachabilityEnd = source.indexOf("protected MapCodec", reachabilityStart);
String reachability = source.substring(reachabilityStart, reachabilityEnd);
int bootstrapGate = locate.indexOf("!platformGenerator.shouldGenerateStructures()");
int generatorIdentity = locate.indexOf("level.getChunkSource().getGenerator() != this");
int lease = locate.indexOf("requireGenerationLease(\"bukkit_nms_structure_locate\")");
int nativePrediction = locate.indexOf("NativeStructureVanillaLocator.predict(");
assertTrue(locate.contains("reached its safety limit"));
assertTrue(locate.contains("unregistered structure holder"));
assertTrue(bootstrapGate >= 0);
assertTrue(generatorIdentity > bootstrapGate);
assertTrue(lease > generatorIdentity);
assertTrue(nativePrediction > lease);
assertFalse(locate.contains("catch (Throwable"));
assertFalse(locate.contains("IrisLogging.reportError"));
assertFalse(reachability.contains("catch (Throwable"));
@@ -78,13 +98,101 @@ public class IrisChunkGeneratorFailureContractTest {
int referencesEnd = source.indexOf("public CompletableFuture<ChunkAccess> createBiomes", referencesStart);
String references = source.substring(referencesStart, referencesEnd);
assertTrue(references.contains("requireGenerationLease(\"bukkit_nms_create_references\")"));
int nativeStructureGate = references.indexOf("!platformGenerator.shouldGenerateStructures()");
int generatorIdentity = references.indexOf("runtimeLevel.getChunkSource().getGenerator() != this");
int stage = references.indexOf("requireGenerationStage(\"bukkit_nms_create_references\")");
int lease = references.indexOf("requireGenerationLease(\"bukkit_nms_create_references\")");
int repair = references.indexOf("NativeStructureReferenceRepair.createReferences(");
assertTrue(nativeStructureGate >= 0);
assertTrue(generatorIdentity > nativeStructureGate);
assertTrue(stage > generatorIdentity);
assertTrue(lease > stage);
assertTrue(repair > lease);
assertTrue(references.contains("IrisContext.open(engine, lease.sessionId(), null)"));
assertTrue(references.contains("NativeStructureReferenceRepair.createReferences("));
assertFalse(references.contains("delegate.createReferences("));
assertFalse(references.contains("catch ("));
}
@Test
public void structureGenerationAcquiresBootstrapGateBeforeUsingPublishedState() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
int generationStart = source.indexOf("public void createStructures(");
int generationEnd = source.indexOf("private void adjustGeneratedStructures", generationStart);
String generation = source.substring(generationStart, generationEnd);
int gate = generation.indexOf("!platformGenerator.shouldGenerateStructures()");
int currentGenerator = generation.indexOf(
"runtimeLevel.getChunkSource().getGenerator() != this");
int stateIdentity = generation.indexOf(
"runtimeLevel.getChunkSource().getGeneratorState() != structureState");
int stage = generation.indexOf(
"requireGenerationStage(\"bukkit_nms_create_structures\")");
int lease = generation.indexOf(
"requireGenerationLease(\"bukkit_nms_create_structures\")");
int generationCall = generation.indexOf(
"super.createStructures(registryAccess, structureState");
assertTrue(gate >= 0);
assertTrue(currentGenerator > gate);
assertTrue(stateIdentity > currentGenerator);
assertTrue(stage > stateIdentity);
assertTrue(lease > stage);
assertTrue(generationCall > lease);
assertTrue(generation.contains("structureState,\n structureManager"));
assertFalse(generation.contains("resolvePublishedStructureState"));
}
@Test
public void nativeRingBootstrapTracksTheExactMinecraftFutures() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
int initializeStart = source.indexOf("private CompletableFuture<Void> initializeStructureState(");
int initializeEnd = source.indexOf("private Map<?, ?> structureRingPositions", initializeStart);
String initialize = source.substring(initializeStart, initializeEnd);
int completionStart = source.indexOf(
"private CompletableFuture<Void> structureRingCompletion", initializeEnd);
int completionEnd = source.indexOf("private void requireCurrentStructureOwner", completionStart);
String completion = source.substring(completionStart, completionEnd);
assertBefore(initialize,
"structureRingPositions(structureState)",
"structureState.ensureStructuresGenerated()");
assertBefore(initialize,
"structureState.ensureStructuresGenerated()",
"structureRingCompletion(ringPositions)");
assertFalse(initialize.contains("catch ("));
assertTrue(completion.contains("for (Object candidate : ringPositions.values())"));
assertTrue(completion.contains("CompletableFuture.allOf(completions)"));
assertTrue(completion.contains("throw new IllegalStateException("));
assertFalse(completion.contains("join()"));
assertFalse(completion.contains("get()"));
}
@Test
public void standardActivationClaimsTheExactPublishedStateBeforeInitialization() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
int activationStart = source.indexOf("CompletableFuture<Void> activateStudioStructureState(");
int activationEnd = source.indexOf(
"private CompletableFuture<Void> startStructureStateBootstrap", activationStart);
String activation = source.substring(activationStart, activationEnd);
int bootstrapStart = activationEnd;
int bootstrapEnd = source.indexOf(
"private CompletableFuture<Void> initializeStructureState", bootstrapStart);
String bootstrap = source.substring(bootstrapStart, bootstrapEnd);
assertTrue(activation.contains("retained.structureState()"));
assertBefore(activation,
"retainedStudioStructureState(",
"claimStudioStructureState(retained)");
assertBefore(bootstrap,
"claim,",
"initializeStructureState(");
assertBefore(bootstrap,
"initializeStructureState(",
"activation.run()");
assertFalse(activation.contains("publishStructureState("));
assertFalse(source.contains("ChunkGeneratorStructureState fullState"));
}
@Test
public void terrainWritesPrimeTheWorldgenHeightmapsForEveryChunk() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
@@ -107,10 +215,171 @@ public class IrisChunkGeneratorFailureContractTest {
assertTrue(placement.contains("WorldgenTerrainHeightmaps.primeStructurePlacement("));
assertTrue(placement.indexOf("WorldgenTerrainHeightmaps.primeStructurePlacement(")
< placement.indexOf("prepareSurfaceStructures"));
assertTrue(source.contains("engine.acquireGenerationLease(\"bukkit_nms_worldgen_heightmaps\")"));
assertTrue(fill.contains("requireGenerationLease(\"bukkit_nms_chunk_pipeline\")"));
assertTrue(source.contains("int minY = chunk.getMinY() + 1;"));
}
@Test
public void fillFromNoiseLeaseSpansTheDelegateAndHeightmapPipeline() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
int fillStart = source.indexOf("public CompletableFuture<ChunkAccess> fillFromNoise");
int fillEnd = source.indexOf("private static boolean isCancellationFailure", fillStart);
String fill = source.substring(fillStart, fillEnd);
int completionStart = fill.indexOf("pipeline.whenComplete(");
int completionEnd = fill.indexOf(" return completion;", completionStart);
assertTrue(completionStart >= 0);
assertTrue(completionEnd > completionStart);
String completion = fill.substring(completionStart, completionEnd);
assertBefore(fill,
"BukkitChunkGenerator.GenerationStagePermit stage = requireNoiseGenerationStage(",
"GenerationSessionLease lease");
assertBefore(fill,
"requireNoiseGenerationStage(",
"\"bukkit_nms_chunk_pipeline\")");
assertBefore(fill,
"lease = requireGenerationLease(\"bukkit_nms_chunk_pipeline\")",
".fillFromNoise(blender, randomstate, structuremanager, ichunkaccess)");
assertTrue(fill.contains("IrisContext.open(engine, lease.sessionId(), null)"));
assertBefore(fill, "primeWorldgenHeightmaps(filled)", "pipeline.whenComplete(");
assertTrue(fill.contains("CompletableFuture<ChunkAccess> completion = new CompletableFuture<>()"));
assertBefore(completion, "boolean cancelled = isCancellationFailure(failure);", "lease.close();");
assertBefore(completion, "lease.close();", "stage.close();");
assertBefore(completion, "stage.close();", "completion.complete(filled)");
assertBefore(completion, "stage.close();", "completion.cancel(false)");
assertBefore(completion, "stage.close();", "completion.completeExceptionally(failure)");
assertTrue(completion.contains("else if (cancelled)"));
assertFalse(completion.contains("pipeline.isCancelled()"));
assertFalse(completion.contains("finally"));
assertTrue(fill.contains("catch (RuntimeException | Error failure)"));
assertTrue(fill.contains("lease.close();\n stage.close();\n throw failure;"));
assertTrue(fill.contains("return completion;"));
assertFalse(fill.contains("return pipeline;"));
assertFalse(fill.contains("pipeline.cancel("));
assertFalse(fill.contains("bukkit_nms_worldgen_heightmaps"));
}
@Test
public void releasedNoiseAdmissionLetsSynchronousDependentWaitForQueuedExclusive() throws Exception {
Semaphore admission = new Semaphore(1, true);
admission.acquire();
CountDownLatch exclusiveEntered = new CountDownLatch(1);
CountDownLatch releaseExclusive = new CountDownLatch(1);
AtomicBoolean dependentFinished = new AtomicBoolean(false);
ExecutorService executor = Executors.newSingleThreadExecutor();
boolean stageReleased = false;
try {
Future<?> exclusive = executor.submit(() -> {
boolean acquired = false;
try {
admission.acquire();
acquired = true;
exclusiveEntered.countDown();
assertTrue(releaseExclusive.await(2, TimeUnit.SECONDS));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
} finally {
if (acquired) {
admission.release();
}
}
});
awaitQueueLength(admission, 1);
CompletableFuture<Void> outward = new CompletableFuture<>();
outward.thenRun(() -> {
try {
assertTrue(exclusiveEntered.await(2, TimeUnit.SECONDS));
dependentFinished.set(true);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
}
});
admission.release();
stageReleased = true;
assertTrue(outward.complete(null));
assertTrue(dependentFinished.get());
releaseExclusive.countDown();
exclusive.get(2, TimeUnit.SECONDS);
assertEquals(1, admission.availablePermits());
} finally {
if (!stageReleased) {
admission.release();
}
releaseExclusive.countDown();
executor.shutdownNow();
}
}
@Test
public void transformedDelegateCancellationIsDetectedThroughItsCompletionFailure() throws IOException {
CompletableFuture<Void> delegate = new CompletableFuture<>();
CompletableFuture<Void> transformed = delegate.thenApply(value -> value);
assertTrue(delegate.cancel(false));
assertFalse(transformed.isCancelled());
CompletionException failure = assertThrows(CompletionException.class, transformed::join);
assertTrue(failure.getCause() instanceof CancellationException);
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
String cancellation = method(
source,
"private static boolean isCancellationFailure",
"private void primeWorldgenHeightmaps");
assertTrue(cancellation.contains("current instanceof CompletionException"));
assertTrue(cancellation.contains("current instanceof CancellationException"));
}
@Test
public void noiseAdmissionCanPrepareStudioBeforeAcquiringTheGenerationLease() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
String fill = method(
source,
"public CompletableFuture<ChunkAccess> fillFromNoise",
"private static boolean isCancellationFailure");
String admission = method(
source,
"private BukkitChunkGenerator.GenerationStagePermit requireNoiseGenerationStage",
"public Optional<Identifier> getTypeNameForDataFixer");
assertBefore(fill,
"requireNoiseGenerationStage(",
"requireGenerationLease(\"bukkit_nms_chunk_pipeline\")");
assertTrue(admission.contains("platformGenerator.acquireNoiseGenerationStage("));
assertBefore(admission, "engine,", "chunkPos.x(),");
assertBefore(admission, "chunkPos.x(),", "chunkPos.z(),");
assertFalse(fill.contains("requireGenerationStage(\"bukkit_nms_chunk_pipeline\")"));
}
@Test
public void everyTopLevelMoonriseStageEntersTheFairGateBeforeItsGenerationLease() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
String structures = method(source, "public void createStructures(", "private void adjustGeneratedStructures");
String references = method(source, "public void createReferences(", "public CompletableFuture<ChunkAccess> createBiomes");
String biomes = method(source, "public CompletableFuture<ChunkAccess> createBiomes", "public void buildSurface");
String noise = method(source, "public CompletableFuture<ChunkAccess> fillFromNoise", "private static boolean isCancellationFailure");
String decoration = method(source,
"public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla)",
"public BiomeGenerationSettings getBiomeGenerationSettings");
assertStageBeforeLease(structures, "bukkit_nms_create_structures");
assertStageBeforeLease(references, "bukkit_nms_create_references");
assertStageBeforeLease(biomes, "bukkit_nms_create_biomes");
assertBefore(noise,
"requireNoiseGenerationStage(",
"requireGenerationLease(\"bukkit_nms_chunk_pipeline\")");
assertStageBeforeLease(decoration, "bukkit_nms_biome_decoration");
assertBefore(decoration,
"requireGenerationLease(\"bukkit_nms_biome_decoration\")",
"importedFeatures.prepare(generatoraccessseed)");
assertEquals(4, occurrences(source, "requireGenerationStage(\"bukkit_nms_"));
assertEquals(2, occurrences(source, "requireNoiseGenerationStage("));
assertFalse(source.contains("GenerationSessionManager"));
}
@Test
public void worldgenHeightmapPrimingLivesInTheSharedNativegenSources() throws IOException {
Path nativegen = Path.of(System.getProperty("iris.nativeStructurePostProcessorSource")).getParent();
@@ -174,6 +443,37 @@ public class IrisChunkGeneratorFailureContractTest {
return count;
}
private static void awaitQueueLength(Semaphore semaphore, int expected) {
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(2);
while (semaphore.getQueueLength() < expected && System.nanoTime() < deadline) {
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1));
}
assertTrue("Expected at least " + expected + " queued semaphore threads",
semaphore.getQueueLength() >= expected);
}
private static String method(String source, String startToken, String endToken) {
int start = source.indexOf(startToken);
int end = source.indexOf(endToken, start);
assertTrue("Missing source contract token: " + startToken, start >= 0);
assertTrue("Missing source contract token: " + endToken, end > start);
return source.substring(start, end);
}
private static void assertStageBeforeLease(String source, String operation) {
assertBefore(source,
"requireGenerationStage(\"" + operation + "\")",
"requireGenerationLease(\"" + operation + "\")");
}
private static void assertBefore(String source, String first, String second) {
int firstIndex = source.indexOf(first);
int secondIndex = source.indexOf(second);
assertTrue("Missing source contract token: " + first, firstIndex >= 0);
assertTrue("Missing source contract token: " + second, secondIndex >= 0);
assertTrue(first + " must occur before " + second, firstIndex < secondIndex);
}
@Test
public void vanillaChunkGenerationMobsUseTheVisibleBiomesVanillaDerivative() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
@@ -0,0 +1,334 @@
package art.arcane.iris.core.nms.v26_2_R1;
import art.arcane.iris.core.datapack.DatapackIngestService;
import art.arcane.iris.core.datapack.DatapackStructureScopeIndex;
import com.mojang.datafixers.util.Either;
import net.minecraft.SharedConstants;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderOwner;
import net.minecraft.core.Vec3i;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.Bootstrap;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType;
import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import org.junit.Test;
import org.junit.BeforeClass;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public class NMSBindingDatapackStructureScopeTest {
private static final String SOURCE = "https://example.test/managed.zip";
@BeforeClass
public static void bootstrapMinecraft() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void managedSetContainingVanillaStructureIsAbsentOutsideDeclaringDimension() {
Holder<Structure> vanillaStructure = structureHolder("minecraft:pillager_outpost");
Holder<StructureSet> managedSet = structureSetHolder(
"managed:illager_barracks", vanillaStructure);
DatapackStructureScopeIndex index = index(
List.of(),
List.of("managed:illager_barracks"));
DatapackStructureStateFilter.Selection vanilla = DatapackStructureStateFilter.filter(
List.of(managedSet), index, Set.of());
DatapackStructureStateFilter.Selection declaring = DatapackStructureStateFilter.filter(
List.of(managedSet), index, index.declaredSources(List.of(SOURCE)));
assertEquals(0, vanilla.structureSets().size());
assertEquals(1, vanilla.excludedManagedSets());
assertEquals(1, declaring.structureSets().size());
assertSame(managedSet, declaring.structureSets().getFirst());
}
@Test
public void unmanagedSetRetainsOnlyDefinitionsAllowedInTheWorld() {
Holder<Structure> vanillaStructure = structureHolder("minecraft:village_plains");
Holder<Structure> managedStructure = structureHolder("managed:tavern");
Holder<StructureSet> vanillaSet = structureSetHolder(
"minecraft:villages", vanillaStructure, managedStructure);
DatapackStructureScopeIndex index = index(
List.of("managed:tavern"),
List.of());
DatapackStructureStateFilter.Selection vanilla = DatapackStructureStateFilter.filter(
List.of(vanillaSet), index, Set.of());
DatapackStructureStateFilter.Selection declaring = DatapackStructureStateFilter.filter(
List.of(vanillaSet), index, index.declaredSources(List.of(SOURCE)));
assertEquals(1, vanilla.structureSets().size());
assertEquals(1, vanilla.structureSets().getFirst().value().structures().size());
assertSame(vanillaStructure,
vanilla.structureSets().getFirst().value().structures().getFirst().structure());
assertSame(vanillaSet.value().placement(),
vanilla.structureSets().getFirst().value().placement());
assertSame(vanillaSet, declaring.structureSets().getFirst());
}
@Test
public void setWithNoAllowedDefinitionsIsRemoved() {
Holder<StructureSet> unmanagedSet = structureSetHolder(
"minecraft:custom", structureHolder("managed:only"));
DatapackStructureScopeIndex index = index(List.of("managed:only"), List.of());
DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter(
List.of(unmanagedSet), index, Set.of());
assertEquals(0, selection.structureSets().size());
}
@Test
public void spigotDirectHolderRetainsItsStructureSetKey() {
ResourceKey<StructureSet> key = ResourceKey.create(
Registries.STRUCTURE_SET,
Identifier.parse("minecraft:villages"));
ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement placement =
new ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement(
key,
Vec3i.ZERO,
StructurePlacement.FrequencyReductionMethod.DEFAULT,
1.0F,
10387312,
Optional.empty(),
34,
8,
RandomSpreadType.LINEAR);
StructureSet structureSet = new StructureSet(
List.of(new StructureSet.StructureSelectionEntry(
structureHolder("minecraft:village_plains"), 1)),
placement);
assertEquals("minecraft:villages",
DatapackStructureStateFilter.structureSetKey(Holder.direct(structureSet)));
}
@Test
public void excludedManagedSetCannotSuppressAnAllowedSetThroughExclusionZone() {
Holder<StructureSet> managedSet = structureSetHolder(
"managed:blocked",
structureHolder("managed:blocked"));
RandomSpreadStructurePlacement originalPlacement = new RandomSpreadStructurePlacement(
Vec3i.ZERO,
StructurePlacement.FrequencyReductionMethod.DEFAULT,
1.0F,
4567,
Optional.of(new StructurePlacement.ExclusionZone(managedSet, 1)),
32,
8,
RandomSpreadType.LINEAR);
Holder<StructureSet> vanillaSet = structureSetHolder(
"minecraft:allowed",
originalPlacement,
structureHolder("minecraft:village_plains"));
DatapackStructureScopeIndex index = index(
List.of("managed:blocked"),
List.of("managed:blocked"));
DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter(
List.of(vanillaSet, managedSet), index, Set.of());
assertEquals(1, selection.structureSets().size());
StructurePlacement scopedPlacement = selection.structureSets().getFirst().value().placement();
assertEquals(0, DatapackStructureStateFilter.exclusionZone(scopedPlacement).stream().count());
}
@Test
public void standardPublishesOneDeferredStateWhileJigsawPublishesInitializedEmptyState() throws IOException {
Path chunkGeneratorSource = Path.of(System.getProperty("iris.nmsChunkGeneratorSource"));
String source = Files.readString(chunkGeneratorSource.resolveSibling("NMSBinding.java"));
int methodStart = source.indexOf("public DatapackStructureScopeResult scopeDatapackStructures(");
int methodEnd = source.indexOf("\n @Override\n public void completeStudioStructureBootstrap", methodStart);
assertTrue(methodStart >= 0);
assertTrue(methodEnd > methodStart);
String method = source.substring(methodStart, methodEnd);
int filteredState = method.indexOf("possibleSetsField.set(scopedState, selection.structureSets());");
int jigsawMode = method.indexOf(
"boolean jigsawStudio = platformGenerator != null");
int jigsawOnly = method.indexOf("if (jigsawStudio)");
int emptyCreation = method.indexOf("ChunkGeneratorStructureState bootstrapState = createStructureState(");
int emptyFiltering = method.indexOf("bootstrapSetsField.set(bootstrapState, List.of());");
int emptyInitialization = method.indexOf("bootstrapState.ensureStructuresGenerated();");
int emptyPublication = method.indexOf("stateField.set(chunkMap, bootstrapState);");
int standardOnly = method.indexOf("else if (studioBootstrap)");
int retention = method.indexOf("irisGenerator.retainStudioStructureState(");
int standardPublication = method.indexOf("stateField.set(chunkMap, scopedState);");
int immediateInitialization = method.indexOf("initializeAndPublishStructureState(");
assertTrue(filteredState >= 0);
assertTrue(jigsawMode > filteredState);
assertTrue(jigsawOnly > jigsawMode);
assertTrue(emptyCreation > jigsawOnly);
assertTrue(emptyFiltering > emptyCreation);
assertTrue(emptyInitialization > emptyFiltering);
assertTrue(emptyPublication > emptyInitialization);
assertTrue(standardOnly > emptyPublication);
assertTrue(retention > standardOnly);
assertTrue(standardPublication > retention);
assertTrue(immediateInitialization > standardPublication);
assertFalse(method.contains("scopedState.ensureStructuresGenerated();"));
assertFalse(method.contains("if (studioBootstrap && platformGenerator.isJigsawStudioActive())"));
}
@Test
public void standardCompletionActivatesTheAlreadyPublishedStateWithoutReplacingIt() throws IOException {
Path chunkGeneratorSource = Path.of(System.getProperty("iris.nmsChunkGeneratorSource"));
String source = Files.readString(chunkGeneratorSource.resolveSibling("NMSBinding.java"));
int methodStart = source.indexOf("public void completeStudioStructureBootstrap(World world)");
int methodEnd = source.indexOf("\n @Override\n public void abandonStudioStructureBootstrap", methodStart);
assertTrue(methodStart >= 0);
assertTrue(methodEnd > methodStart);
String method = source.substring(methodStart, methodEnd);
int retained = method.indexOf("generator.retainedStudioStructureState(level, chunkMap)");
int activation = method.indexOf("generator.activateStudioStructureState(retained);");
assertTrue(retained >= 0);
assertTrue(activation > retained);
assertFalse(method.contains("stateField.set("));
assertFalse(method.contains("retained.fullState()"));
}
private static DatapackStructureScopeIndex index(
List<String> structureKeys,
List<String> structureSetKeys
) {
return DatapackStructureScopeIndex.create(List.of(
new DatapackIngestService.StructureScopeResources(
SOURCE,
structureKeys,
structureSetKeys)));
}
private static Holder<Structure> structureHolder(String key) {
return new KeyedHolder<>(ResourceKey.create(Registries.STRUCTURE, Identifier.parse(key)), null);
}
private static Holder<StructureSet> structureSetHolder(
String key,
Holder<Structure>... structures
) {
return structureSetHolder(
key,
new RandomSpreadStructurePlacement(32, 8, RandomSpreadType.LINEAR, 12345),
structures);
}
private static Holder<StructureSet> structureSetHolder(
String key,
StructurePlacement placement,
Holder<Structure>... structures
) {
List<StructureSet.StructureSelectionEntry> entries = Stream.of(structures)
.map(structure -> new StructureSet.StructureSelectionEntry(structure, 1))
.toList();
StructureSet value = new StructureSet(entries, placement);
return new KeyedHolder<>(
ResourceKey.create(Registries.STRUCTURE_SET, Identifier.parse(key)),
value);
}
private static final class KeyedHolder<T> implements Holder<T> {
private final ResourceKey<T> key;
private final T value;
private KeyedHolder(ResourceKey<T> key, T value) {
this.key = key;
this.value = value;
}
@Override
public T value() {
return value;
}
@Override
public boolean isBound() {
return true;
}
@Override
public boolean areComponentsBound() {
return true;
}
@Override
public boolean is(Identifier identifier) {
return key.identifier().equals(identifier);
}
@Override
public boolean is(ResourceKey<T> candidate) {
return key.equals(candidate);
}
@Override
public boolean is(Predicate<ResourceKey<T>> predicate) {
return predicate.test(key);
}
@Override
public boolean is(TagKey<T> tag) {
return false;
}
@Override
public boolean is(Holder<T> holder) {
return holder == this;
}
@Override
public Stream<TagKey<T>> tags() {
return Stream.empty();
}
@Override
public DataComponentMap components() {
return DataComponentMap.EMPTY;
}
@Override
public Either<ResourceKey<T>, T> unwrap() {
return Either.left(key);
}
@Override
public Optional<ResourceKey<T>> unwrapKey() {
return Optional.of(key);
}
@Override
public Kind kind() {
return Kind.REFERENCE;
}
@Override
public boolean canSerializeIn(HolderOwner<T> owner) {
return true;
}
}
}
@@ -0,0 +1,30 @@
package art.arcane.iris.core.nms.v26_2_R1;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.Assert.assertTrue;
public class NMSBindingTileDeserializeContractTest {
@Test
public void ownedRegionTileDataMergesBeforeScheduledFallback() throws IOException {
Path chunkGeneratorSource = Path.of(System.getProperty("iris.nmsChunkGeneratorSource"));
String source = Files.readString(chunkGeneratorSource.resolveSibling("NMSBinding.java"));
int methodStart = source.indexOf("public void deserializeTile(");
int methodEnd = source.indexOf("\n private void merge(", methodStart);
String method = source.substring(methodStart, methodEnd);
int ownershipCheck = method.indexOf("J.isOwnedByCurrentRegion(");
int synchronousMerge = method.indexOf("merge(level, blockPos, tag);", ownershipCheck);
int synchronousReturn = method.indexOf("return;", synchronousMerge);
int scheduledFallback = method.indexOf("J.runAt(pos, () -> merge(level, blockPos, tag))");
assertTrue(ownershipCheck >= 0);
assertTrue(synchronousMerge > ownershipCheck);
assertTrue(synchronousReturn > synchronousMerge);
assertTrue(scheduledFallback > synchronousReturn);
}
}
@@ -0,0 +1,294 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.core.structure;
import art.arcane.iris.core.structure.authoring.StructureCapability;
import art.arcane.iris.core.structure.authoring.StructureLoss;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement;
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public class VillageImporterListPoolElementRuntimeContractTest {
@BeforeClass
public static void bootstrapMinecraft() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void pillagerOutpostTowerCompositeProducesOnePhysicalTowerChoice() throws Exception {
StructurePoolElement tower = StructurePoolElement.legacy("minecraft:pillager_outpost/watchtower")
.apply(StructureTemplatePool.Projection.RIGID);
StructurePoolElement overgrown = StructurePoolElement.legacy("minecraft:pillager_outpost/watchtower_overgrown")
.apply(StructureTemplatePool.Projection.RIGID);
ListPoolElement towers = new ListPoolElement(
List.of(tower, overgrown),
StructureTemplatePool.Projection.RIGID
);
VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(towers);
Map<String, Object> entry = VillageImporter.piecePoolEntry(
"qa/pillager_outpost/piece/minecraft/pillager_outpost/watchtower",
1
);
StructureLoss loss = VillageImporter.listElementFallbackLoss(
resolution,
"minecraft:pillager_outpost/towers"
);
Map<String, Object> basePlatePiece = VillageImporter.pieceJson(
"qa/pillager_outpost/piece/minecraft/pillager_outpost/base_plate",
List.of(Map.of("name", "minecraft:bottom")),
0
);
Map<String, Object> watchtowerPiece = VillageImporter.pieceJson(
"qa/pillager_outpost/piece/minecraft/pillager_outpost/watchtower",
List.of(),
1155
);
VillageImporter.PoolMemberNormalization connectorScaffold = VillageImporter.normalizePoolMember(
"minecraft:pillager_outpost/features",
"qa/pillager_outpost/pool/minecraft/pillager_outpost/features",
false,
2,
"minecraft:empty",
"minecraft:pillager_outpost/feature_plate",
"qa/pillager_outpost/piece/minecraft/pillager_outpost/feature_plate",
1,
0,
List.of(Map.of("name", "minecraft:bottom")));
assertSame(tower, resolution.physicalElement());
assertEquals("minecraft:pillager_outpost/watchtower", resolution.templateLocation());
assertEquals(1, resolution.omittedElements());
assertEquals("qa/pillager_outpost/piece/minecraft/pillager_outpost/watchtower", entry.get("piece"));
assertEquals(1, entry.get("weight"));
assertEquals(StructureCapability.LIST_ELEMENTS, loss.capability());
assertEquals("list_pool_overlays_not_imported", loss.code());
assertEquals(false, basePlatePiece.get("collidable"));
assertFalse(watchtowerPiece.containsKey("collidable"));
assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, connectorScaffold.disposition());
assertTrue(connectorScaffold.losses().isEmpty());
}
@Test
public void actualWaystoneLikeAllAirMemberNormalizesToEmpty() throws Exception {
StructurePoolElement waystone = StructurePoolElement
.legacy("dungeons_and_taverns:waystones/waystone_scaffold")
.apply(StructureTemplatePool.Projection.RIGID);
VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(waystone);
VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember(
"dungeons_and_taverns:waystones",
"qa/pool/dungeons_and_taverns/waystones",
false,
1,
"dungeons_and_taverns:waystones",
resolution.templateLocation(),
"qa/piece/dungeons_and_taverns/waystones/waystone_scaffold",
2,
0,
List.of());
assertSame(waystone, resolution.physicalElement());
assertEquals("dungeons_and_taverns:waystones/waystone_scaffold", resolution.templateLocation());
assertEquals(VillageImporter.PoolMemberDisposition.EMPTY, normalization.disposition());
assertEquals(true, normalization.poolEntry().get("empty"));
assertEquals(2, normalization.poolEntry().get("weight"));
assertEquals("connectorless_all_air_member_normalized_empty",
normalization.losses().getFirst().code());
}
@Test
public void actualNonAirOrphanMemberIsOmitted() throws Exception {
StructurePoolElement orphan = StructurePoolElement
.legacy("test:orphan_house")
.apply(StructureTemplatePool.Projection.RIGID);
VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(orphan);
VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember(
"test:orphan_pool",
"qa/pool/test/orphan_pool",
false,
1,
null,
resolution.templateLocation(),
"qa/piece/test/orphan_house",
4,
37,
List.of());
assertSame(orphan, resolution.physicalElement());
assertEquals("test:orphan_house", resolution.templateLocation());
assertEquals(VillageImporter.PoolMemberDisposition.OMITTED, normalization.disposition());
assertTrue(normalization.poolEntry().isEmpty());
assertEquals("connectorless_non_air_member_omitted",
normalization.losses().getFirst().code());
assertTrue(normalization.losses().getFirst().detail().contains("37 non-air block(s)"));
assertTrue(normalization.losses().getFirst().detail().contains("no source fallback"));
assertTrue(normalization.losses().getFirst().detail().contains("selection weights"));
assertTrue(normalization.losses().getFirst().detail().contains("RNG consumption"));
}
@Test
public void actualConnectorlessStartMemberRemainsPhysical() throws Exception {
StructurePoolElement start = StructurePoolElement
.legacy("test:air_start")
.apply(StructureTemplatePool.Projection.RIGID);
VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(start);
VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember(
"test:start",
"qa/pool/test/start",
true,
2,
"test:fallback",
resolution.templateLocation(),
"qa/piece/test/air_start",
6,
0,
List.of());
assertSame(start, resolution.physicalElement());
assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, normalization.disposition());
assertEquals("qa/piece/test/air_start", normalization.poolEntry().get("piece"));
assertTrue(normalization.losses().isEmpty());
}
@Test
public void actualMixedPoolAllAirMemberIsOmittedWithoutBecomingEmpty() throws Exception {
StructurePoolElement inert = StructurePoolElement
.legacy("test:mixed_air_inert")
.apply(StructureTemplatePool.Projection.RIGID);
VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(inert);
VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember(
"test:mixed",
"qa/pool/test/mixed",
false,
4,
"test:mixed",
resolution.templateLocation(),
"qa/piece/test/mixed_air_inert",
9,
0,
List.of());
assertSame(inert, resolution.physicalElement());
assertEquals(VillageImporter.PoolMemberDisposition.OMITTED, normalization.disposition());
assertTrue(normalization.poolEntry().isEmpty());
assertEquals("connectorless_all_air_mixed_member_omitted",
normalization.losses().getFirst().code());
assertTrue(normalization.losses().getFirst().detail().contains("selection weights"));
assertTrue(normalization.losses().getFirst().detail().contains("RNG consumption"));
}
@Test
public void actualMixedDistinctFallbackRetainsAllConnectorlessMembers() throws Exception {
StructurePoolElement allAirElement = StructurePoolElement
.legacy("test:mixed_all_air_primary")
.apply(StructureTemplatePool.Projection.RIGID);
StructurePoolElement nonAirElement = StructurePoolElement
.legacy("test:mixed_non_air_primary")
.apply(StructureTemplatePool.Projection.RIGID);
VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(allAirElement);
VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(nonAirElement);
VillageImporter.PoolMemberNormalization allAir = VillageImporter.normalizePoolMember(
"test:mixed",
"qa/pool/test/mixed",
false,
4,
"test:fallback",
allAirResolution.templateLocation(),
"qa/piece/test/mixed_all_air_primary",
9,
0,
List.of());
VillageImporter.PoolMemberNormalization nonAir = VillageImporter.normalizePoolMember(
"test:mixed",
"qa/pool/test/mixed",
false,
4,
"test:fallback",
nonAirResolution.templateLocation(),
"qa/piece/test/mixed_non_air_primary",
12,
41,
List.of());
assertSame(allAirElement, allAirResolution.physicalElement());
assertSame(nonAirElement, nonAirResolution.physicalElement());
assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, allAir.disposition());
assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, nonAir.disposition());
assertEquals("qa/piece/test/mixed_all_air_primary", allAir.poolEntry().get("piece"));
assertEquals("qa/piece/test/mixed_non_air_primary", nonAir.poolEntry().get("piece"));
assertTrue(allAir.losses().isEmpty());
assertTrue(nonAir.losses().isEmpty());
}
@Test
public void actualSingletonDistinctFallbackRetainsAllAirAndNonAirMembers() throws Exception {
StructurePoolElement allAirElement = StructurePoolElement
.legacy("test:all_air_primary")
.apply(StructureTemplatePool.Projection.RIGID);
StructurePoolElement nonAirElement = StructurePoolElement
.legacy("test:non_air_primary")
.apply(StructureTemplatePool.Projection.RIGID);
VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(allAirElement);
VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(nonAirElement);
VillageImporter.PoolMemberNormalization allAir = VillageImporter.normalizePoolMember(
"test:primary",
"qa/pool/test/primary",
false,
1,
"test:fallback",
allAirResolution.templateLocation(),
"qa/piece/test/all_air_primary",
5,
0,
List.of());
VillageImporter.PoolMemberNormalization nonAir = VillageImporter.normalizePoolMember(
"test:primary",
"qa/pool/test/primary",
false,
1,
"test:fallback",
nonAirResolution.templateLocation(),
"qa/piece/test/non_air_primary",
7,
29,
List.of());
assertSame(allAirElement, allAirResolution.physicalElement());
assertSame(nonAirElement, nonAirResolution.physicalElement());
assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, allAir.disposition());
assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, nonAir.disposition());
assertEquals("qa/piece/test/all_air_primary", allAir.poolEntry().get("piece"));
assertEquals("qa/piece/test/non_air_primary", nonAir.poolEntry().get("piece"));
assertTrue(allAir.losses().isEmpty());
assertTrue(nonAir.losses().isEmpty());
}
}