mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-28 13:00:43 +00:00
Vwop
This commit is contained in:
+10
-8
@@ -27,7 +27,8 @@ import art.arcane.iris.engine.framework.GenerationSessionLease;
|
||||
import art.arcane.iris.engine.framework.NativeStructureStartPlan;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.nativegen.NativeStructureStartInjector;
|
||||
import art.arcane.iris.nativegen.NativeStructureLocateResults;
|
||||
import art.arcane.iris.nativegen.NativeStructureReferenceRepair;
|
||||
import art.arcane.iris.nativegen.NativeStructureVanillaLocator;
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import art.arcane.iris.spi.PlatformBiome;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
@@ -294,14 +295,14 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
Engine current = engine();
|
||||
try (GenerationSessionLease lease = requireGenerationLease(current, "modded_structure_locate");
|
||||
IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) {
|
||||
Pair<BlockPos, Holder<Structure>> irisPlaced = nativeStructures.findNearestIrisStructure(
|
||||
level, holders, pos, Math.max(1, radius), findUnexplored, current);
|
||||
HolderSet<Structure> reachable = nativeStructures.filterReachableNativeStructures(
|
||||
level, holders, current);
|
||||
Pair<BlockPos, Holder<Structure>> nativeLocated = reachable.size() == 0
|
||||
? null
|
||||
: super.findNearestMapStructure(level, reachable, pos, radius, findUnexplored);
|
||||
return NativeStructureLocateResults.nearest(pos, irisPlaced, nativeLocated);
|
||||
NativeStructureVanillaLocator.Candidate nativeCandidate =
|
||||
reachable.size() == 0 ? null
|
||||
: NativeStructureVanillaLocator.predict(
|
||||
level, reachable, pos, radius, findUnexplored);
|
||||
return nativeStructures.findNearestIrisStructure(
|
||||
level, holders, pos, Math.max(0, radius), findUnexplored, current, nativeCandidate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -879,7 +880,8 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
Engine current = engine();
|
||||
try (GenerationSessionLease lease = requireGenerationLease(current, "modded_create_references");
|
||||
IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) {
|
||||
super.createReferences(level, structureManager, chunk);
|
||||
NativeStructureReferenceRepair.createReferences(
|
||||
current, level, structureManager, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-19
@@ -23,7 +23,7 @@ import art.arcane.iris.core.localization.IrisLanguage;
|
||||
import art.arcane.iris.core.localization.RuntimeUiMessages;
|
||||
import art.arcane.iris.core.nms.datapack.DataVersion;
|
||||
import art.arcane.iris.core.nms.datapack.IDataFixer;
|
||||
import art.arcane.iris.core.pack.PackValidationRegistry;
|
||||
import art.arcane.iris.core.pack.PackDirectoryResolver;
|
||||
import art.arcane.iris.core.pack.PackValidationResult;
|
||||
import art.arcane.iris.core.pack.PackValidator;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
@@ -74,7 +74,6 @@ public final class ModdedForcedDatapack {
|
||||
// v2: custom biomes now inherit their vanilla derivative's biome tags, so every already-published pack has
|
||||
// to regenerate once.
|
||||
private static final String HASH_SALT = "iris-forced-datapack-v2";
|
||||
private static final String GIT_DIRECTORY = ".git";
|
||||
private static final long PACKS_HASH_TTL_NANOS = 2_000_000_000L;
|
||||
private static final Object LOCK = new Object();
|
||||
private static final AtomicBoolean LOADED = new AtomicBoolean(false);
|
||||
@@ -147,13 +146,13 @@ public final class ModdedForcedDatapack {
|
||||
return;
|
||||
}
|
||||
Path packsRoot = packsRoot();
|
||||
File[] packs = packsRoot.toFile().listFiles(File::isDirectory);
|
||||
if (packs == null || packs.length == 0) {
|
||||
List<File> packs = PackDirectoryResolver.listVisiblePackDirectories(packsRoot.toFile());
|
||||
if (packs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LOGGER.error("===============================================================");
|
||||
LOGGER.error("Iris forced datapack '{}' was never loaded by this server.", PACK_ID);
|
||||
LOGGER.error("{} installed pack(s) at {} contributed no dimension types or custom biomes.", packs.length, packsRoot);
|
||||
LOGGER.error("{} installed pack(s) at {} contributed no dimension types or custom biomes.", packs.size(), packsRoot);
|
||||
LOGGER.error("Datapack source injection failed for this loader (mixin/event not applied), so world creation will fail and restarting will not fix it.");
|
||||
LOGGER.error("===============================================================");
|
||||
}
|
||||
@@ -360,8 +359,8 @@ public final class ModdedForcedDatapack {
|
||||
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path directory, BasicFileAttributes attributes) {
|
||||
// Studio packs can be git checkouts; .git churns constantly and never reaches the datapack.
|
||||
return GIT_DIRECTORY.equals(directory.getFileName().toString())
|
||||
return !directory.equals(root)
|
||||
&& PackDirectoryResolver.isHiddenName(directory.getFileName().toString())
|
||||
? FileVisitResult.SKIP_SUBTREE
|
||||
: FileVisitResult.CONTINUE;
|
||||
}
|
||||
@@ -397,16 +396,10 @@ public final class ModdedForcedDatapack {
|
||||
int packCount = 0;
|
||||
KList<String> presetIds = new KList<>();
|
||||
File root = packsRoot().toFile();
|
||||
File[] packs = root.listFiles(File::isDirectory);
|
||||
if (packs == null && root.exists()) {
|
||||
throw new IOException("Iris could not read installed pack directory " + root.getAbsolutePath());
|
||||
}
|
||||
if (packs != null) {
|
||||
Arrays.sort(packs, Comparator.comparing(File::getName));
|
||||
for (File pack : packs) {
|
||||
if (stagePack(pack, fixer, stagingDirectory, seenBiomes, presetIds)) {
|
||||
packCount++;
|
||||
}
|
||||
List<File> packs = PackDirectoryResolver.listVisiblePackDirectoriesOrThrow(root);
|
||||
for (File pack : packs) {
|
||||
if (stagePack(pack, fixer, stagingDirectory, seenBiomes, presetIds)) {
|
||||
packCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,8 +424,7 @@ public final class ModdedForcedDatapack {
|
||||
KList<String> presetIds) throws IOException {
|
||||
PackValidationResult validation;
|
||||
try {
|
||||
validation = PackValidator.validate(sourcePack);
|
||||
PackValidationRegistry.publish(validation);
|
||||
validation = PackValidator.validateForDatapackBootstrap(sourcePack);
|
||||
} catch (Throwable validationFailure) {
|
||||
LOGGER.error("Iris excluded pack '{}' from Create World because validation failed",
|
||||
sourcePack.getName(), validationFailure);
|
||||
|
||||
+102
-49
@@ -21,17 +21,21 @@ package art.arcane.iris.modded;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.framework.IrisStructureLocator;
|
||||
import art.arcane.iris.engine.framework.NativeStructureGenerationPolicy;
|
||||
import art.arcane.iris.engine.framework.NativeStructurePlacementPlanner;
|
||||
import art.arcane.iris.engine.framework.NativeStructureOwnershipRecord;
|
||||
import art.arcane.iris.engine.framework.NativeStructureStartPlan;
|
||||
import art.arcane.iris.engine.object.IrisMaterialPalette;
|
||||
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
|
||||
import art.arcane.iris.nativegen.NativeStructureGenerationException;
|
||||
import art.arcane.iris.nativegen.NativeStructureLocatePersistence;
|
||||
import art.arcane.iris.nativegen.NativeStructureLocateResults;
|
||||
import art.arcane.iris.nativegen.NativeStructureOwnershipRecovery;
|
||||
import art.arcane.iris.nativegen.NativeStructurePostProcessor;
|
||||
import art.arcane.iris.nativegen.NativeStructureReferenceEnvelope;
|
||||
import art.arcane.iris.nativegen.NativeStructureSurfaceFitter;
|
||||
import art.arcane.iris.nativegen.NativeStructureTerrainIntegrator;
|
||||
import art.arcane.iris.nativegen.NativeStructureVegetationClearer;
|
||||
import art.arcane.iris.nativegen.NativeStructureVerticalPlacer;
|
||||
import art.arcane.iris.nativegen.NativeStructureVanillaLocator;
|
||||
import art.arcane.iris.nativegen.WorldgenTerrainHeightmaps;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
@@ -60,6 +64,7 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -85,42 +90,85 @@ final class ModdedNativeStructureStage {
|
||||
Pair<BlockPos, Holder<Structure>> findNearestIrisStructure(ServerLevel level,
|
||||
HolderSet<Structure> holders,
|
||||
BlockPos pos, int radius, boolean findUnexplored,
|
||||
Engine current) {
|
||||
if (findUnexplored) {
|
||||
return null;
|
||||
}
|
||||
Engine current,
|
||||
NativeStructureVanillaLocator.Candidate nativeCandidate) {
|
||||
Pair<BlockPos, Holder<Structure>> nativeLocated =
|
||||
nativeCandidate == null ? null : nativeCandidate.result();
|
||||
Runnable nativeReference = () -> {
|
||||
if (nativeCandidate != null) {
|
||||
nativeCandidate.reference(level.structureManager());
|
||||
}
|
||||
};
|
||||
Registry<Structure> registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||
BlockPos best = null;
|
||||
Holder<Structure> bestHolder = null;
|
||||
long bestDistance = Long.MAX_VALUE;
|
||||
List<IrisNativeLocateSearch> searches = new ArrayList<>(holders.size());
|
||||
NativeStructureLocatePersistence.ProbeBudget budget = NativeStructureLocatePersistence.probeBudget();
|
||||
for (Holder<Structure> holder : holders) {
|
||||
Identifier id = registry.getKey(holder.value());
|
||||
if (id == null) {
|
||||
throw new IllegalStateException("Native structure locate received an unregistered structure holder");
|
||||
}
|
||||
String structureId = id.toString();
|
||||
if (!IrisStructureLocator.isPlaced(current, structureId)) {
|
||||
if (!IrisStructureLocator.hasNativePlacement(current, structureId)) {
|
||||
continue;
|
||||
}
|
||||
IrisStructureLocator.LocateResult result = IrisStructureLocator.locate(
|
||||
current, structureId, pos.getX(), pos.getZ(), radius);
|
||||
if (result.status() == IrisStructureLocator.LocateStatus.SEARCH_LIMIT_REACHED) {
|
||||
throw new IllegalStateException("Iris structure locate reached its safety limit for "
|
||||
+ structureId + " within " + radius + " chunks");
|
||||
}
|
||||
if (!result.found()) {
|
||||
continue;
|
||||
}
|
||||
long dx = (long) result.originX() - pos.getX();
|
||||
long dz = (long) result.originZ() - pos.getZ();
|
||||
long distance = dx * dx + dz * dz;
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
best = new BlockPos(result.originX(), result.baseY(), result.originZ());
|
||||
bestHolder = holder;
|
||||
}
|
||||
NativeStructureLocatePersistence.Probe probe = NativeStructureLocatePersistence.probe(
|
||||
level, holder.value(), findUnexplored, budget);
|
||||
searches.add(new IrisNativeLocateSearch(
|
||||
holder, structureId, NativeStructureLocatePersistence.search(
|
||||
current, structureId, pos.getX(), pos.getZ(), radius, probe)));
|
||||
}
|
||||
return best == null ? null : Pair.of(best, bestHolder);
|
||||
searches.sort(Comparator.comparing(IrisNativeLocateSearch::structureId));
|
||||
for (int attempt = 0; attempt < NativeStructureLocatePersistence.MAX_SELECTED_CANDIDATE_RETRIES; attempt++) {
|
||||
IrisNativeLocateSearch bestSearch = null;
|
||||
IrisStructureLocator.LocateResult bestResult = null;
|
||||
long bestDistance = Long.MAX_VALUE;
|
||||
for (IrisNativeLocateSearch search : searches) {
|
||||
IrisStructureLocator.LocateResult result = search.search().predict();
|
||||
if (result.status() == IrisStructureLocator.LocateStatus.SEARCH_LIMIT_REACHED) {
|
||||
throw new IllegalStateException("Iris structure locate reached its safety limit for "
|
||||
+ search.structureId() + " within " + radius + " placement rings");
|
||||
}
|
||||
if (!result.found()) {
|
||||
continue;
|
||||
}
|
||||
long dx = (long) result.originX() - pos.getX();
|
||||
long dz = (long) result.originZ() - pos.getZ();
|
||||
long distance = dx * dx + dz * dz;
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
bestSearch = search;
|
||||
bestResult = result;
|
||||
}
|
||||
}
|
||||
if (bestSearch == null) {
|
||||
return NativeStructureLocateResults.selectAndReference(
|
||||
pos, null, () -> { }, nativeLocated, nativeReference);
|
||||
}
|
||||
Pair<BlockPos, Holder<Structure>> predicted = Pair.of(
|
||||
new BlockPos(bestResult.originX(), bestResult.baseY(), bestResult.originZ()),
|
||||
bestSearch.holder());
|
||||
if (NativeStructureLocateResults.nearest(pos, predicted, nativeLocated) != predicted) {
|
||||
return NativeStructureLocateResults.selectAndReference(
|
||||
pos, predicted, () -> { }, nativeLocated, nativeReference);
|
||||
}
|
||||
NativeStructureLocatePersistence.VerifiedStart verified =
|
||||
bestSearch.search().verify(bestResult);
|
||||
if (verified == null) {
|
||||
bestSearch.search().reject(bestResult);
|
||||
continue;
|
||||
}
|
||||
BlockPos located = new BlockPos(
|
||||
bestResult.originX(), verified.ownership().locatorY(),
|
||||
bestResult.originZ());
|
||||
Pair<BlockPos, Holder<Structure>> irisLocated = Pair.of(located, bestSearch.holder());
|
||||
IrisNativeLocateSearch selectedSearch = bestSearch;
|
||||
NativeStructureLocatePersistence.VerifiedStart selectedStart = verified;
|
||||
return NativeStructureLocateResults.selectAndReference(
|
||||
pos, irisLocated, () -> selectedSearch.search().reference(selectedStart),
|
||||
nativeLocated, nativeReference);
|
||||
}
|
||||
throw new IllegalStateException("Iris structure locate rejected too many selected candidates within "
|
||||
+ radius + " placement rings");
|
||||
}
|
||||
|
||||
HolderSet<Structure> filterReachableNativeStructures(ServerLevel level, HolderSet<Structure> holders,
|
||||
@@ -193,10 +241,14 @@ final class ModdedNativeStructureStage {
|
||||
decision.preserveSourceY(),
|
||||
decision.yBand(),
|
||||
(x, z) -> current.getHeight(x, z, true) + current.getMinHeight());
|
||||
StructureStart wrapped = NativeStructureReferenceEnvelope.wrap(
|
||||
start, structure, start.getReferences(), templateManager,
|
||||
NativeStructureTerrainIntegrator.resolveNativeTerrain(start, decision.terrain()));
|
||||
StructureStart wrapped = NativeStructureReferenceEnvelope.wrapForPublication(
|
||||
start, structure, start.getReferences(),
|
||||
NativeStructureTerrainIntegrator.resolveNativeTerrain(start, decision.terrain()),
|
||||
structureId);
|
||||
chunk.setStartForStructure(structure, wrapped);
|
||||
if (!wrapped.isValid()) {
|
||||
continue;
|
||||
}
|
||||
} catch (Throwable error) {
|
||||
throw NativeStructureGenerationException.failure(
|
||||
"vertical adjustment", structureId, chunkPos.x(), chunkPos.z(), error);
|
||||
@@ -227,7 +279,6 @@ final class ModdedNativeStructureStage {
|
||||
Engine current = generator.engine();
|
||||
List<NativePlacementGroup> placementGroups = new ArrayList<>();
|
||||
List<StructureStart> heightmapStarts = new ArrayList<>();
|
||||
List<StructureStart> nativeStarts = new ArrayList<>();
|
||||
List<NativeStructureVegetationClearer.VegetationTarget> vegetationTargets = new ArrayList<>();
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> terrainTargets = new ArrayList<>();
|
||||
for (int step = 0; step < steps; step++) {
|
||||
@@ -245,10 +296,11 @@ final class ModdedNativeStructureStage {
|
||||
List<StructureStart> starts = structureManager.startsForStructure(sectionPos, structure);
|
||||
List<NativePlacement> resolvedPlacements = new ArrayList<>(starts.size());
|
||||
for (StructureStart start : starts) {
|
||||
NativeStructureStartPlan plan = NativeStructurePlacementPlanner.matchingPlan(
|
||||
current, structureId, start.getChunkPos().x(), start.getChunkPos().z());
|
||||
IrisNativeStructureDecision decision = plan == null
|
||||
? sourceDecision : NativeStructurePlacementPlanner.decisionFor(plan);
|
||||
NativeStructureOwnershipRecord ownership =
|
||||
NativeStructureOwnershipRecovery.resolve(
|
||||
current, world.getLevel(), structureId, structure, start);
|
||||
IrisNativeStructureDecision decision =
|
||||
ownership == null ? sourceDecision : ownership.restoredDecision();
|
||||
if (!decision.generate()) {
|
||||
continue;
|
||||
}
|
||||
@@ -258,9 +310,6 @@ final class ModdedNativeStructureStage {
|
||||
structureId, start,
|
||||
NativeStructureTerrainIntegrator.resolveNativeTerrain(
|
||||
start, decision.terrain())));
|
||||
if (plan == null || !plan.placement().isUnderground()) {
|
||||
nativeStarts.add(start);
|
||||
}
|
||||
boolean clearEntireFootprint = NativeStructureVegetationClearer
|
||||
.shouldClearEntireVegetationFootprint(
|
||||
structure.step(), decision.clearVegetation());
|
||||
@@ -289,15 +338,6 @@ final class ModdedNativeStructureStage {
|
||||
"heightmap priming", nativeStructureBatchContext(placementGroups),
|
||||
chunkPos.x(), chunkPos.z(), error);
|
||||
}
|
||||
try {
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
world, area, nativeStarts,
|
||||
(x, z) -> current.getHeight(x, z, true) + current.getMinHeight());
|
||||
} catch (Throwable error) {
|
||||
throw NativeStructureGenerationException.failure(
|
||||
"terrain integration", nativeStructureBatchContext(placementGroups),
|
||||
chunkPos.x(), chunkPos.z(), error);
|
||||
}
|
||||
try {
|
||||
NativeStructureVegetationClearer.clearIntersectingVegetation(
|
||||
world, chunk, area, vegetationTargets);
|
||||
@@ -306,6 +346,15 @@ final class ModdedNativeStructureStage {
|
||||
"vegetation cleanup", nativeStructureBatchContext(placementGroups),
|
||||
chunkPos.x(), chunkPos.z(), error);
|
||||
}
|
||||
try {
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
world, area, terrainTargets,
|
||||
(x, z) -> current.getHeight(x, z, true) + current.getMinHeight());
|
||||
} catch (Throwable error) {
|
||||
throw NativeStructureGenerationException.failure(
|
||||
"terrain integration", nativeStructureBatchContext(placementGroups),
|
||||
chunkPos.x(), chunkPos.z(), error);
|
||||
}
|
||||
try {
|
||||
NativeStructurePostProcessor.prepareTerrain(
|
||||
world, area, terrainTargets, this::resolvePaletteBlock);
|
||||
@@ -413,8 +462,8 @@ final class ModdedNativeStructureStage {
|
||||
ChunkPos chunkPos = chunk.getPos();
|
||||
int minX = chunkPos.getMinBlockX();
|
||||
int minZ = chunkPos.getMinBlockZ();
|
||||
int minY = chunk.getMinY();
|
||||
int maxY = minY + chunk.getHeight() - 1;
|
||||
int minY = chunk.getMinY() + 1;
|
||||
int maxY = chunk.getMinY() + chunk.getHeight() - 1;
|
||||
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||
}
|
||||
|
||||
@@ -436,6 +485,10 @@ final class ModdedNativeStructureStage {
|
||||
List<NativePlacement> placements) {
|
||||
}
|
||||
|
||||
private record IrisNativeLocateSearch(Holder<Structure> holder, String structureId,
|
||||
NativeStructureLocatePersistence.Search search) {
|
||||
}
|
||||
|
||||
private record StructureStepCache(Registry<Structure> registry, List<List<Structure>> structures) {
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -56,8 +56,8 @@ public final class ModdedPackInstaller {
|
||||
synchronized (installLock) {
|
||||
File packs = configDir.resolve("irisworldgen").resolve("packs").toFile();
|
||||
try {
|
||||
boolean installed = PackDownloader.isDefaultOverworld(pack)
|
||||
? PackDownloader.downloadDefaultOverworld(packs, forceOverwrite, feedback) != null
|
||||
PackDownloader.PackInstallResult result = PackDownloader.isDefaultOverworld(pack)
|
||||
? PackDownloader.downloadDefaultOverworld(packs, forceOverwrite, feedback)
|
||||
: PackDownloader.download(
|
||||
packs,
|
||||
"IrisDimensions/" + pack,
|
||||
@@ -65,8 +65,9 @@ public final class ModdedPackInstaller {
|
||||
forceOverwrite,
|
||||
false,
|
||||
pack,
|
||||
feedback) != null;
|
||||
if (installed) {
|
||||
feedback);
|
||||
boolean installed = result != null;
|
||||
if (result != null && result.changed()) {
|
||||
// Pack-install completion is one of the four forced-datapack regeneration triggers; every
|
||||
// install call site already runs off the server thread, so regenerate inline here. A
|
||||
// regeneration failure must never turn a successful install into a failed one.
|
||||
@@ -76,6 +77,9 @@ public final class ModdedPackInstaller {
|
||||
LOGGER.error("Iris installed pack '{}' but could not regenerate the forced datapack", pack, regenerationFailure);
|
||||
}
|
||||
}
|
||||
if (result != null && result.restartRequired()) {
|
||||
feedback.accept("Pack '" + pack + "' is installed on disk and requires a server restart before its active data changes.");
|
||||
}
|
||||
return installed;
|
||||
} catch (IOException error) {
|
||||
LOGGER.error("Iris pack download failed for IrisDimensions/{} ({})", pack, branch, error);
|
||||
|
||||
@@ -28,6 +28,7 @@ import art.arcane.iris.spi.PlatformEntityType;
|
||||
import art.arcane.iris.spi.PlatformItem;
|
||||
import art.arcane.iris.spi.PlatformRegistries;
|
||||
import art.arcane.volmlib.util.data.UnresolvedKeyLog;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
@@ -40,6 +41,7 @@ import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.world.level.storage.loot.LootTable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -212,6 +214,20 @@ public final class ModdedRegistries implements PlatformRegistries {
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> lootTableKeys() {
|
||||
List<String> keys = new ArrayList<>();
|
||||
MinecraftServer instance = server.get();
|
||||
if (instance == null) {
|
||||
warnNotReady("loot table");
|
||||
return keys;
|
||||
}
|
||||
HolderLookup.RegistryLookup<LootTable> registry = instance.reloadableRegistries().lookup()
|
||||
.lookupOrThrow(Registries.LOOT_TABLE);
|
||||
registry.listElementIds().forEach(key -> keys.add(key.identifier().toString()));
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<PlatformBlockProperty>> blockStateProperties() {
|
||||
Map<String, List<PlatformBlockProperty>> properties = new LinkedHashMap<>();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import art.arcane.iris.core.pack.BrokenPackException;
|
||||
import art.arcane.iris.core.pack.PackDirectoryResolver;
|
||||
import art.arcane.iris.core.pack.PackDownloader;
|
||||
import art.arcane.iris.core.pack.PackValidationRegistry;
|
||||
import art.arcane.iris.core.pack.PackValidationResult;
|
||||
@@ -104,9 +105,9 @@ public final class ModdedStartup {
|
||||
|
||||
public static void validateAllPacks() {
|
||||
File packsRoot = ModdedPackCommands.packsRoot();
|
||||
File[] packDirs = packsRoot.listFiles(File::isDirectory);
|
||||
List<File> packDirs = PackDirectoryResolver.listVisiblePackDirectories(packsRoot);
|
||||
PackValidationRegistry.clear();
|
||||
if (packDirs == null || packDirs.length == 0) {
|
||||
if (packDirs.isEmpty()) {
|
||||
LOGGER.info("Iris found no packs to validate under {}; install one with /iris download <pack>",
|
||||
packsRoot.getAbsolutePath());
|
||||
return;
|
||||
@@ -146,8 +147,8 @@ public final class ModdedStartup {
|
||||
if (pack == null || pack.isBlank()) {
|
||||
throw new IllegalArgumentException("Pack name is required for world creation");
|
||||
}
|
||||
File packDir = new File(ModdedPackCommands.packsRoot(), pack);
|
||||
if (!packDir.isDirectory()) {
|
||||
File packDir = PackDirectoryResolver.resolveExisting(ModdedPackCommands.packsRoot(), pack);
|
||||
if (packDir == null) {
|
||||
throw new BrokenPackException(pack, List.of(
|
||||
"Pack folder does not exist under " + ModdedPackCommands.packsRoot().getAbsolutePath() + "."));
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import art.arcane.iris.nativegen.NativeStructureGenerationException;
|
||||
import art.arcane.iris.nativegen.NativeStructureFactory;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.PlatformStructureHooks;
|
||||
import art.arcane.iris.spi.PlatformStructureHooks.JigsawSourceMetadata;
|
||||
import art.arcane.iris.spi.PlatformWorld;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -95,6 +97,67 @@ public final class ModdedStructureHooks implements PlatformStructureHooks {
|
||||
return registryKeys(Registries.TEMPLATE_POOL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JigsawSourceMetadata jigsawSourceMetadata(String structureKey) {
|
||||
MinecraftServer instance = requireServer("resolve live jigsaw metadata for registered structure '"
|
||||
+ structureKey + "'");
|
||||
try {
|
||||
Identifier identifier = Identifier.tryParse(structureKey);
|
||||
if (identifier == null) {
|
||||
throw new IllegalArgumentException("Invalid registered structure key: " + structureKey);
|
||||
}
|
||||
Registry<Structure> registry = instance.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||
Structure structure = registry.getValue(identifier);
|
||||
if (structure == null) {
|
||||
throw new IllegalArgumentException("Registered structure does not exist: " + structureKey);
|
||||
}
|
||||
if (!(structure instanceof JigsawStructure jigsaw)) {
|
||||
throw new IllegalArgumentException("Registered structure is not a jigsaw: " + structureKey);
|
||||
}
|
||||
return NativeStructureFactory.sourceMetadata(
|
||||
instance.registryAccess(), instance.getStructureManager(), jigsaw);
|
||||
} catch (RuntimeException error) {
|
||||
throw new IllegalStateException("Iris failed to resolve live jigsaw metadata for registered structure '"
|
||||
+ structureKey + "' from the modded structure registry", error);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int templatePoolHorizontalSpan(String templatePoolKey) {
|
||||
MinecraftServer instance = requireServer("resolve the live horizontal span for registered template pool '"
|
||||
+ templatePoolKey + "'");
|
||||
try {
|
||||
return NativeStructureFactory.templatePoolHorizontalSpan(
|
||||
instance.registryAccess(), instance.getStructureManager(), templatePoolKey);
|
||||
} catch (RuntimeException error) {
|
||||
throw new IllegalStateException("Iris failed to resolve the live horizontal span for registered "
|
||||
+ "template pool '" + templatePoolKey + "' from the modded template-pool registry", error);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int jigsawStartPoolHorizontalSpan(String structureKey, String templatePoolKey) {
|
||||
MinecraftServer instance = requireServer("resolve the effective start-pool span for registered jigsaw '"
|
||||
+ structureKey + "'");
|
||||
try {
|
||||
Identifier identifier = Identifier.tryParse(structureKey);
|
||||
if (identifier == null) {
|
||||
throw new IllegalArgumentException("Invalid registered structure key: " + structureKey);
|
||||
}
|
||||
Structure structure = instance.registryAccess().lookupOrThrow(Registries.STRUCTURE)
|
||||
.getValue(identifier);
|
||||
if (!(structure instanceof JigsawStructure jigsaw)) {
|
||||
throw new IllegalArgumentException("Registered structure is not a jigsaw: " + structureKey);
|
||||
}
|
||||
return NativeStructureFactory.jigsawStartPoolHorizontalSpan(
|
||||
instance.registryAccess(), instance.getStructureManager(), jigsaw, templatePoolKey);
|
||||
} catch (RuntimeException error) {
|
||||
throw new IllegalStateException("Iris failed to resolve the effective start-pool span for registered "
|
||||
+ "jigsaw structure '" + structureKey + "' and pool '" + templatePoolKey
|
||||
+ "' from the modded registries", error);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> structureSetKeys() {
|
||||
return registryKeys(Registries.STRUCTURE_SET);
|
||||
|
||||
+13
-18
@@ -18,6 +18,7 @@
|
||||
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import art.arcane.iris.core.pack.PackDirectoryResolver;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.framework.IrisStructureLocator;
|
||||
import art.arcane.iris.modded.IrisModdedChunkGenerator;
|
||||
@@ -153,24 +154,18 @@ final class ModdedCommandSuggestions {
|
||||
names.add("overworld");
|
||||
try {
|
||||
File packs = ModdedEngineBootstrap.loader().configDir().resolve("irisworldgen").resolve("packs").toFile();
|
||||
File[] children = packs.listFiles();
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
if (!child.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
String packName = child.getName();
|
||||
names.add(packName);
|
||||
File dimensions = new File(child, "dimensions");
|
||||
File[] dimensionFiles = dimensions.listFiles(
|
||||
(File directory, String name) -> name.endsWith(".json"));
|
||||
if (dimensionFiles == null) {
|
||||
continue;
|
||||
}
|
||||
for (File dimensionFile : dimensionFiles) {
|
||||
String fileName = dimensionFile.getName();
|
||||
names.add(packName + ":" + fileName.substring(0, fileName.length() - 5));
|
||||
}
|
||||
for (File child : PackDirectoryResolver.listVisiblePackDirectories(packs)) {
|
||||
String packName = child.getName();
|
||||
names.add(packName);
|
||||
File dimensions = new File(child, "dimensions");
|
||||
File[] dimensionFiles = dimensions.listFiles(
|
||||
(File directory, String name) -> name.endsWith(".json"));
|
||||
if (dimensionFiles == null) {
|
||||
continue;
|
||||
}
|
||||
for (File dimensionFile : dimensionFiles) {
|
||||
String fileName = dimensionFile.getName();
|
||||
names.add(packName + ":" + fileName.substring(0, fileName.length() - 5));
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
||||
+54
-26
@@ -20,6 +20,7 @@ package art.arcane.iris.modded.command;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.nms.datapack.DataVersion;
|
||||
import art.arcane.iris.core.pack.PackDirectoryResolver;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.modded.IrisModdedChunkGenerator;
|
||||
@@ -42,8 +43,12 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import art.arcane.iris.core.localization.IrisLanguage;
|
||||
@@ -74,11 +79,11 @@ public final class ModdedDatapackCommands {
|
||||
root.then(Commands.literal("ls")
|
||||
.executes((CommandContext<CommandSourceStack> context) -> list(context.getSource())));
|
||||
|
||||
root.then(message("ingest", "Modrinth datapack ingest requires the Bukkit plugin because its editable-resource import and manifest workflow use Bukkit tooling. Iris modded dimensions do run native vanilla and datapack structure placement; install the datapack in world/datapacks and restart to generate its registered structures."));
|
||||
root.then(message("pull", "Modrinth datapack ingest requires the Bukkit plugin because its editable-resource import and manifest workflow use Bukkit tooling. Iris modded dimensions do run native vanilla and datapack structure placement; install the datapack in world/datapacks and restart to generate its registered structures."));
|
||||
root.then(message("ingest", "Managed Modrinth datapack ingest is Bukkit-only. On modded servers install the datapack folder or zip in world/datapacks, enable it, restart, then use /iris datapack list to confirm it is enabled. Registered structures generate natively in Iris dimensions."));
|
||||
root.then(message("pull", "Managed Modrinth datapack ingest is Bukkit-only. On modded servers install the datapack folder or zip in world/datapacks, enable it, restart, then use /iris datapack list to confirm it is enabled. Registered structures generate natively in Iris dimensions."));
|
||||
|
||||
root.then(message("remove", "Datapack removal manages the Bukkit ingest manifest. On modded servers delete the datapack folder from world/datapacks and restart."));
|
||||
root.then(message("rm", "Datapack removal manages the Bukkit ingest manifest. On modded servers delete the datapack folder from world/datapacks and restart."));
|
||||
root.then(message("remove", "Managed datapack removal is Bukkit-only. On modded servers disable the pack, delete its folder or zip from world/datapacks, and restart."));
|
||||
root.then(message("rm", "Managed datapack removal is Bukkit-only. On modded servers disable the pack, delete its folder or zip from world/datapacks, and restart."));
|
||||
|
||||
return root;
|
||||
}
|
||||
@@ -215,27 +220,24 @@ public final class ModdedDatapackCommands {
|
||||
MinecraftServer server = source.getServer();
|
||||
LinkedHashSet<String> configured = new LinkedHashSet<>();
|
||||
File packsRoot = ModdedPackCommands.packsRoot();
|
||||
File[] packs = packsRoot.isDirectory() ? packsRoot.listFiles(File::isDirectory) : null;
|
||||
if (packs != null) {
|
||||
for (File pack : packs) {
|
||||
if (!new File(pack, "dimensions").isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
IrisData data = IrisData.get(pack);
|
||||
for (IrisDimension dimension : data.getDimensionLoader().loadAll(data.getDimensionLoader().getPossibleKeys())) {
|
||||
if (dimension == null || dimension.getDatapackImports() == null) {
|
||||
continue;
|
||||
}
|
||||
for (String url : dimension.getDatapackImports()) {
|
||||
if (url != null && !url.isBlank()) {
|
||||
configured.add(url.trim());
|
||||
}
|
||||
for (File pack : PackDirectoryResolver.listVisiblePackDirectories(packsRoot)) {
|
||||
if (!new File(pack, "dimensions").isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
IrisData data = IrisData.get(pack);
|
||||
for (IrisDimension dimension : data.getDimensionLoader().loadAll(data.getDimensionLoader().getPossibleKeys())) {
|
||||
if (dimension == null || dimension.getDatapackImports() == null) {
|
||||
continue;
|
||||
}
|
||||
for (String url : dimension.getDatapackImports()) {
|
||||
if (url != null && !url.isBlank()) {
|
||||
configured.add(url.trim());
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("Iris datapack import scan failed for pack {}", pack.getName(), e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("Iris datapack import scan failed for pack {}", pack.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,19 +250,45 @@ public final class ModdedDatapackCommands {
|
||||
}
|
||||
|
||||
File datapacks = worldDatapacksFolder(server);
|
||||
File[] installed = datapacks.isDirectory() ? datapacks.listFiles(File::isDirectory) : null;
|
||||
File[] installed = datapacks.isDirectory()
|
||||
? datapacks.listFiles(file -> file.isDirectory() || file.isFile() && file.getName().toLowerCase(Locale.ROOT).endsWith(".zip"))
|
||||
: null;
|
||||
Set<String> availableIds = new HashSet<>(server.getPackRepository().getAvailableIds());
|
||||
Set<String> selectedIds = new HashSet<>(server.getPackRepository().getSelectedIds());
|
||||
KList<String> names = new KList<>();
|
||||
if (installed != null) {
|
||||
for (File folder : installed) {
|
||||
if (new File(folder, "pack.mcmeta").isFile()) {
|
||||
names.add(folder.getName());
|
||||
for (File installedPack : installed) {
|
||||
String name = installedPack.getName();
|
||||
String repositoryId = resolveRepositoryId(name, availableIds);
|
||||
String state;
|
||||
if (repositoryId == null) {
|
||||
state = "unavailable";
|
||||
} else if (selectedIds.contains(repositoryId)) {
|
||||
state = "enabled";
|
||||
} else {
|
||||
state = "disabled";
|
||||
}
|
||||
names.add(name + " [" + state + "]");
|
||||
}
|
||||
}
|
||||
Collections.sort(names);
|
||||
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_INSTALLED_WORLD_DATAPACKS, MessageArgument.untrusted("value", names.size())));
|
||||
for (String name : names) {
|
||||
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_MESSAGE_2, MessageArgument.untrusted("name", name)));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static String resolveRepositoryId(String filename, Set<String> availableIds) {
|
||||
String direct = "file/" + filename;
|
||||
if (availableIds.contains(direct)) {
|
||||
return direct;
|
||||
}
|
||||
for (String availableId : availableIds) {
|
||||
if (availableId.equals(filename) || availableId.endsWith("/" + filename)) {
|
||||
return availableId;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -173,10 +173,16 @@ final class ModdedLocateCommands {
|
||||
return 0;
|
||||
}
|
||||
if (decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS) {
|
||||
locateIrisStructure(source, level, engine, player, target.key());
|
||||
if (!IrisStructureLocator.hasNativePlacement(engine, target.key())) {
|
||||
locateIrisStructure(source, level, engine, player, target.key());
|
||||
return 1;
|
||||
}
|
||||
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SEARCHING_NATIVE_STRUCTURE_WITHIN_CHUNKS, MessageArgument.untrusted("value", target.key()), MessageArgument.untrusted("NATIVESTRUCTURELOCATERADIUS", NATIVE_STRUCTURE_LOCATE_RADIUS)));
|
||||
runNativeStructureLocate(source, level, player, target);
|
||||
return 1;
|
||||
}
|
||||
if (target.availability() != NativeStructureAvailability.AVAILABLE) {
|
||||
if (!IrisStructureLocator.hasNativePlacement(engine, target.key())
|
||||
&& target.availability() != NativeStructureAvailability.AVAILABLE) {
|
||||
IrisModdedCommands.fail(source, nativeUnavailableMessage(target.key(), target.availability()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
+3
-5
@@ -110,14 +110,12 @@ public final class ModdedPackCommands {
|
||||
|
||||
List<File> targets = new ArrayList<>();
|
||||
if (pack == null || pack.isBlank()) {
|
||||
File[] dirs = packsRoot.listFiles(File::isDirectory);
|
||||
if (dirs == null || dirs.length == 0) {
|
||||
List<File> dirs = PackDirectoryResolver.listVisiblePackDirectories(packsRoot);
|
||||
if (dirs.isEmpty()) {
|
||||
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_NO_PACKS_VALIDATE_UNDER, MessageArgument.untrusted("value", packsRoot.getAbsolutePath())));
|
||||
return 0;
|
||||
}
|
||||
for (File dir : dirs) {
|
||||
targets.add(dir);
|
||||
}
|
||||
targets.addAll(dirs);
|
||||
} else {
|
||||
File target = PackDirectoryResolver.resolveExisting(packsRoot, pack);
|
||||
if (target == null) {
|
||||
|
||||
Reference in New Issue
Block a user